[PATCH] fio: add 'directory' and 'numjobs' options
[disktools.git] / dops.c
1 /*
2  * direct disk operation exerciser
3  *
4  * Copyright (C) 2005 Jens Axboe <axboe@suse.de>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 #include <sys/time.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/ioctl.h>
30 #include <libaio.h>
31 #include <getopt.h>
32 #include <locale.h>
33
34 #include <linux/fs.h>
35
36 #define MAX_DEPTH       128
37
38 static int io_size = 512;               /* bytes */
39 static int sequential = 1;              /* 0 for random, 1 for sequential */
40 static unsigned long long range = 2;    /* megabytes */
41 static char filename[256] = "/dev/sdb";
42 static int depth = 32;
43 static int total_run = 15000;
44 static int display_stats = 0;
45 static int writing = 0;
46 static int force = 0;
47
48 struct iocb *iocbs[MAX_DEPTH];
49 struct io_event *events;
50
51 struct drand48_data random_state;
52 static unsigned long long next_block;
53 static unsigned long long blocks_done;
54 static unsigned long long nr_blocks;
55
56 #define ALIGN(buf)      (((unsigned long) (buf) + (io_size-1)) & ~((io_size-1)))
57
58 unsigned long long get_next_offset(void)
59 {
60         unsigned long long block;
61         long r;
62
63         if (sequential) {
64                 if (next_block >= nr_blocks)
65                         next_block = 0;
66
67                 block = next_block;
68                 next_block += (256 * 1024 / io_size);
69                 return block;
70         }
71                 
72         lrand48_r(&random_state, &r);
73         block = (1 + (double) (nr_blocks) * (unsigned long long) r / (RAND_MAX+1.0));
74         return block - 1;
75 }
76
77 void init_randomizer(void)
78 {
79         unsigned long seed = 0;
80
81         if (sequential)
82                 return;
83
84         srand48_r(seed, &random_state);
85 }
86
87 unsigned long time_elapsed(struct timeval *start, struct timeval *end)
88 {
89         int seconds = end->tv_sec - start->tv_sec;
90         int useconds = end->tv_usec - start->tv_usec;
91
92         return (1000 * seconds) + (useconds / 1000);
93 }
94
95 void do_io(io_context_t io_ctx, int fd, char *buffer)
96 {
97         struct timeval real_start, start, end;
98         int i, ret, cur_depth;
99         unsigned long ops, best_ops;
100         unsigned long bytes, best_bytes;
101
102         for (i = 0; i < depth; i++) {
103                 struct iocb *io = iocbs[i];
104                 char *b = buffer + i * io_size;
105                 unsigned long long off = get_next_offset();
106                 unsigned long long offset = off * io_size;
107
108                 if (writing)
109                         io_prep_pwrite(io, fd, b, io_size, offset);
110                 else
111                         io_prep_pread(io, fd, b, io_size, offset);
112         }
113
114         ret = io_submit(io_ctx, depth, iocbs);
115         if (ret < 0) {
116                 fprintf(stderr, "io_submit: %d\n", ret);
117                 return;
118         }
119
120         cur_depth = ret;
121         gettimeofday(&start, NULL);
122         gettimeofday(&real_start, NULL);
123
124         best_bytes = best_ops = bytes = ops = 0;
125         do {
126                 struct timespec ts = { .tv_sec = 30, .tv_nsec = 0 };
127                 unsigned long elapsed;
128
129                 ret = io_getevents(io_ctx, 1, cur_depth, events, &ts);
130                 if (ret < 0) {
131                         if (ret == EINTR)
132                                 continue;
133
134                         fprintf(stderr, "io_getevents: %d\n", ret);
135                         break;
136                 } else if (!ret) {      
137                         fprintf(stderr, "No events completed, depth %d\n", cur_depth);
138                         cur_depth = 0;
139                         break;
140                 }
141
142                 cur_depth -= ret;
143                 ops += (ret * (io_size >> 9));
144                 bytes += (ret * io_size);
145                 blocks_done += ret;
146
147                 gettimeofday(&end, NULL);
148
149                 elapsed = time_elapsed(&start, &end);
150                 if (elapsed >= 1000) {
151                         unsigned long kb = bytes >> 10;
152                         unsigned long o, b;
153
154                         o = ops * 1000 / elapsed;
155                         b = kb * 1000 / elapsed;
156
157                         if (display_stats)
158                                 printf("stat: ops/sec = %8lu, KiB/sec = %8lu (depth=%2d)\n", o, b, cur_depth);
159                         if (o > best_ops)
160                                 best_ops = o;
161                         if (b > best_bytes)
162                                 best_bytes = b;
163                                 
164                         bytes = ops = 0;
165                         gettimeofday(&start, NULL);
166                 }
167
168                 if (total_run) {
169                         elapsed = time_elapsed(&real_start, &end);
170                         if (total_run && (elapsed >= total_run)) {
171                                 printf("OPS_SEC=%'lu RATE=%'lu\n",best_ops, best_bytes);
172                                 break;
173                         }
174                 }
175
176                 for (i = 0; i < ret; i++) {
177                         struct io_event *ev = events + i;
178                         struct iocb *io = ev->obj;
179                         unsigned long long offset = get_next_offset();
180                         int err;
181
182                         offset *= io_size;
183
184                         if (writing) {
185                                 io_prep_pwrite(io, fd, io->u.c.buf, io_size, offset);
186                         } else
187                                 io_prep_pread(io, fd, io->u.c.buf, io_size, offset);
188
189                         err = io_submit(io_ctx, 1, &io);
190                         if (err != 1) {
191                                 fprintf(stderr, "io_submit: %d\n", err);
192                                 goto error;
193                         }
194
195                         cur_depth++;
196                 }
197
198         } while (1);
199
200 error:
201         if (cur_depth)
202                 io_getevents(io_ctx, cur_depth, cur_depth, events, NULL);
203 }
204
205 int parse_options(int argc, char **argv)
206 {
207         static struct option longoptions[] = {
208                 { "device",     1,      NULL,   'd' },
209                 { "area",       1,      NULL,   'a' },
210                 { "random",     0,      NULL,   'r' },
211                 { "blocks",     1,      NULL,   'b' },
212                 { "queue depth",1,      NULL,   'q' },
213                 { "time",       1,      NULL,   't' },
214                 { "stats",      1,      NULL,   's' },
215                 { "write",      0,      NULL,   'w' },
216                 { "force",      0,      NULL,   'w' },
217         };
218         int c, res;
219
220         while (1) {
221                 c = getopt_long(argc, argv, "d:a:rb:q:t:swf", longoptions,&res);
222                 if (c == -1)
223                         break;
224
225                 switch (c) {
226                         case 'd':
227                                 strncpy(filename, optarg, sizeof(filename)-1);
228                                 break;
229                         case 'a':
230                                 range = atoi(optarg);
231                                 break;
232                         case 'r':
233                                 sequential = 0;
234                                 break;
235                         case 'b':
236                                 io_size = atoi(optarg) * 512;
237                                 break;
238                         case 'q':
239                                 depth = atoi(optarg);
240                                 if (depth <= 0 || depth > MAX_DEPTH) {
241                                         fprintf(stderr, "queue depth out-of-range %d\n", depth);
242                                         return 1;
243                                 }
244                                 break;
245                         case 't':
246                                 total_run = atoi(optarg) * 1000;
247                                 if (total_run < 0) {
248                                         fprintf(stderr, "bad runtime\n");
249                                         return 1;
250                                 }
251                                 break;
252                         case 's':
253                                 display_stats = 1;
254                                 break;
255                         case 'w':
256                                 writing = 1;
257                                 break;
258                         case 'f':
259                                 force = 1;
260                                 break;
261                 }
262         }
263
264         printf("Dev=%s, %s, a=%LuMiB, %s, b=%u, q=%u, t=%u\n", filename, writing ? "write" : "read", range, sequential ? "sequential" : "random", io_size / 512, depth, total_run / 1000);
265         return 0;
266 }
267
268 unsigned long get_size(int fd)
269 {
270         unsigned long long blocks;
271         struct stat sb;
272
273         if (fstat(fd, &sb) == -1) {
274                 perror("fstat");
275                 return -1;
276         }
277
278         if (S_ISBLK(sb.st_mode)) {
279                 /*
280                  * be a little cautious about writing to a device. if
281                  * the user didn't use -f for force, ask for confirmation
282                  */
283                 if (writing && !force) {
284                         printf("Writing directly to block device, are you sure? CTRL-C to cancel now\n");
285                         (void) getc(stdin);
286                 }
287                 if (ioctl(fd, BLKGETSIZE64, &blocks) == -1) {
288                         perror("BLKGETSIZE64");
289                         return -1;
290                 }
291                 return blocks >> 20ULL;
292         } else if (S_ISREG(sb.st_mode))
293                 return sb.st_size >> 20ULL;
294
295         fprintf(stderr, "Bad file type (must be file or block device)\n");
296         return -1;
297 }
298
299 int main(int argc, char *argv[])
300 {
301         unsigned long mb;
302         io_context_t *io_ctx;
303         char *orig, *buffer;
304         int fd, i;
305
306         if (parse_options(argc, argv))
307                 return 1;
308
309         if (writing)
310                 fd = open(filename, O_WRONLY | O_DIRECT);
311         else
312                 fd = open(filename, O_RDONLY | O_DIRECT);
313
314         if (fd == -1) {
315                 perror("open");
316                 return 1;
317         }
318
319         mb = get_size(fd);
320         if (nr_blocks == -1)
321                 return 1;
322
323         if (!range || (range > mb))
324                 range = mb;
325
326         nr_blocks = (range << 20ULL) / (unsigned long long) io_size;
327
328         printf("nr_blocks=%Lu (size=%u)\n", nr_blocks, io_size);
329
330         io_ctx = malloc(sizeof(*io_ctx));
331
332         i = io_queue_init(depth, io_ctx);
333         if (i) {
334                 fprintf(stderr, "io_queue_init: %d\n", i);
335                 return 1;
336         }
337
338         init_randomizer();
339
340         orig = malloc((depth + 1) * io_size);
341         buffer = (char *) ALIGN(orig);
342
343         for (i = 0; i < depth; i++)
344                 iocbs[i] = malloc(sizeof(struct iocb));
345
346         setlocale(LC_NUMERIC, "en_US");
347
348         events = malloc(depth * sizeof(struct io_event));
349
350         do_io(*io_ctx, fd, buffer);
351
352         io_destroy(*io_ctx);
353
354         for (i = 0; i < depth; i++)
355                 free(iocbs[i]);
356
357         free(io_ctx);
358         free(events);
359         free(orig);
360         close(fd);
361         return 0;
362 }