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