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