[PATCH] fio: add 'directory' and 'numjobs' options
[disktools.git] / dops.c
CommitLineData
1f961edd
JA
1/*
2 * direct disk operation exerciser
3 *
abe4da87
JA
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
1f961edd
JA
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
38static int io_size = 512; /* bytes */
39static int sequential = 1; /* 0 for random, 1 for sequential */
40static unsigned long long range = 2; /* megabytes */
41static char filename[256] = "/dev/sdb";
42static int depth = 32;
43static int total_run = 15000;
44static int display_stats = 0;
45static int writing = 0;
46static int force = 0;
47
48struct iocb *iocbs[MAX_DEPTH];
49struct io_event *events;
50
51struct drand48_data random_state;
52static unsigned long long next_block;
53static unsigned long long blocks_done;
54static unsigned long long nr_blocks;
55
56#define ALIGN(buf) (((unsigned long) (buf) + (io_size-1)) & ~((io_size-1)))
57
58unsigned 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
77void init_randomizer(void)
78{
79 unsigned long seed = 0;
80
81 if (sequential)
82 return;
83
84 srand48_r(seed, &random_state);
85}
86
87unsigned 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
95void 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
200error:
201 if (cur_depth)
202 io_getevents(io_ctx, cur_depth, cur_depth, events, NULL);
203}
204
205int 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
268unsigned 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
299int 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}