[PATCH] fio: more async fixes
[disktools.git] / fio.c
CommitLineData
abe4da87
JA
1/*
2 * fio - the flexible io tester
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 */
892199bd
JA
21#include <stdio.h>
22#include <stdlib.h>
23#include <unistd.h>
24#include <fcntl.h>
25#include <string.h>
26#include <errno.h>
27#include <signal.h>
28#include <time.h>
7dd1389e 29#include <ctype.h>
18e0b78c 30#include <sched.h>
43000118 31#include <libaio.h>
892199bd
JA
32#include <sys/time.h>
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <sys/wait.h>
36#include <semaphore.h>
37#include <sys/ipc.h>
38#include <sys/shm.h>
39#include <asm/unistd.h>
40
4240cfa1
JA
41#define MAX_JOBS (1024)
42
892199bd
JA
43/*
44 * assume we don't have _get either, if _set isn't defined
45 */
46#ifndef __NR_ioprio_set
47
48#if defined(__i386__)
49#define __NR_ioprio_set 289
50#define __NR_ioprio_get 290
51#elif defined(__powerpc__) || defined(__powerpc64__)
52#define __NR_ioprio_set 273
53#define __NR_ioprio_get 274
54#elif defined(__x86_64__)
55#define __NR_ioprio_set 251
56#define __NR_ioprio_get 252
57#elif defined(__ia64__)
58#define __NR_ioprio_set 1274
59#define __NR_ioprio_get 1275
60#elif defined(__alpha__)
61#define __NR_ioprio_set 442
62#define __NR_ioprio_get 443
63#elif defined(__s390x__) || defined(__s390__)
64#define __NR_ioprio_set 282
65#define __NR_ioprio_get 283
66#else
67#error "Unsupported arch"
68#endif
69
70#endif
71
72static int ioprio_set(int which, int who, int ioprio)
73{
74 return syscall(__NR_ioprio_set, which, who, ioprio);
75}
76
77enum {
78 IOPRIO_WHO_PROCESS = 1,
79 IOPRIO_WHO_PGRP,
80 IOPRIO_WHO_USER,
81};
82
83#define IOPRIO_CLASS_SHIFT 13
84
892199bd
JA
85#define MASK (4095)
86
4240cfa1
JA
87#define DEF_BS (4096)
88#define DEF_TIMEOUT (30)
89#define DEF_RATE_CYCLE (1000)
90#define DEF_ODIRECT (1)
91#define DEF_SEQUENTIAL (1)
92#define DEF_WRITESTAT (0)
93#define DEF_RAND_REPEAT (1)
94
95#define ALIGN(buf) (char *) (((unsigned long) (buf) + MASK) & ~(MASK))
892199bd 96
4240cfa1
JA
97static int sequential = DEF_SEQUENTIAL;
98static int write_stat = DEF_WRITESTAT;
99static int repeatable = DEF_RAND_REPEAT;
100static int timeout = DEF_TIMEOUT;
101static int odirect = DEF_ODIRECT;
102static int global_bs = DEF_BS;
02bdd9ba 103static int rate_quit = 1;
892199bd 104
892199bd 105static int thread_number;
7dd1389e 106static char *ini_file;
892199bd
JA
107
108static int shm_id;
109
18e0b78c
JA
110static cpu_set_t def_cpumask;
111
4240cfa1
JA
112enum {
113 DDIR_READ = 0,
114 DDIR_WRITE,
115};
892199bd 116
02bdd9ba
JA
117/*
118 * thread life cycle
119 */
120enum {
121 TD_NOT_CREATED = 0,
122 TD_CREATED,
123 TD_STARTED,
124 TD_EXITED,
125 TD_REAPED,
126};
127
892199bd
JA
128struct thread_data {
129 char file_name[256];
130 int thread_number;
131 int error;
132 int fd;
133 int stat_fd;
134 pid_t pid;
4240cfa1 135 volatile int terminate;
02bdd9ba 136 volatile int runstate;
f737299d
JA
137 unsigned int ddir;
138 unsigned int ioprio;
139 unsigned int sequential;
140 unsigned int bs;
141 unsigned int odirect;
142 unsigned int delay_sleep;
4240cfa1 143 unsigned int fsync_blocks;
fc24389f 144 unsigned int start_delay;
43000118 145 unsigned int use_aio;
18e0b78c 146 cpu_set_t cpumask;
86184d14 147
43000118
JA
148 io_context_t *aio_ctx;
149 struct iocb *aio_iocbs;
150 unsigned int aio_depth;
151 unsigned int aio_cur_depth;
152 struct io_event *aio_events;
153 char *aio_iocbs_status;
154
7dd1389e 155 unsigned int rate;
4240cfa1
JA
156 unsigned int ratemin;
157 unsigned int ratecycle;
158 unsigned long rate_usec_cycle;
159 long rate_pending_usleep;
160 unsigned long rate_blocks;
161 struct timeval lastrate;
86184d14 162
892199bd
JA
163 unsigned long max_latency; /* msec */
164 unsigned long min_latency; /* msec */
165 unsigned long runtime; /* sec */
166 unsigned long blocks;
4240cfa1 167 unsigned long io_blocks;
892199bd
JA
168 unsigned long last_block;
169 sem_t mutex;
892199bd
JA
170 struct drand48_data random_state;
171
172 /*
173 * bandwidth stat
174 */
175 unsigned long stat_time;
176 unsigned long stat_time_last;
177 unsigned long stat_blocks_last;
4240cfa1
JA
178
179 struct timeval start;
892199bd
JA
180};
181
182static struct thread_data *threads;
892199bd
JA
183
184static sem_t startup_sem;
185
5c24b2c4 186static void sig_handler(int sig)
892199bd
JA
187{
188 int i;
189
213b446c
JA
190 for (i = 0; i < thread_number; i++) {
191 struct thread_data *td = &threads[i];
192
193 td->terminate = 1;
194 td->start_delay = 0;
195 }
02bdd9ba
JA
196}
197
5c24b2c4 198static int init_random_state(struct thread_data *td)
892199bd
JA
199{
200 unsigned long seed = 123;
201
202 if (td->sequential)
203 return 0;
204
205 if (!repeatable) {
206 int fd = open("/dev/random", O_RDONLY);
207
208 if (fd == -1) {
209 td->error = errno;
210 return 1;
211 }
212
7dd1389e 213 if (read(fd, &seed, sizeof(seed)) < (int) sizeof(seed)) {
892199bd
JA
214 td->error = EIO;
215 close(fd);
216 return 1;
217 }
218
219 close(fd);
220 }
221
222 srand48_r(seed, &td->random_state);
223 return 0;
224}
225
5c24b2c4 226static void shutdown_stat_file(struct thread_data *td)
892199bd
JA
227{
228 if (td->stat_fd != -1) {
229 fsync(td->stat_fd);
230 close(td->stat_fd);
231 }
232}
233
5c24b2c4 234static int init_stat_file(struct thread_data *td)
892199bd 235{
4240cfa1 236 char n[256];
892199bd
JA
237
238 if (!write_stat)
239 return 0;
240
892199bd
JA
241 sprintf(n, "%s.stat", td->file_name);
242 td->stat_fd = open(n, O_WRONLY | O_CREAT | O_TRUNC, 0644);
243 if (td->stat_fd == -1) {
892199bd
JA
244 td->error = errno;
245 return 1;
246 }
247
892199bd
JA
248 return 0;
249}
250
5c24b2c4 251static unsigned long utime_since(struct timeval *s, struct timeval *e)
892199bd
JA
252{
253 double sec, usec;
254
255 sec = e->tv_sec - s->tv_sec;
256 usec = e->tv_usec - s->tv_usec;
257 if (sec > 0 && usec < 0) {
258 sec--;
259 usec += 1000000;
260 }
261
262 sec *= (double) 1000000;
263
264 return sec + usec;
265}
266
5c24b2c4 267static unsigned long mtime_since(struct timeval *s, struct timeval *e)
892199bd
JA
268{
269 double sec, usec;
270
271 sec = e->tv_sec - s->tv_sec;
272 usec = e->tv_usec - s->tv_usec;
273 if (sec > 0 && usec < 0) {
274 sec--;
275 usec += 1000000;
276 }
277
278 sec *= (double) 1000;
279 usec /= (double) 1000;
280
281 return sec + usec;
282}
283
98168d55
JA
284static inline unsigned long msec_now(struct timeval *s)
285{
286 return s->tv_sec * 1000 + s->tv_usec / 1000;
287}
288
5c24b2c4 289static unsigned long get_next_offset(struct thread_data *td)
892199bd
JA
290{
291 unsigned long b;
292 long r;
293
294 if (!td->sequential) {
295 lrand48_r(&td->random_state, &r);
296 b = (1+(double) (td->blocks-1) * r / (RAND_MAX+1.0));
297 } else {
298 b = td->last_block;
299 td->last_block++;
300 }
301
302 return b * td->bs;
303}
304
5c24b2c4 305static void add_stat_sample(struct thread_data *td, unsigned long msec)
892199bd
JA
306{
307 char sample[256];
308
309 if (!td->stat_fd)
310 return;
311
312#if 0
4240cfa1 313 sprintf(sample, "%lu, %lu\n", td->io_blocks, msec);
892199bd
JA
314 write(td->stat_fd, sample, strlen(sample));
315#else
316 td->stat_time += msec;
317 td->stat_time_last += msec;
318 td->stat_blocks_last++;
319
320 if (td->stat_time_last >= 500) {
321 unsigned long rate = td->stat_blocks_last * td->bs / (td->stat_time_last);
322
323 td->stat_time_last = 0;
324 td->stat_blocks_last = 0;
325 sprintf(sample, "%lu, %lu\n", td->stat_time, rate);
4240cfa1 326 //sprintf(sample, "%lu, %lu\n", td->io_blocks, msec);
892199bd
JA
327 write(td->stat_fd, sample, strlen(sample));
328 }
329#endif
330}
331
5c24b2c4 332static void usec_sleep(int usec)
892199bd 333{
86184d14
JA
334 struct timespec req = { .tv_sec = 0, .tv_nsec = usec * 1000 };
335 struct timespec rem;
892199bd
JA
336
337 do {
86184d14
JA
338 rem.tv_sec = rem.tv_nsec = 0;
339 nanosleep(&req, &rem);
340 if (!rem.tv_nsec)
892199bd 341 break;
86184d14
JA
342
343 req.tv_nsec = rem.tv_nsec;
892199bd
JA
344 } while (1);
345}
346
5c24b2c4 347static void rate_throttle(struct thread_data *td, unsigned long time_spent)
86184d14 348{
4240cfa1
JA
349 if (!td->rate)
350 return;
351
86184d14
JA
352 if (time_spent < td->rate_usec_cycle) {
353 unsigned long s = td->rate_usec_cycle - time_spent;
354
355 td->rate_pending_usleep += s;
fad86e6a 356 if (td->rate_pending_usleep >= 100000) {
86184d14
JA
357 usec_sleep(td->rate_pending_usleep);
358 td->rate_pending_usleep = 0;
359 }
4240cfa1 360 } else {
42b2b9fe
JA
361 long overtime = time_spent - td->rate_usec_cycle;
362
4240cfa1
JA
363 td->rate_pending_usleep -= overtime;
364 }
365}
366
5c24b2c4 367static int check_min_rate(struct thread_data *td, struct timeval *now)
4240cfa1
JA
368{
369 unsigned long spent = mtime_since(&td->start, now);
370 unsigned long rate;
371
372 /*
373 * allow a 2 second settle period in the beginning
374 */
375 if (spent < 2000)
376 return 0;
377
378 /*
379 * if rate blocks is set, sample is running
380 */
381 if (td->rate_blocks) {
382 spent = mtime_since(&td->lastrate, now);
383 if (spent < td->ratecycle)
384 return 0;
385
386 rate = ((td->io_blocks - td->rate_blocks) * td->bs) / spent;
387 if (rate < td->ratemin) {
388 printf("Client%d: min rate %d not met, got %ldKiB/sec\n", td->thread_number, td->ratemin, rate);
02bdd9ba 389 if (rate_quit)
e6402082 390 sig_handler(0);
4240cfa1
JA
391 return 1;
392 }
86184d14 393 }
4240cfa1
JA
394
395 td->rate_blocks = td->io_blocks;
396 memcpy(&td->lastrate, now, sizeof(*now));
397 return 0;
86184d14
JA
398}
399
4240cfa1
JA
400#define should_fsync(td) ((td)->ddir == DDIR_WRITE && !(td)->odirect)
401
43000118 402static void do_sync_io(struct thread_data *td)
892199bd 403{
4240cfa1 404 struct timeval s, e;
892199bd 405 char *buffer, *ptr;
86184d14 406 unsigned long blocks, msec, usec;
892199bd 407
4240cfa1
JA
408 ptr = malloc(td->bs + MASK);
409 buffer = ALIGN(ptr);
892199bd 410
4240cfa1
JA
411 gettimeofday(&td->start, NULL);
412
413 if (td->ratemin)
414 memcpy(&td->lastrate, &td->start, sizeof(td->start));
892199bd
JA
415
416 for (blocks = 0; blocks < td->blocks; blocks++) {
417 off_t offset = get_next_offset(td);
418 int ret;
419
420 if (td->terminate)
421 break;
422
423 if (lseek(td->fd, offset, SEEK_SET) == -1) {
424 td->error = errno;
425 break;
426 }
427
428 if (td->delay_sleep)
86184d14 429 usec_sleep(td->delay_sleep);
892199bd
JA
430
431 gettimeofday(&s, NULL);
432
433 if (td->ddir == DDIR_READ)
434 ret = read(td->fd, buffer, td->bs);
435 else
436 ret = write(td->fd, buffer, td->bs);
437
f737299d 438 if (ret < (int) td->bs) {
892199bd
JA
439 if (ret == -1)
440 td->error = errno;
441 break;
442 }
443
4240cfa1
JA
444 td->io_blocks++;
445
446 if (should_fsync(td) && td->fsync_blocks &&
447 (td->io_blocks % td->fsync_blocks) == 0)
448 fsync(td->fd);
449
86184d14
JA
450 gettimeofday(&e, NULL);
451
452 usec = utime_since(&s, &e);
86184d14 453
4240cfa1 454 rate_throttle(td, usec);
892199bd 455
4240cfa1
JA
456 if (check_min_rate(td, &e)) {
457 td->error = ENODATA;
458 break;
459 }
892199bd 460
4240cfa1
JA
461 msec = usec / 1000;
462 add_stat_sample(td, msec);
892199bd
JA
463
464 if (msec < td->min_latency)
465 td->min_latency = msec;
466 if (msec > td->max_latency)
467 td->max_latency = msec;
468 }
469
4240cfa1 470 if (should_fsync(td))
892199bd
JA
471 fsync(td->fd);
472
473 gettimeofday(&e, NULL);
4240cfa1 474 td->runtime = mtime_since(&td->start, &e);
892199bd
JA
475
476 free(ptr);
477}
43000118
JA
478
479static void aio_put_iocb(struct thread_data *td, struct iocb *iocb)
480{
481 long offset = ((long) iocb - (long) td->aio_iocbs)/ sizeof(struct iocb);
482
483 td->aio_iocbs_status[offset] = 0;
56b0eff0 484 td->aio_cur_depth--;
43000118
JA
485}
486
98168d55
JA
487static struct iocb *aio_get_iocb(struct thread_data *td, char *buffer,
488 struct timeval *t)
43000118
JA
489{
490 struct iocb *iocb = NULL;
56b0eff0 491 unsigned int i;
43000118
JA
492
493 for (i = 0; i < td->aio_depth; i++) {
494 if (td->aio_iocbs_status[i] == 0) {
495 td->aio_iocbs_status[i] = 1;
496 iocb = &td->aio_iocbs[i];
497 break;
498 }
499 }
500
501 if (iocb) {
502 off_t off = get_next_offset(td);
503 char *p = buffer + i * td->bs;
504
505 if (td->ddir == DDIR_READ)
506 io_prep_pread(iocb, td->fd, p, td->bs, off);
507 else
508 io_prep_pwrite(iocb, td->fd, p, td->bs, off);
98168d55
JA
509
510 io_set_callback(iocb, (io_callback_t) msec_now(t));
43000118
JA
511 }
512
513 return iocb;
514}
515
56b0eff0
JA
516static int aio_submit(struct thread_data *td, struct iocb *iocb)
517{
518 int ret;
519
520 do {
521 ret = io_submit(*td->aio_ctx, 1, &iocb);
522 if (ret == 1)
523 return 0;
524
525 if (errno == EINTR)
526 continue;
527 else if (errno == EAGAIN)
528 usleep(100);
529 else
530 break;
531 } while (1);
532
533 return 1;
534}
535
98168d55
JA
536#define iocb_time(iocb) ((unsigned long) (iocb)->data)
537
43000118
JA
538static void do_async_io(struct thread_data *td)
539{
540 struct timeval s, e;
541 char *buf, *ptr;
542 unsigned long blocks, msec, usec;
43000118
JA
543
544 ptr = malloc(td->bs * td->aio_depth + MASK);
545 buf = ALIGN(ptr);
546
547 gettimeofday(&td->start, NULL);
548
549 if (td->ratemin)
550 memcpy(&td->lastrate, &td->start, sizeof(td->start));
551
552 for (blocks = 0; blocks < td->blocks; blocks++) {
553 struct timespec ts = { .tv_sec = 0, .tv_nsec = 0};
554 struct timespec *timeout;
43000118 555 int ret, i, min_evts = 0;
8baf1bcc 556 struct iocb *iocb;
43000118
JA
557
558 if (td->terminate)
559 break;
560
561 if (td->delay_sleep)
562 usec_sleep(td->delay_sleep);
563
564 gettimeofday(&s, NULL);
565
98168d55 566 iocb = aio_get_iocb(td, buf, &s);
8baf1bcc 567
56b0eff0
JA
568 ret = aio_submit(td, iocb);
569 if (ret) {
43000118
JA
570 td->error = errno;
571 break;
572 }
573
574 td->aio_cur_depth++;
43000118
JA
575
576 if (td->aio_cur_depth < td->aio_depth) {
577 timeout = &ts;
578 min_evts = 0;
579 } else {
580 timeout = NULL;
581 min_evts = 1;
582 }
583
584 ret = io_getevents(*td->aio_ctx, min_evts, td->aio_cur_depth, td->aio_events, timeout);
585 if (ret < 0) {
586 td->error = errno;
587 break;
588 } else if (!ret)
589 continue;
590
98168d55
JA
591 gettimeofday(&e, NULL);
592
43000118
JA
593 for (i = 0; i < ret; i++) {
594 struct io_event *ev = td->aio_events + i;
595
596 td->io_blocks++;
43000118
JA
597
598 iocb = ev->obj;
98168d55
JA
599
600 msec = msec_now(&e) - iocb_time(iocb);
601 add_stat_sample(td, msec);
602
603 if (msec < td->min_latency)
604 td->min_latency = msec;
605 if (msec > td->max_latency)
606 td->max_latency = msec;
607
43000118
JA
608 aio_put_iocb(td, iocb);
609 }
610
98168d55
JA
611 /*
612 * the rate is batched for now, it should work for batches
613 * of completions except the very first one which may look
614 * a little bursty
615 */
43000118
JA
616 usec = utime_since(&s, &e);
617
618 rate_throttle(td, usec);
619
620 if (check_min_rate(td, &e)) {
621 td->error = ENODATA;
622 break;
623 }
43000118
JA
624 }
625
626 gettimeofday(&e, NULL);
627 td->runtime = mtime_since(&td->start, &e);
628
629 free(ptr);
630}
631
56b0eff0 632static void cleanup_pending_aio(struct thread_data *td)
43000118 633{
56b0eff0
JA
634 struct timespec ts = { .tv_sec = 0, .tv_nsec = 0};
635 unsigned int i;
636 int r;
637
43000118 638 /*
56b0eff0 639 * get immediately available events, if any
43000118 640 */
56b0eff0
JA
641 r = io_getevents(*td->aio_ctx, 0, td->aio_cur_depth, td->aio_events, &ts);
642 if (r > 0) {
643 for (i = 0; i < r; i++)
644 aio_put_iocb(td, &td->aio_iocbs[i]);
645 }
646
647 /*
648 * now cancel remaining active events
649 */
650 for (i = 0; i < td->aio_depth; i++) {
651 if (td->aio_iocbs_status[i] == 0)
652 continue;
653
654 r = io_cancel(*td->aio_ctx, &td->aio_iocbs[i], td->aio_events);
655 if (!r)
656 aio_put_iocb(td, &td->aio_iocbs[i]);
657 }
658
43000118
JA
659 if (td->aio_cur_depth)
660 io_getevents(*td->aio_ctx, td->aio_cur_depth, td->aio_cur_depth, td->aio_events, NULL);
56b0eff0
JA
661}
662
663static void cleanup_aio(struct thread_data *td)
664{
665 if (td->aio_cur_depth)
666 cleanup_pending_aio(td);
43000118
JA
667
668 if (td->aio_ctx) {
669 io_destroy(*td->aio_ctx);
670 free(td->aio_ctx);
671 }
672 if (td->aio_iocbs)
673 free(td->aio_iocbs);
674 if (td->aio_events)
675 free(td->aio_events);
676 if (td->aio_iocbs_status)
677 free(td->aio_iocbs_status);
678}
679
680static int init_aio(struct thread_data *td)
681{
682 td->aio_ctx = malloc(sizeof(*td->aio_ctx));
683
684 if (io_queue_init(td->aio_depth, td->aio_ctx)) {
685 td->error = errno;
686 return 1;
687 }
688
689 td->aio_iocbs = malloc(td->aio_depth * sizeof(struct iocb));
690 td->aio_events = malloc(td->aio_depth * sizeof(struct io_event));
691 td->aio_iocbs_status = malloc(td->aio_depth * sizeof(char));
692 return 0;
693}
694
5c24b2c4 695static void *thread_main(int shm_id, int offset, char *argv[])
892199bd
JA
696{
697 struct thread_data *td;
698 void *data;
4240cfa1 699 struct stat st;
892199bd
JA
700 int ret = 1, flags;
701
702 data = shmat(shm_id, NULL, 0);
703 td = data + offset * sizeof(struct thread_data);
704 td->pid = getpid();
705
18e0b78c
JA
706 td->fd = -1;
707
708 if (sched_setaffinity(td->pid, sizeof(td->cpumask), &td->cpumask) == -1) {
709 td->error = errno;
710 goto err;
711 }
712
43000118 713 printf("Thread (%s) (pid=%u) (f=%s) (aio=%d) started\n", td->ddir == DDIR_READ ? "read" : "write", td->pid, td->file_name, td->use_aio);
892199bd
JA
714 fflush(stdout);
715
4240cfa1 716 sprintf(argv[0], "fio%d", offset);
892199bd 717
7dd1389e 718 flags = 0;
892199bd
JA
719 if (td->odirect)
720 flags |= O_DIRECT;
721
7dd1389e
JA
722 if (td->ddir == DDIR_READ)
723 td->fd = open(td->file_name, flags | O_RDONLY);
724 else
725 td->fd = open(td->file_name, flags | O_WRONLY | O_CREAT | O_TRUNC, 0644);
726
892199bd
JA
727 if (td->fd == -1) {
728 td->error = errno;
729 goto err;
730 }
731
43000118
JA
732 if (td->use_aio && init_aio(td))
733 goto err;
734
892199bd
JA
735 if (init_random_state(td))
736 goto out;
737 if (init_stat_file(td))
738 goto out;
739
740 if (td->ddir == DDIR_READ) {
4240cfa1 741 if (fstat(td->fd, &st) == -1) {
892199bd
JA
742 td->error = errno;
743 goto out;
744 }
745
4240cfa1 746 td->blocks = st.st_size / td->bs;
892199bd
JA
747 if (!td->blocks) {
748 td->error = EINVAL;
749 goto out;
750 }
751 } else
752 td->blocks = 1024 * 1024 * 1024 / td->bs;
753
f737299d 754 if (td->ioprio) {
892199bd
JA
755 if (ioprio_set(IOPRIO_WHO_PROCESS, 0, td->ioprio) == -1) {
756 td->error = errno;
757 goto out;
758 }
759 }
760
761 sem_post(&startup_sem);
762 sem_wait(&td->mutex);
43000118
JA
763
764 if (!td->use_aio)
765 do_sync_io(td);
766 else
767 do_async_io(td);
768
892199bd
JA
769 ret = 0;
770
771out:
892199bd
JA
772 shutdown_stat_file(td);
773err:
18e0b78c
JA
774 if (td->fd != -1)
775 close(td->fd);
43000118
JA
776 if (td->use_aio)
777 cleanup_aio(td);
892199bd
JA
778 if (ret)
779 sem_post(&startup_sem);
02bdd9ba
JA
780
781 td->runstate = TD_EXITED;
4240cfa1 782 shmdt(data);
892199bd
JA
783 return NULL;
784}
785
5c24b2c4 786static void free_shm(void)
892199bd
JA
787{
788 shmdt(threads);
789}
790
5c24b2c4 791static void show_thread_status(struct thread_data *td)
892199bd
JA
792{
793 int prio, prio_class;
794 unsigned long bw = 0;
795
213b446c
JA
796 if (!td->io_blocks && !td->error)
797 return;
798
892199bd 799 if (td->runtime)
4240cfa1 800 bw = (td->io_blocks * td->bs) / td->runtime;
892199bd
JA
801
802 prio = td->ioprio & 0xff;
803 prio_class = td->ioprio >> IOPRIO_CLASS_SHIFT;
804
4240cfa1 805 printf("thread%d (%s): err=%2d, prio=%1d/%1d maxl=%5lumsec, io=%6luMiB, bw=%6luKiB/sec\n", td->thread_number, td->ddir == DDIR_READ ? " read": "write", td->error, prio_class, prio, td->max_latency, td->io_blocks * td->bs >> 20, bw);
892199bd
JA
806}
807
5c24b2c4 808static int setup_rate(struct thread_data *td)
86184d14 809{
4240cfa1
JA
810 int nr_reads_per_sec;
811
812 if (!td->rate)
813 return 0;
814
815 if (td->rate < td->ratemin) {
816 fprintf(stderr, "min rate larger than nominal rate\n");
817 return -1;
818 }
86184d14 819
4240cfa1 820 nr_reads_per_sec = td->rate * 1024 / td->bs;
86184d14
JA
821 td->rate_usec_cycle = 1000000 / nr_reads_per_sec;
822 td->rate_pending_usleep = 0;
4240cfa1 823 return 0;
86184d14
JA
824}
825
5c24b2c4 826static struct thread_data *get_new_job(void)
892199bd 827{
4240cfa1
JA
828 struct thread_data *td;
829
830 if (thread_number >= MAX_JOBS)
831 return NULL;
832
833 td = &threads[thread_number++];
fc24389f 834 memset(td, 0, sizeof(*td));
892199bd 835
86184d14 836 td->thread_number = thread_number;
f737299d
JA
837 td->ddir = DDIR_READ;
838 td->bs = global_bs;
839 td->odirect = 1;
840 td->delay_sleep = 0;
841 td->rate = 0;
4240cfa1 842 td->ratecycle = DEF_RATE_CYCLE;
f737299d
JA
843 td->sequential = sequential;
844 td->ioprio = 0;
43000118
JA
845 td->use_aio = 0;
846 td->aio_depth = 0;
847 td->aio_cur_depth = 0;
f737299d
JA
848 memcpy(&td->cpumask, &def_cpumask, sizeof(td->cpumask));
849
850 return td;
851}
852
4240cfa1
JA
853static void put_job(struct thread_data *td)
854{
855 memset(&threads[td->thread_number - 1], 0, sizeof(*td));
856 thread_number--;
857}
858
5c24b2c4
JA
859static int add_job(struct thread_data *td, const char *filename, int prioclass,
860 int prio)
f737299d
JA
861{
862 strcpy(td->file_name, filename);
892199bd 863 td->stat_fd = -1;
4240cfa1 864 sem_init(&td->mutex, 1, 0);
892199bd 865 td->min_latency = 10000000;
f737299d
JA
866 td->ioprio = (prioclass << IOPRIO_CLASS_SHIFT) | prio;
867
43000118
JA
868 if (td->use_aio && !td->aio_depth)
869 td->aio_depth = 1;
870
4240cfa1
JA
871 if (setup_rate(td))
872 return -1;
f737299d 873
43000118 874 printf("Client%d: file=%s, rw=%d, prio=%d, seq=%d, odir=%d, bs=%d, rate=%d, aio=%d, aio_depth=%d\n", td->thread_number, filename, td->ddir, td->ioprio, td->sequential, td->odirect, td->bs, td->rate, td->use_aio, td->aio_depth);
4240cfa1 875 return 0;
892199bd
JA
876}
877
18e0b78c
JA
878static void fill_cpu_mask(cpu_set_t cpumask, int cpu)
879{
f737299d 880 unsigned int i;
18e0b78c
JA
881
882 CPU_ZERO(&cpumask);
883
884 for (i = 0; i < sizeof(int) * 8; i++) {
885 if ((1 << i) & cpu)
886 CPU_SET(i, &cpumask);
887 }
888}
889
5c24b2c4 890static void fill_option(const char *input, char *output)
892199bd
JA
891{
892 int i;
893
894 i = 0;
895 while (input[i] != ',' && input[i] != '}' && input[i] != '\0') {
896 output[i] = input[i];
897 i++;
898 }
899
900 output[i] = '\0';
901}
902
903/*
904 * job key words:
905 *
906 * file=
907 * bs=
908 * rw=
909 * direct=
910 */
5c24b2c4 911static void parse_jobs_cmd(int argc, char *argv[], int index)
892199bd 912{
f737299d
JA
913 struct thread_data *td;
914 unsigned int prio, prioclass, cpu;
892199bd
JA
915 char *string, *filename, *p, *c;
916 int i;
917
918 string = malloc(256);
919 filename = malloc(256);
920
921 for (i = index; i < argc; i++) {
922 p = argv[i];
923
924 c = strpbrk(p, "{");
925 if (!c)
926 break;
927
928 filename[0] = 0;
4240cfa1 929
f737299d 930 td = get_new_job();
4240cfa1
JA
931 if (!td)
932 break;
f737299d 933
892199bd 934 prioclass = 2;
f737299d 935 prio = 4;
892199bd
JA
936
937 c = strstr(p, "rw=");
938 if (c) {
939 c += 3;
940 if (*c == '0')
f737299d 941 td->ddir = DDIR_READ;
892199bd 942 else
f737299d 943 td->ddir = DDIR_WRITE;
892199bd
JA
944 }
945
946 c = strstr(p, "prio=");
947 if (c) {
948 c += 5;
949 prio = *c - '0';
950 }
951
952 c = strstr(p, "prioclass=");
953 if (c) {
954 c += 10;
955 prioclass = *c - '0';
956 }
957
958 c = strstr(p, "file=");
959 if (c) {
960 c += 5;
961 fill_option(c, filename);
962 }
963
964 c = strstr(p, "bs=");
965 if (c) {
966 c += 3;
967 fill_option(c, string);
f737299d
JA
968 td->bs = strtoul(string, NULL, 10);
969 td->bs <<= 10;
892199bd
JA
970 }
971
972 c = strstr(p, "direct=");
973 if (c) {
974 c += 7;
975 if (*c != '0')
f737299d 976 td->odirect = 1;
892199bd 977 else
f737299d 978 td->odirect = 0;
892199bd
JA
979 }
980
981 c = strstr(p, "delay=");
982 if (c) {
983 c += 6;
984 fill_option(c, string);
f737299d 985 td->delay_sleep = strtoul(string, NULL, 10);
892199bd
JA
986 }
987
86184d14
JA
988 c = strstr(p, "rate=");
989 if (c) {
990 c += 5;
991 fill_option(c, string);
f737299d 992 td->rate = strtoul(string, NULL, 10);
86184d14
JA
993 }
994
4240cfa1
JA
995 c = strstr(p, "ratemin=");
996 if (c) {
997 c += 8;
998 fill_option(c, string);
999 td->ratemin = strtoul(string, NULL, 10);
1000 }
1001
1002 c = strstr(p, "ratecycle=");
1003 if (c) {
1004 c += 10;
1005 fill_option(c, string);
1006 td->ratecycle = strtoul(string, NULL, 10);
1007 }
1008
18e0b78c
JA
1009 c = strstr(p, "cpumask=");
1010 if (c) {
1011 c += 8;
1012 fill_option(c, string);
1013 cpu = strtoul(string, NULL, 10);
f737299d 1014 fill_cpu_mask(td->cpumask, cpu);
18e0b78c
JA
1015 }
1016
4240cfa1
JA
1017 c = strstr(p, "fsync=");
1018 if (c) {
1019 c += 6;
1020 fill_option(c, string);
1021 td->fsync_blocks = strtoul(string, NULL, 10);
1022 }
18e0b78c 1023
fc24389f
JA
1024 c = strstr(p, "startdelay=");
1025 if (c) {
1026 c += 11;
1027 fill_option(c, string);
1028 td->start_delay = strtoul(string, NULL, 10);
1029 }
1030
43000118
JA
1031 c = strstr(p, "aio_depth=");
1032 if (c) {
1033 c += 10;
1034 fill_option(c, string);
1035 td->aio_depth = strtoul(string, NULL, 10);
1036 }
1037
1038 c = strstr(p, "aio");
1039 if (c)
1040 td->use_aio = 1;
1041
892199bd
JA
1042 c = strstr(p, "random");
1043 if (c)
f737299d 1044 td->sequential = 0;
892199bd
JA
1045 c = strstr(p, "sequential");
1046 if (c)
f737299d 1047 td->sequential = 1;
892199bd 1048
4240cfa1
JA
1049 if (add_job(td, filename, prioclass, prio))
1050 put_job(td);
892199bd
JA
1051 }
1052
7dd1389e
JA
1053 free(string);
1054 free(filename);
892199bd
JA
1055}
1056
5c24b2c4 1057static int check_int(char *p, char *name, unsigned int *val)
7dd1389e
JA
1058{
1059 char str[128];
1060
1061 sprintf(str, "%s=%%d", name);
1062 if (sscanf(p, str, val) == 1)
1063 return 0;
1064
1065 sprintf(str, "%s = %%d", name);
1066 if (sscanf(p, str, val) == 1)
1067 return 0;
1068
1069 return 1;
1070}
1071
5c24b2c4 1072static int is_empty(char *line)
7dd1389e
JA
1073{
1074 unsigned int i;
1075
1076 for (i = 0; i < strlen(line); i++)
1077 if (!isspace(line[i]) && !iscntrl(line[i]))
1078 return 0;
1079
1080 return 1;
1081}
1082
5c24b2c4 1083static int parse_jobs_ini(char *file)
7dd1389e 1084{
4240cfa1 1085 unsigned int prioclass, prio, cpu;
f737299d 1086 struct thread_data *td;
7dd1389e
JA
1087 char *string, *name;
1088 fpos_t off;
1089 FILE *f;
1090 char *p;
1091
1092 f = fopen(file, "r");
1093 if (!f) {
1094 perror("fopen");
4240cfa1 1095 return 1;
7dd1389e
JA
1096 }
1097
1098 string = malloc(4096);
1099 name = malloc(256);
1100
7dd1389e
JA
1101 while ((p = fgets(string, 4096, f)) != NULL) {
1102 if (sscanf(p, "[%s]", name) != 1)
1103 continue;
1104
1105 name[strlen(name) - 1] = '\0';
1106
f737299d 1107 td = get_new_job();
4240cfa1
JA
1108 if (!td)
1109 break;
f737299d 1110
7dd1389e 1111 prioclass = 2;
f737299d 1112 prio = 4;
7dd1389e
JA
1113
1114 fgetpos(f, &off);
1115 while ((p = fgets(string, 4096, f)) != NULL) {
1116 if (is_empty(p))
e6402082
JA
1117 continue;
1118 if (strstr(p, "["))
7dd1389e 1119 break;
f737299d
JA
1120 if (!check_int(p, "bs", &td->bs)) {
1121 td->bs <<= 10;
7dd1389e
JA
1122 fgetpos(f, &off);
1123 continue;
1124 }
f737299d 1125 if (!check_int(p, "rw", &td->ddir)) {
7dd1389e
JA
1126 fgetpos(f, &off);
1127 continue;
1128 }
1129 if (!check_int(p, "prio", &prio)) {
1130 fgetpos(f, &off);
1131 continue;
1132 }
1133 if (!check_int(p, "prioclass", &prioclass)) {
1134 fgetpos(f, &off);
1135 continue;
1136 }
f737299d 1137 if (!check_int(p, "direct", &td->odirect)) {
7dd1389e
JA
1138 fgetpos(f, &off);
1139 continue;
1140 }
f737299d 1141 if (!check_int(p, "rate", &td->rate)) {
7dd1389e
JA
1142 fgetpos(f, &off);
1143 continue;
1144 }
4240cfa1
JA
1145 if (!check_int(p, "ratemin", &td->ratemin)) {
1146 fgetpos(f, &off);
1147 continue;
1148 }
1149 if (!check_int(p, "ratecycle", &td->ratecycle)) {
1150 fgetpos(f, &off);
1151 continue;
1152 }
f737299d 1153 if (!check_int(p, "delay", &td->delay_sleep)) {
7dd1389e
JA
1154 fgetpos(f, &off);
1155 continue;
1156 }
18e0b78c 1157 if (!check_int(p, "cpumask", &cpu)) {
f737299d 1158 fill_cpu_mask(td->cpumask, cpu);
18e0b78c
JA
1159 fgetpos(f, &off);
1160 continue;
1161 }
4240cfa1
JA
1162 if (!check_int(p, "fsync", &td->fsync_blocks)) {
1163 fgetpos(f, &off);
1164 continue;
1165 }
fc24389f
JA
1166 if (!check_int(p, "startdelay", &td->start_delay)) {
1167 fgetpos(f, &off);
1168 continue;
1169 }
43000118
JA
1170 if (!check_int(p, "aio_depth", &td->aio_depth)) {
1171 fgetpos(f, &off);
1172 continue;
1173 }
1174 if (!strncmp(p, "sequential", 10)) {
f737299d 1175 td->sequential = 1;
7dd1389e
JA
1176 fgetpos(f, &off);
1177 continue;
1178 }
43000118 1179 if (!strncmp(p, "random", 6)) {
f737299d 1180 td->sequential = 0;
7dd1389e
JA
1181 fgetpos(f, &off);
1182 continue;
1183 }
43000118
JA
1184 if (!strncmp(p, "aio", 3)) {
1185 td->use_aio = 1;
1186 fgetpos(f, &off);
1187 continue;
1188 }
1189
e6402082 1190 printf("Client%d: bad option %s\n",td->thread_number,p);
7dd1389e
JA
1191 }
1192 fsetpos(f, &off);
1193
4240cfa1
JA
1194 if (add_job(td, name, prioclass, prio))
1195 put_job(td);
7dd1389e
JA
1196 }
1197
1198 free(string);
1199 free(name);
fc7d63df 1200 fclose(f);
4240cfa1 1201 return 0;
7dd1389e
JA
1202}
1203
5c24b2c4 1204static int parse_options(int argc, char *argv[])
892199bd 1205{
01c4d8de 1206 int i;
892199bd
JA
1207
1208 for (i = 1; i < argc; i++) {
1209 char *parm = argv[i];
1210
1211 if (parm[0] != '-')
1212 break;
1213
1214 parm++;
1215 switch (*parm) {
1216 case 's':
1217 parm++;
1218 sequential = !!atoi(parm);
1219 break;
1220 case 'b':
1221 parm++;
1222 global_bs = atoi(parm);
1223 global_bs <<= 10;
4240cfa1
JA
1224 if (!global_bs) {
1225 printf("bad block size\n");
1226 global_bs = DEF_BS;
1227 }
892199bd
JA
1228 break;
1229 case 't':
1230 parm++;
1231 timeout = atoi(parm);
1232 break;
1233 case 'w':
1234 parm++;
1235 write_stat = !!atoi(parm);
1236 break;
1237 case 'r':
1238 parm++;
1239 repeatable = !!atoi(parm);
1240 break;
02bdd9ba
JA
1241 case 'R':
1242 parm++;
1243 rate_quit = !!atoi(parm);
1244 break;
892199bd
JA
1245 case 'o':
1246 parm++;
1247 odirect = !!atoi(parm);
1248 break;
7dd1389e
JA
1249 case 'f':
1250 if (i + 1 >= argc) {
1251 printf("-f needs file as arg\n");
1252 break;
1253 }
1254 ini_file = strdup(argv[i+1]);
1255 break;
892199bd 1256 default:
7dd1389e 1257 printf("bad option %s\n", argv[i]);
892199bd
JA
1258 break;
1259 }
1260 }
1261
892199bd
JA
1262 return i;
1263}
1264
213b446c 1265static void reap_threads(int *nr_running, int *t_rate, int *m_rate)
02bdd9ba 1266{
213b446c 1267 int i;
02bdd9ba
JA
1268
1269 for (i = 0; i < thread_number; i++) {
1270 struct thread_data *td = &threads[i];
1271
213b446c
JA
1272 if (td->runstate != TD_EXITED)
1273 continue;
02bdd9ba 1274
213b446c
JA
1275 td->runstate = TD_REAPED;
1276 waitpid(td->pid, NULL, 0);
1277 (*nr_running)--;
1278 (*m_rate) -= td->ratemin;
1279 (*t_rate) -= td->rate;
e6402082
JA
1280
1281 if (td->terminate)
1282 continue;
1283
213b446c
JA
1284 printf("Threads now running: %d", *nr_running);
1285 if (*m_rate || *t_rate)
1286 printf(", rate %d/%dKiB/sec", *t_rate, *m_rate);
1287 printf("\n");
1288 }
02bdd9ba
JA
1289}
1290
fc24389f
JA
1291static void run_threads(char *argv[])
1292{
1293 struct timeval genesis, now;
1294 struct thread_data *td;
1295 unsigned long spent;
213b446c 1296 int i, todo, nr_running, m_rate, t_rate;
fc24389f
JA
1297
1298 gettimeofday(&genesis, NULL);
1299
1300 printf("Starting %d threads\n", thread_number);
1301 fflush(stdout);
1302
1303 if (timeout) {
1304 signal(SIGALRM, sig_handler);
56b0eff0 1305 signal(SIGINT, sig_handler);
fc24389f
JA
1306 alarm(timeout);
1307 }
1308
1309 todo = thread_number;
02bdd9ba 1310 nr_running = 0;
213b446c 1311 m_rate = t_rate = 0;
fc24389f 1312
213b446c 1313 while (todo) {
fc24389f
JA
1314 for (i = 0; i < thread_number; i++) {
1315 td = &threads[i];
1316
02bdd9ba 1317 if (td->runstate != TD_NOT_CREATED)
fc24389f
JA
1318 continue;
1319
213b446c
JA
1320 /*
1321 * never got a chance to start, killed by other
1322 * thread for some reason
1323 */
1324 if (td->terminate) {
1325 todo--;
1326 continue;
1327 }
1328
fc24389f
JA
1329 if (td->start_delay) {
1330 gettimeofday(&now, NULL);
1331 spent = mtime_since(&genesis, &now);
1332
1333 if (td->start_delay * 1000 > spent)
1334 continue;
1335 }
1336
02bdd9ba 1337 td->runstate = TD_CREATED;
fc24389f
JA
1338 sem_init(&startup_sem, 1, 1);
1339 todo--;
1340
1341 if (fork())
1342 sem_wait(&startup_sem);
1343 else {
1344 thread_main(shm_id, i, argv);
1345 exit(0);
1346 }
1347 }
1348
1349 for (i = 0; i < thread_number; i++) {
1350 struct thread_data *td = &threads[i];
1351
02bdd9ba
JA
1352 if (td->runstate == TD_CREATED) {
1353 td->runstate = TD_STARTED;
1354 nr_running++;
213b446c
JA
1355 m_rate += td->ratemin;
1356 t_rate += td->rate;
fc24389f 1357 sem_post(&td->mutex);
213b446c
JA
1358
1359 printf("Threads now running: %d", nr_running);
1360 if (m_rate || t_rate)
1361 printf(", rate %d/%dKiB/sec", t_rate, m_rate);
1362 printf("\n");
fc24389f
JA
1363 }
1364 }
1365
213b446c 1366 reap_threads(&nr_running, &t_rate, &m_rate);
02bdd9ba 1367
fc24389f
JA
1368 if (todo)
1369 usleep(100000);
1370 }
02bdd9ba
JA
1371
1372 while (nr_running) {
213b446c 1373 reap_threads(&nr_running, &t_rate, &m_rate);
02bdd9ba
JA
1374 usleep(10000);
1375 }
fc24389f
JA
1376}
1377
892199bd
JA
1378int main(int argc, char *argv[])
1379{
1380 static unsigned long max_run[2], min_run[2], total_blocks[2];
1381 static unsigned long max_bw[2], min_bw[2], maxl[2], minl[2];
1382 static unsigned long read_mb, write_mb, read_agg, write_agg;
4240cfa1 1383 int i;
18e0b78c 1384
4240cfa1 1385 shm_id = shmget(0, MAX_JOBS * sizeof(struct thread_data), IPC_CREAT | 0600);
892199bd
JA
1386 if (shm_id == -1) {
1387 perror("shmget");
1388 return 1;
1389 }
1390
1391 threads = shmat(shm_id, NULL, 0);
86184d14
JA
1392 if (threads == (void *) -1 ) {
1393 perror("shmat");
1394 return 1;
1395 }
892199bd
JA
1396
1397 atexit(free_shm);
1398
4240cfa1
JA
1399 if (sched_getaffinity(getpid(), sizeof(def_cpumask), &def_cpumask) == -1) {
1400 perror("sched_getaffinity");
1401 return 1;
1402 }
1403
892199bd 1404 i = parse_options(argc, argv);
7dd1389e 1405
4240cfa1
JA
1406 if (ini_file) {
1407 if (parse_jobs_ini(ini_file))
1408 return 1;
1409 } else
1410 parse_jobs_cmd(argc, argv, i);
7dd1389e 1411
4240cfa1
JA
1412 if (!thread_number) {
1413 printf("Nothing to do\n");
1414 return 1;
1415 }
7dd1389e
JA
1416
1417 printf("%s: %s, bs=%uKiB, timeo=%u, write_stat=%u, odirect=%d\n", argv[0], sequential ? "sequential" : "random", global_bs >> 10, timeout, write_stat, odirect);
1418
fc24389f 1419 run_threads(argv);
892199bd 1420
892199bd
JA
1421 min_bw[0] = min_run[0] = ~0UL;
1422 min_bw[1] = min_run[1] = ~0UL;
1423 minl[0] = minl[1] = ~0UL;
1424 for (i = 0; i < thread_number; i++) {
1425 struct thread_data *td = &threads[i];
1426 unsigned long bw = 0;
1427
1428 if (td->error)
7dd1389e 1429 goto show_stat;
892199bd
JA
1430
1431 if (td->runtime < min_run[td->ddir])
1432 min_run[td->ddir] = td->runtime;
1433 if (td->runtime > max_run[td->ddir])
1434 max_run[td->ddir] = td->runtime;
1435
1436 if (td->runtime)
4240cfa1 1437 bw = (td->io_blocks * td->bs) / td->runtime;
892199bd
JA
1438 if (bw < min_bw[td->ddir])
1439 min_bw[td->ddir] = bw;
1440 if (bw > max_bw[td->ddir])
1441 max_bw[td->ddir] = bw;
1442 if (td->max_latency < minl[td->ddir])
1443 minl[td->ddir] = td->max_latency;
1444 if (td->max_latency > maxl[td->ddir])
1445 maxl[td->ddir] = td->max_latency;
1446
4240cfa1 1447 total_blocks[td->ddir] += td->io_blocks;
892199bd
JA
1448
1449 if (td->ddir == DDIR_READ) {
4240cfa1 1450 read_mb += (td->bs * td->io_blocks) >> 20;
892199bd 1451 if (td->runtime)
4240cfa1 1452 read_agg += (td->io_blocks * td->bs) / td->runtime;
892199bd
JA
1453 }
1454 if (td->ddir == DDIR_WRITE) {
4240cfa1 1455 write_mb += (td->bs * td->io_blocks) >> 20;
892199bd 1456 if (td->runtime)
4240cfa1 1457 write_agg += (td->io_blocks * td->bs) / td->runtime;
892199bd
JA
1458 }
1459
7dd1389e 1460show_stat:
892199bd
JA
1461 show_thread_status(td);
1462 }
1463
1464 printf("Run status:\n");
1465 if (max_run[DDIR_READ])
1466 printf(" READ: io=%luMiB, aggrb=%lu, minl=%lu, maxl=%lu, minb=%lu, maxb=%lu, mint=%lumsec, maxt=%lumsec\n", read_mb, read_agg, minl[0], maxl[0], min_bw[0], max_bw[0], min_run[0], max_run[0]);
1467 if (max_run[DDIR_WRITE])
1468 printf(" WRITE: io=%luMiB, aggrb=%lu, minl=%lu, maxl=%lu, minb=%lu, maxb=%lu, mint=%lumsec, maxt=%lumsec\n", write_mb, write_agg, minl[1], maxl[1], min_bw[1], max_bw[1], min_run[1], max_run[1]);
fc24389f 1469
892199bd
JA
1470 return 0;
1471}