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