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