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