[PATCH] drivescan: default to 64k block size
[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>
892199bd
JA
30#include <sys/time.h>
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <sys/wait.h>
34#include <semaphore.h>
35#include <sys/ipc.h>
36#include <sys/shm.h>
37#include <asm/unistd.h>
38
39/*
40 * assume we don't have _get either, if _set isn't defined
41 */
42#ifndef __NR_ioprio_set
43
44#if defined(__i386__)
45#define __NR_ioprio_set 289
46#define __NR_ioprio_get 290
47#elif defined(__powerpc__) || defined(__powerpc64__)
48#define __NR_ioprio_set 273
49#define __NR_ioprio_get 274
50#elif defined(__x86_64__)
51#define __NR_ioprio_set 251
52#define __NR_ioprio_get 252
53#elif defined(__ia64__)
54#define __NR_ioprio_set 1274
55#define __NR_ioprio_get 1275
56#elif defined(__alpha__)
57#define __NR_ioprio_set 442
58#define __NR_ioprio_get 443
59#elif defined(__s390x__) || defined(__s390__)
60#define __NR_ioprio_set 282
61#define __NR_ioprio_get 283
62#else
63#error "Unsupported arch"
64#endif
65
66#endif
67
68static int ioprio_set(int which, int who, int ioprio)
69{
70 return syscall(__NR_ioprio_set, which, who, ioprio);
71}
72
73enum {
74 IOPRIO_WHO_PROCESS = 1,
75 IOPRIO_WHO_PGRP,
76 IOPRIO_WHO_USER,
77};
78
79#define IOPRIO_CLASS_SHIFT 13
80
81#define BS (4096)
82#define MASK (4095)
83
84#define TIMEOUT (30)
892199bd
JA
85
86#define ALIGN(buf) (((unsigned long) (buf) + MASK) & ~(MASK))
87
88static int sequential = 1;
89static int write_stat = 0;
90static int repeatable = 1;
91static int thread_number;
92static int timeout = TIMEOUT;
93static int odirect = 1;
94static int global_bs = BS;
7dd1389e 95static char *ini_file;
892199bd
JA
96
97static int shm_id;
98
99#define DDIR_READ (0)
100#define DDIR_WRITE (1)
101
102struct thread_data {
103 char file_name[256];
104 int thread_number;
105 int error;
106 int fd;
107 int stat_fd;
108 pid_t pid;
109 int terminate;
110 int ddir;
111 int ioprio;
112 int sequential;
113 int bs;
114 int odirect;
115 int delay_sleep;
86184d14 116
7dd1389e
JA
117 unsigned int rate;
118 unsigned int rate_usec_cycle;
119 unsigned int rate_pending_usleep;
86184d14 120
892199bd
JA
121 unsigned long max_latency; /* msec */
122 unsigned long min_latency; /* msec */
123 unsigned long runtime; /* sec */
124 unsigned long blocks;
125 unsigned long blocks_read;
126 unsigned long last_block;
127 sem_t mutex;
128 sem_t done_mutex;
129 struct drand48_data random_state;
130
131 /*
132 * bandwidth stat
133 */
134 unsigned long stat_time;
135 unsigned long stat_time_last;
136 unsigned long stat_blocks_last;
137};
138
139static struct thread_data *threads;
892199bd
JA
140
141static sem_t startup_sem;
142
143void sig_handler(int sig)
144{
145 int i;
146
147 for (i = 0; i < thread_number; i++) {
148 struct thread_data *td = &threads[i];
149
150 td->terminate = 1;
151 }
152}
153
154int init_random_state(struct thread_data *td)
155{
156 unsigned long seed = 123;
157
158 if (td->sequential)
159 return 0;
160
161 if (!repeatable) {
162 int fd = open("/dev/random", O_RDONLY);
163
164 if (fd == -1) {
165 td->error = errno;
166 return 1;
167 }
168
7dd1389e 169 if (read(fd, &seed, sizeof(seed)) < (int) sizeof(seed)) {
892199bd
JA
170 td->error = EIO;
171 close(fd);
172 return 1;
173 }
174
175 close(fd);
176 }
177
178 srand48_r(seed, &td->random_state);
179 return 0;
180}
181
182void shutdown_stat_file(struct thread_data *td)
183{
184 if (td->stat_fd != -1) {
185 fsync(td->stat_fd);
186 close(td->stat_fd);
187 }
188}
189
190int init_stat_file(struct thread_data *td)
191{
192 char *n;
193
194 if (!write_stat)
195 return 0;
196
197 n = malloc(256);
198 sprintf(n, "%s.stat", td->file_name);
199 td->stat_fd = open(n, O_WRONLY | O_CREAT | O_TRUNC, 0644);
200 if (td->stat_fd == -1) {
201 free(n);
202 td->error = errno;
203 return 1;
204 }
205
206 free(n);
207 return 0;
208}
209
210unsigned long utime_since(struct timeval *s, struct timeval *e)
211{
212 double sec, usec;
213
214 sec = e->tv_sec - s->tv_sec;
215 usec = e->tv_usec - s->tv_usec;
216 if (sec > 0 && usec < 0) {
217 sec--;
218 usec += 1000000;
219 }
220
221 sec *= (double) 1000000;
222
223 return sec + usec;
224}
225
226unsigned long mtime_since(struct timeval *s, struct timeval *e)
227{
228 double sec, usec;
229
230 sec = e->tv_sec - s->tv_sec;
231 usec = e->tv_usec - s->tv_usec;
232 if (sec > 0 && usec < 0) {
233 sec--;
234 usec += 1000000;
235 }
236
237 sec *= (double) 1000;
238 usec /= (double) 1000;
239
240 return sec + usec;
241}
242
243unsigned long time_since(struct timeval *s, struct timeval *e)
244{
245 double sec, usec, ret;
246
247 sec = e->tv_sec - s->tv_sec;
248 usec = e->tv_usec - s->tv_usec;
249 if (sec > 0 && usec < 0) {
250 sec--;
251 usec += 1000000;
252 }
253
254 ret = sec + usec / (double) 1000000;
255 if (ret < 0)
256 ret = 0;
257
258 return (unsigned long) ret;
259}
260
261unsigned long get_next_offset(struct thread_data *td)
262{
263 unsigned long b;
264 long r;
265
266 if (!td->sequential) {
267 lrand48_r(&td->random_state, &r);
268 b = (1+(double) (td->blocks-1) * r / (RAND_MAX+1.0));
269 } else {
270 b = td->last_block;
271 td->last_block++;
272 }
273
274 return b * td->bs;
275}
276
7dd1389e 277void add_stat_sample(struct thread_data *td, unsigned long msec)
892199bd
JA
278{
279 char sample[256];
280
281 if (!td->stat_fd)
282 return;
283
284#if 0
285 sprintf(sample, "%lu, %lu\n", td->blocks_read, msec);
286 write(td->stat_fd, sample, strlen(sample));
287#else
288 td->stat_time += msec;
289 td->stat_time_last += msec;
290 td->stat_blocks_last++;
291
292 if (td->stat_time_last >= 500) {
293 unsigned long rate = td->stat_blocks_last * td->bs / (td->stat_time_last);
294
295 td->stat_time_last = 0;
296 td->stat_blocks_last = 0;
297 sprintf(sample, "%lu, %lu\n", td->stat_time, rate);
298 //sprintf(sample, "%lu, %lu\n", td->blocks_read, msec);
299 write(td->stat_fd, sample, strlen(sample));
300 }
301#endif
302}
303
86184d14 304void usec_sleep(int usec)
892199bd 305{
86184d14
JA
306 struct timespec req = { .tv_sec = 0, .tv_nsec = usec * 1000 };
307 struct timespec rem;
892199bd
JA
308
309 do {
86184d14
JA
310 rem.tv_sec = rem.tv_nsec = 0;
311 nanosleep(&req, &rem);
312 if (!rem.tv_nsec)
892199bd 313 break;
86184d14
JA
314
315 req.tv_nsec = rem.tv_nsec;
892199bd
JA
316 } while (1);
317}
318
86184d14
JA
319void rate_throttle(struct thread_data *td, unsigned long time_spent)
320{
321 if (time_spent < td->rate_usec_cycle) {
322 unsigned long s = td->rate_usec_cycle - time_spent;
323
324 td->rate_pending_usleep += s;
fad86e6a 325 if (td->rate_pending_usleep >= 100000) {
86184d14
JA
326 usec_sleep(td->rate_pending_usleep);
327 td->rate_pending_usleep = 0;
328 }
42b2b9fe
JA
329 } else if (td->rate_pending_usleep) {
330 long overtime = time_spent - td->rate_usec_cycle;
331
332 if (overtime > td->rate_pending_usleep)
333 td->rate_pending_usleep = 0;
334 else
335 td->rate_pending_usleep -= overtime;
86184d14
JA
336 }
337}
338
892199bd
JA
339void do_thread_io(struct thread_data *td)
340{
341 struct timeval s, e, start;
342 char *buffer, *ptr;
86184d14 343 unsigned long blocks, msec, usec;
892199bd
JA
344
345 ptr = malloc(td->bs+MASK);
346 buffer = (char *) ALIGN(ptr);
347
348 gettimeofday(&start, NULL);
349
350 for (blocks = 0; blocks < td->blocks; blocks++) {
351 off_t offset = get_next_offset(td);
352 int ret;
353
354 if (td->terminate)
355 break;
356
357 if (lseek(td->fd, offset, SEEK_SET) == -1) {
358 td->error = errno;
359 break;
360 }
361
362 if (td->delay_sleep)
86184d14 363 usec_sleep(td->delay_sleep);
892199bd
JA
364
365 gettimeofday(&s, NULL);
366
367 if (td->ddir == DDIR_READ)
368 ret = read(td->fd, buffer, td->bs);
369 else
370 ret = write(td->fd, buffer, td->bs);
371
892199bd
JA
372 if (ret < td->bs) {
373 if (ret == -1)
374 td->error = errno;
375 break;
376 }
377
86184d14
JA
378 gettimeofday(&e, NULL);
379
380 usec = utime_since(&s, &e);
381 msec = usec / 1000;
382
383 if (td->rate)
384 rate_throttle(td, usec);
892199bd 385
7dd1389e 386 add_stat_sample(td, msec);
892199bd
JA
387
388 td->blocks_read++;
389
390 //if (td->ddir == DDIR_WRITE && !(td->blocks_read % 512))
391 // fsync(td->fd);
392
393 if (msec < td->min_latency)
394 td->min_latency = msec;
395 if (msec > td->max_latency)
396 td->max_latency = msec;
397 }
398
399 if (td->ddir == DDIR_WRITE && !td->odirect)
400 fsync(td->fd);
401
402 gettimeofday(&e, NULL);
403 td->runtime = mtime_since(&start, &e);
404
405 free(ptr);
406}
407
408void *thread_main(int shm_id, int offset, char *argv[])
409{
410 struct thread_data *td;
411 void *data;
412 struct stat *statbuf = NULL;
413 int ret = 1, flags;
414
415 data = shmat(shm_id, NULL, 0);
416 td = data + offset * sizeof(struct thread_data);
417 td->pid = getpid();
418
419 printf("Thread (%s) (pid=%u) (f=%s) started\n", td->ddir == DDIR_READ ? "read" : "write", td->pid, td->file_name);
420 fflush(stdout);
421
422 sprintf(argv[0], "%s%d\n", argv[0], offset);
423
7dd1389e 424 flags = 0;
892199bd
JA
425 if (td->odirect)
426 flags |= O_DIRECT;
427
7dd1389e
JA
428 if (td->ddir == DDIR_READ)
429 td->fd = open(td->file_name, flags | O_RDONLY);
430 else
431 td->fd = open(td->file_name, flags | O_WRONLY | O_CREAT | O_TRUNC, 0644);
432
892199bd
JA
433 if (td->fd == -1) {
434 td->error = errno;
435 goto err;
436 }
437
438 if (init_random_state(td))
439 goto out;
440 if (init_stat_file(td))
441 goto out;
442
443 if (td->ddir == DDIR_READ) {
444 statbuf = malloc(sizeof(*statbuf));
445 if (fstat(td->fd, statbuf) == -1) {
446 td->error = errno;
447 goto out;
448 }
449
450 td->blocks = statbuf->st_size / td->bs;
451 if (!td->blocks) {
452 td->error = EINVAL;
453 goto out;
454 }
455 } else
456 td->blocks = 1024 * 1024 * 1024 / td->bs;
457
458 if (td->ioprio != -1) {
459 if (ioprio_set(IOPRIO_WHO_PROCESS, 0, td->ioprio) == -1) {
460 td->error = errno;
461 goto out;
462 }
463 }
464
465 sem_post(&startup_sem);
466 sem_wait(&td->mutex);
467 do_thread_io(td);
468 ret = 0;
469
470out:
471 close(td->fd);
472 if (statbuf)
473 free(statbuf);
474 shutdown_stat_file(td);
475err:
476 sem_post(&td->done_mutex);
477 if (ret)
478 sem_post(&startup_sem);
479 shmdt(td);
480 return NULL;
481}
482
483void free_shm(void)
484{
485 shmdt(threads);
486}
487
488void show_thread_status(struct thread_data *td)
489{
490 int prio, prio_class;
491 unsigned long bw = 0;
492
493 if (td->runtime)
494 bw = (td->blocks_read * td->bs) / td->runtime;
495
496 prio = td->ioprio & 0xff;
497 prio_class = td->ioprio >> IOPRIO_CLASS_SHIFT;
498
499 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->blocks_read * td->bs >> 20, bw);
500}
501
502void usage(char *progname)
503{
504 printf("%s: <-s 0/1> <-b kb> <-t sec> <-w 0/1> <-c r,w,r...> file0... fileN\n", progname);
505}
506
86184d14
JA
507void setup_rate(struct thread_data *td, int rate)
508{
509 int nr_reads_per_sec = rate * 1024 / td->bs;
510
511 td->rate = rate;
512 td->rate_usec_cycle = 1000000 / nr_reads_per_sec;
513 td->rate_pending_usleep = 0;
514}
515
516void add_job(const char *filename, int rw, int bs, int direct, int prio, int random, int delay, int rate)
892199bd 517{
86184d14 518 struct thread_data *td = &threads[thread_number++];
892199bd
JA
519
520 strcpy(td->file_name, filename);
86184d14 521 td->thread_number = thread_number;
892199bd
JA
522 td->stat_fd = -1;
523 sem_init(&td->mutex, 1, 1);
524 sem_init(&td->done_mutex, 1, 0);
525 td->min_latency = 10000000;
526 td->ddir = rw;
527 td->ioprio = prio;
528 td->sequential = !random;
529 td->odirect = direct;
530 td->bs = bs;
531 td->delay_sleep = delay;
532
86184d14
JA
533 if (rate)
534 setup_rate(td, rate);
7dd1389e
JA
535
536 printf("Client%d: file=%s, rw=%d, prio=%d, seq=%d, odir=%d, bs=%d, rate=%d\n", thread_number, filename, rw, prio, !random, direct, bs, rate);
892199bd
JA
537}
538
539void fill_option(const char *input, char *output)
540{
541 int i;
542
543 i = 0;
544 while (input[i] != ',' && input[i] != '}' && input[i] != '\0') {
545 output[i] = input[i];
546 i++;
547 }
548
549 output[i] = '\0';
550}
551
552/*
553 * job key words:
554 *
555 * file=
556 * bs=
557 * rw=
558 * direct=
559 */
7dd1389e 560int parse_jobs_cmd(int argc, char *argv[], int index)
892199bd 561{
86184d14 562 int rw, bs, direct, prio, random, prioclass, delay, rate;
892199bd
JA
563 char *string, *filename, *p, *c;
564 int i;
565
566 string = malloc(256);
567 filename = malloc(256);
568
569 for (i = index; i < argc; i++) {
570 p = argv[i];
571
572 c = strpbrk(p, "{");
573 if (!c)
574 break;
575
576 filename[0] = 0;
577 rw = DDIR_READ;
578 bs = global_bs;
579 direct = 1;
580 prio = 4;
581 random = !sequential;
582 prioclass = 2;
583 delay = 0;
86184d14 584 rate = 0;
892199bd
JA
585
586 c = strstr(p, "rw=");
587 if (c) {
588 c += 3;
589 if (*c == '0')
590 rw = DDIR_READ;
591 else
592 rw = DDIR_WRITE;
593 }
594
595 c = strstr(p, "prio=");
596 if (c) {
597 c += 5;
598 prio = *c - '0';
599 }
600
601 c = strstr(p, "prioclass=");
602 if (c) {
603 c += 10;
604 prioclass = *c - '0';
605 }
606
607 c = strstr(p, "file=");
608 if (c) {
609 c += 5;
610 fill_option(c, filename);
611 }
612
613 c = strstr(p, "bs=");
614 if (c) {
615 c += 3;
616 fill_option(c, string);
617 bs = strtoul(string, NULL, 10);
618 bs <<= 10;
619 }
620
621 c = strstr(p, "direct=");
622 if (c) {
623 c += 7;
624 if (*c != '0')
625 direct = 1;
626 else
627 direct = 0;
628 }
629
630 c = strstr(p, "delay=");
631 if (c) {
632 c += 6;
633 fill_option(c, string);
634 delay = strtoul(string, NULL, 10);
635 }
636
86184d14
JA
637 c = strstr(p, "rate=");
638 if (c) {
639 c += 5;
640 fill_option(c, string);
641 rate = strtoul(string, NULL, 10);
642 }
643
892199bd
JA
644 c = strstr(p, "random");
645 if (c)
646 random = 1;
647 c = strstr(p, "sequential");
648 if (c)
649 random = 0;
650
86184d14 651 add_job(filename, rw, bs, direct, (prioclass << IOPRIO_CLASS_SHIFT) | prio, random, delay, rate);
892199bd
JA
652 }
653
7dd1389e
JA
654 free(string);
655 free(filename);
892199bd
JA
656 return thread_number;
657}
658
7dd1389e
JA
659int check_int(char *p, char *name, int *val)
660{
661 char str[128];
662
663 sprintf(str, "%s=%%d", name);
664 if (sscanf(p, str, val) == 1)
665 return 0;
666
667 sprintf(str, "%s = %%d", name);
668 if (sscanf(p, str, val) == 1)
669 return 0;
670
671 return 1;
672}
673
674int is_empty(char *line)
675{
676 unsigned int i;
677
678 for (i = 0; i < strlen(line); i++)
679 if (!isspace(line[i]) && !iscntrl(line[i]))
680 return 0;
681
682 return 1;
683}
684
685int parse_jobs_ini(char *file)
686{
687 int rw, bs, direct, prio, random, prioclass, delay, rate, jobs;
688 char *string, *name;
689 fpos_t off;
690 FILE *f;
691 char *p;
692
693 f = fopen(file, "r");
694 if (!f) {
695 perror("fopen");
696 return 0;
697 }
698
699 string = malloc(4096);
700 name = malloc(256);
701
702 jobs = 0;
703
704 while ((p = fgets(string, 4096, f)) != NULL) {
705 if (sscanf(p, "[%s]", name) != 1)
706 continue;
707
708 name[strlen(name) - 1] = '\0';
709
710 rw = DDIR_READ;
711 bs = global_bs;
712 direct = 1;
713 prio = 4;
714 random = !sequential;
715 prioclass = 2;
716 delay = 0;
717 rate = 0;
718
719 fgetpos(f, &off);
720 while ((p = fgets(string, 4096, f)) != NULL) {
721 if (is_empty(p))
722 break;
723 if (!check_int(p, "bs", &bs)) {
724 bs <<= 10;
725 fgetpos(f, &off);
726 continue;
727 }
728 if (!check_int(p, "rw", &rw)) {
729 fgetpos(f, &off);
730 continue;
731 }
732 if (!check_int(p, "prio", &prio)) {
733 fgetpos(f, &off);
734 continue;
735 }
736 if (!check_int(p, "prioclass", &prioclass)) {
737 fgetpos(f, &off);
738 continue;
739 }
740 if (!check_int(p, "direct", &direct)) {
741 fgetpos(f, &off);
742 continue;
743 }
744 if (!check_int(p, "rate", &rate)) {
745 fgetpos(f, &off);
746 continue;
747 }
748 if (!check_int(p, "delay", &delay)) {
749 fgetpos(f, &off);
750 continue;
751 }
752 if (!strcmp(p, "sequential")) {
753 sequential = 1;
754 fgetpos(f, &off);
755 continue;
756 }
757 if (!strcmp(p, "random")) {
758 sequential = 0;
759 fgetpos(f, &off);
760 continue;
761 }
762 }
763 fsetpos(f, &off);
764
765 add_job(name, rw, bs, direct, (prioclass << IOPRIO_CLASS_SHIFT) | prio, random, delay, rate);
766 jobs++;
767 }
768
769 free(string);
770 free(name);
771 return jobs;
772}
773
892199bd
JA
774int parse_options(int argc, char *argv[])
775{
01c4d8de 776 int i;
892199bd
JA
777
778 for (i = 1; i < argc; i++) {
779 char *parm = argv[i];
780
781 if (parm[0] != '-')
782 break;
783
784 parm++;
785 switch (*parm) {
786 case 's':
787 parm++;
788 sequential = !!atoi(parm);
789 break;
790 case 'b':
791 parm++;
792 global_bs = atoi(parm);
793 global_bs <<= 10;
794 break;
795 case 't':
796 parm++;
797 timeout = atoi(parm);
798 break;
799 case 'w':
800 parm++;
801 write_stat = !!atoi(parm);
802 break;
803 case 'r':
804 parm++;
805 repeatable = !!atoi(parm);
806 break;
892199bd
JA
807 case 'o':
808 parm++;
809 odirect = !!atoi(parm);
810 break;
7dd1389e
JA
811 case 'f':
812 if (i + 1 >= argc) {
813 printf("-f needs file as arg\n");
814 break;
815 }
816 ini_file = strdup(argv[i+1]);
817 break;
892199bd 818 default:
7dd1389e 819 printf("bad option %s\n", argv[i]);
892199bd
JA
820 break;
821 }
822 }
823
892199bd
JA
824 return i;
825}
826
827int main(int argc, char *argv[])
828{
829 static unsigned long max_run[2], min_run[2], total_blocks[2];
830 static unsigned long max_bw[2], min_bw[2], maxl[2], minl[2];
831 static unsigned long read_mb, write_mb, read_agg, write_agg;
832 int i, jobs;
833
86184d14 834 shm_id = shmget(0, (argc - 1) * sizeof(struct thread_data), IPC_CREAT | 0600);
892199bd
JA
835 if (shm_id == -1) {
836 perror("shmget");
837 return 1;
838 }
839
840 threads = shmat(shm_id, NULL, 0);
86184d14
JA
841 if (threads == (void *) -1 ) {
842 perror("shmat");
843 return 1;
844 }
892199bd
JA
845
846 atexit(free_shm);
847
892199bd 848 i = parse_options(argc, argv);
7dd1389e
JA
849
850 if (ini_file)
851 jobs = parse_jobs_ini(ini_file);
852 else
853 jobs = parse_jobs_cmd(argc, argv, i);
854
855 if (global_bs <= 0)
856 global_bs = BS;
857 if (timeout <= 0)
858 timeout = TIMEOUT;
859
860 printf("%s: %s, bs=%uKiB, timeo=%u, write_stat=%u, odirect=%d\n", argv[0], sequential ? "sequential" : "random", global_bs >> 10, timeout, write_stat, odirect);
861
892199bd
JA
862 if (!jobs) {
863 printf("Nothing to do\n");
864 return 1;
7dd1389e
JA
865 } else
866 printf("%d Clients configured\n", jobs);
892199bd
JA
867
868 for (i = 0; i < jobs; i++) {
869 sem_init(&startup_sem, 1, 1);
870
871 if (fork())
872 sem_wait(&startup_sem);
873 else {
874 thread_main(shm_id, i, argv);
875 exit(0);
876 }
877 }
878
879 if (!thread_number) {
880 usage(argv[0]);
881 return 1;
882 }
883
884 signal(SIGALRM, sig_handler);
885 alarm(timeout);
886
887 printf("Starting %d threads\n", thread_number);
888 for (i = 0; i < thread_number; i++) {
889 struct thread_data *td = &threads[i];
890
891 sem_post(&td->mutex);
892 }
893
894 for (i = 0; i < thread_number; i++) {
895 struct thread_data *td = &threads[i];
896
897 waitpid(td->pid, NULL, 0);
898 }
899
900 min_bw[0] = min_run[0] = ~0UL;
901 min_bw[1] = min_run[1] = ~0UL;
902 minl[0] = minl[1] = ~0UL;
903 for (i = 0; i < thread_number; i++) {
904 struct thread_data *td = &threads[i];
905 unsigned long bw = 0;
906
907 if (td->error)
7dd1389e 908 goto show_stat;
892199bd
JA
909
910 if (td->runtime < min_run[td->ddir])
911 min_run[td->ddir] = td->runtime;
912 if (td->runtime > max_run[td->ddir])
913 max_run[td->ddir] = td->runtime;
914
915 if (td->runtime)
916 bw = (td->blocks_read * td->bs) / td->runtime;
917 if (bw < min_bw[td->ddir])
918 min_bw[td->ddir] = bw;
919 if (bw > max_bw[td->ddir])
920 max_bw[td->ddir] = bw;
921 if (td->max_latency < minl[td->ddir])
922 minl[td->ddir] = td->max_latency;
923 if (td->max_latency > maxl[td->ddir])
924 maxl[td->ddir] = td->max_latency;
925
926 total_blocks[td->ddir] += td->blocks_read;
927
928 if (td->ddir == DDIR_READ) {
929 read_mb += (td->bs * td->blocks_read) >> 20;
930 if (td->runtime)
931 read_agg += (td->blocks_read * td->bs) / td->runtime;
932 }
933 if (td->ddir == DDIR_WRITE) {
934 write_mb += (td->bs * td->blocks_read) >> 20;
935 if (td->runtime)
936 write_agg += (td->blocks_read * td->bs) / td->runtime;
937 }
938
7dd1389e 939show_stat:
892199bd
JA
940 show_thread_status(td);
941 }
942
943 printf("Run status:\n");
944 if (max_run[DDIR_READ])
945 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]);
946 if (max_run[DDIR_WRITE])
947 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]);
948 return 0;
949}