[PATCH] fio: kill unused functions (mark others static)
[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;
892199bd 102
892199bd 103static int thread_number;
7dd1389e 104static char *ini_file;
892199bd
JA
105
106static int shm_id;
107
18e0b78c
JA
108static cpu_set_t def_cpumask;
109
4240cfa1
JA
110enum {
111 DDIR_READ = 0,
112 DDIR_WRITE,
113};
892199bd
JA
114
115struct 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;
4240cfa1 122 volatile int terminate;
fc24389f 123 volatile int started;
f737299d
JA
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;
4240cfa1 130 unsigned int fsync_blocks;
fc24389f 131 unsigned int start_delay;
18e0b78c 132 cpu_set_t cpumask;
86184d14 133
7dd1389e 134 unsigned int rate;
4240cfa1
JA
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;
86184d14 141
892199bd
JA
142 unsigned long max_latency; /* msec */
143 unsigned long min_latency; /* msec */
144 unsigned long runtime; /* sec */
145 unsigned long blocks;
4240cfa1 146 unsigned long io_blocks;
892199bd
JA
147 unsigned long last_block;
148 sem_t mutex;
892199bd
JA
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;
4240cfa1
JA
157
158 struct timeval start;
892199bd
JA
159};
160
161static struct thread_data *threads;
892199bd
JA
162
163static sem_t startup_sem;
164
5c24b2c4 165static void sig_handler(int sig)
892199bd
JA
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
5c24b2c4 176static int init_random_state(struct thread_data *td)
892199bd
JA
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
7dd1389e 191 if (read(fd, &seed, sizeof(seed)) < (int) sizeof(seed)) {
892199bd
JA
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
5c24b2c4 204static void shutdown_stat_file(struct thread_data *td)
892199bd
JA
205{
206 if (td->stat_fd != -1) {
207 fsync(td->stat_fd);
208 close(td->stat_fd);
209 }
210}
211
5c24b2c4 212static int init_stat_file(struct thread_data *td)
892199bd 213{
4240cfa1 214 char n[256];
892199bd
JA
215
216 if (!write_stat)
217 return 0;
218
892199bd
JA
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) {
892199bd
JA
222 td->error = errno;
223 return 1;
224 }
225
892199bd
JA
226 return 0;
227}
228
5c24b2c4 229static unsigned long utime_since(struct timeval *s, struct timeval *e)
892199bd
JA
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
5c24b2c4 245static unsigned long mtime_since(struct timeval *s, struct timeval *e)
892199bd
JA
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
5c24b2c4 262static unsigned long get_next_offset(struct thread_data *td)
892199bd
JA
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
5c24b2c4 278static void add_stat_sample(struct thread_data *td, unsigned long msec)
892199bd
JA
279{
280 char sample[256];
281
282 if (!td->stat_fd)
283 return;
284
285#if 0
4240cfa1 286 sprintf(sample, "%lu, %lu\n", td->io_blocks, msec);
892199bd
JA
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);
4240cfa1 299 //sprintf(sample, "%lu, %lu\n", td->io_blocks, msec);
892199bd
JA
300 write(td->stat_fd, sample, strlen(sample));
301 }
302#endif
303}
304
5c24b2c4 305static void usec_sleep(int usec)
892199bd 306{
86184d14
JA
307 struct timespec req = { .tv_sec = 0, .tv_nsec = usec * 1000 };
308 struct timespec rem;
892199bd
JA
309
310 do {
86184d14
JA
311 rem.tv_sec = rem.tv_nsec = 0;
312 nanosleep(&req, &rem);
313 if (!rem.tv_nsec)
892199bd 314 break;
86184d14
JA
315
316 req.tv_nsec = rem.tv_nsec;
892199bd
JA
317 } while (1);
318}
319
5c24b2c4 320static void rate_throttle(struct thread_data *td, unsigned long time_spent)
86184d14 321{
4240cfa1
JA
322 if (!td->rate)
323 return;
324
86184d14
JA
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;
fad86e6a 329 if (td->rate_pending_usleep >= 100000) {
86184d14
JA
330 usec_sleep(td->rate_pending_usleep);
331 td->rate_pending_usleep = 0;
332 }
4240cfa1 333 } else {
42b2b9fe
JA
334 long overtime = time_spent - td->rate_usec_cycle;
335
4240cfa1
JA
336 td->rate_pending_usleep -= overtime;
337 }
338}
339
5c24b2c4 340static int check_min_rate(struct thread_data *td, struct timeval *now)
4240cfa1
JA
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 }
86184d14 364 }
4240cfa1
JA
365
366 td->rate_blocks = td->io_blocks;
367 memcpy(&td->lastrate, now, sizeof(*now));
368 return 0;
86184d14
JA
369}
370
4240cfa1
JA
371#define should_fsync(td) ((td)->ddir == DDIR_WRITE && !(td)->odirect)
372
5c24b2c4 373static void do_thread_io(struct thread_data *td)
892199bd 374{
4240cfa1 375 struct timeval s, e;
892199bd 376 char *buffer, *ptr;
86184d14 377 unsigned long blocks, msec, usec;
892199bd 378
4240cfa1
JA
379 ptr = malloc(td->bs + MASK);
380 buffer = ALIGN(ptr);
892199bd 381
4240cfa1
JA
382 gettimeofday(&td->start, NULL);
383
384 if (td->ratemin)
385 memcpy(&td->lastrate, &td->start, sizeof(td->start));
892199bd
JA
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)
86184d14 400 usec_sleep(td->delay_sleep);
892199bd
JA
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
f737299d 409 if (ret < (int) td->bs) {
892199bd
JA
410 if (ret == -1)
411 td->error = errno;
412 break;
413 }
414
4240cfa1
JA
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
86184d14
JA
421 gettimeofday(&e, NULL);
422
423 usec = utime_since(&s, &e);
86184d14 424
4240cfa1 425 rate_throttle(td, usec);
892199bd 426
4240cfa1
JA
427 if (check_min_rate(td, &e)) {
428 td->error = ENODATA;
429 break;
430 }
892199bd 431
4240cfa1
JA
432 msec = usec / 1000;
433 add_stat_sample(td, msec);
892199bd
JA
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
4240cfa1 441 if (should_fsync(td))
892199bd
JA
442 fsync(td->fd);
443
444 gettimeofday(&e, NULL);
4240cfa1 445 td->runtime = mtime_since(&td->start, &e);
892199bd
JA
446
447 free(ptr);
448}
449
5c24b2c4 450static void *thread_main(int shm_id, int offset, char *argv[])
892199bd
JA
451{
452 struct thread_data *td;
453 void *data;
4240cfa1 454 struct stat st;
892199bd
JA
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
18e0b78c
JA
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
892199bd
JA
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
4240cfa1 471 sprintf(argv[0], "fio%d", offset);
892199bd 472
7dd1389e 473 flags = 0;
892199bd
JA
474 if (td->odirect)
475 flags |= O_DIRECT;
476
7dd1389e
JA
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
892199bd
JA
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) {
4240cfa1 493 if (fstat(td->fd, &st) == -1) {
892199bd
JA
494 td->error = errno;
495 goto out;
496 }
497
4240cfa1 498 td->blocks = st.st_size / td->bs;
892199bd
JA
499 if (!td->blocks) {
500 td->error = EINVAL;
501 goto out;
502 }
503 } else
504 td->blocks = 1024 * 1024 * 1024 / td->bs;
505
f737299d 506 if (td->ioprio) {
892199bd
JA
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
518out:
892199bd
JA
519 shutdown_stat_file(td);
520err:
18e0b78c
JA
521 if (td->fd != -1)
522 close(td->fd);
892199bd
JA
523 if (ret)
524 sem_post(&startup_sem);
4240cfa1 525 shmdt(data);
892199bd
JA
526 return NULL;
527}
528
5c24b2c4 529static void free_shm(void)
892199bd
JA
530{
531 shmdt(threads);
532}
533
5c24b2c4 534static void show_thread_status(struct thread_data *td)
892199bd
JA
535{
536 int prio, prio_class;
537 unsigned long bw = 0;
538
539 if (td->runtime)
4240cfa1 540 bw = (td->io_blocks * td->bs) / td->runtime;
892199bd
JA
541
542 prio = td->ioprio & 0xff;
543 prio_class = td->ioprio >> IOPRIO_CLASS_SHIFT;
544
4240cfa1 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);
892199bd
JA
546}
547
5c24b2c4 548static int setup_rate(struct thread_data *td)
86184d14 549{
4240cfa1
JA
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 }
86184d14 559
4240cfa1 560 nr_reads_per_sec = td->rate * 1024 / td->bs;
86184d14
JA
561 td->rate_usec_cycle = 1000000 / nr_reads_per_sec;
562 td->rate_pending_usleep = 0;
4240cfa1 563 return 0;
86184d14
JA
564}
565
5c24b2c4 566static struct thread_data *get_new_job(void)
892199bd 567{
4240cfa1
JA
568 struct thread_data *td;
569
570 if (thread_number >= MAX_JOBS)
571 return NULL;
572
573 td = &threads[thread_number++];
fc24389f 574 memset(td, 0, sizeof(*td));
892199bd 575
86184d14 576 td->thread_number = thread_number;
f737299d
JA
577 td->ddir = DDIR_READ;
578 td->bs = global_bs;
579 td->odirect = 1;
580 td->delay_sleep = 0;
581 td->rate = 0;
4240cfa1 582 td->ratecycle = DEF_RATE_CYCLE;
f737299d
JA
583 td->sequential = sequential;
584 td->ioprio = 0;
585 memcpy(&td->cpumask, &def_cpumask, sizeof(td->cpumask));
586
587 return td;
588}
589
4240cfa1
JA
590static void put_job(struct thread_data *td)
591{
592 memset(&threads[td->thread_number - 1], 0, sizeof(*td));
593 thread_number--;
594}
595
5c24b2c4
JA
596static int add_job(struct thread_data *td, const char *filename, int prioclass,
597 int prio)
f737299d
JA
598{
599 strcpy(td->file_name, filename);
892199bd 600 td->stat_fd = -1;
4240cfa1 601 sem_init(&td->mutex, 1, 0);
892199bd 602 td->min_latency = 10000000;
f737299d
JA
603 td->ioprio = (prioclass << IOPRIO_CLASS_SHIFT) | prio;
604
4240cfa1
JA
605 if (setup_rate(td))
606 return -1;
f737299d
JA
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);
4240cfa1 609 return 0;
892199bd
JA
610}
611
18e0b78c
JA
612static void fill_cpu_mask(cpu_set_t cpumask, int cpu)
613{
f737299d 614 unsigned int i;
18e0b78c
JA
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
5c24b2c4 624static void fill_option(const char *input, char *output)
892199bd
JA
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 */
5c24b2c4 645static void parse_jobs_cmd(int argc, char *argv[], int index)
892199bd 646{
f737299d
JA
647 struct thread_data *td;
648 unsigned int prio, prioclass, cpu;
892199bd
JA
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;
4240cfa1 663
f737299d 664 td = get_new_job();
4240cfa1
JA
665 if (!td)
666 break;
f737299d 667
892199bd 668 prioclass = 2;
f737299d 669 prio = 4;
892199bd
JA
670
671 c = strstr(p, "rw=");
672 if (c) {
673 c += 3;
674 if (*c == '0')
f737299d 675 td->ddir = DDIR_READ;
892199bd 676 else
f737299d 677 td->ddir = DDIR_WRITE;
892199bd
JA
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);
f737299d
JA
702 td->bs = strtoul(string, NULL, 10);
703 td->bs <<= 10;
892199bd
JA
704 }
705
706 c = strstr(p, "direct=");
707 if (c) {
708 c += 7;
709 if (*c != '0')
f737299d 710 td->odirect = 1;
892199bd 711 else
f737299d 712 td->odirect = 0;
892199bd
JA
713 }
714
715 c = strstr(p, "delay=");
716 if (c) {
717 c += 6;
718 fill_option(c, string);
f737299d 719 td->delay_sleep = strtoul(string, NULL, 10);
892199bd
JA
720 }
721
86184d14
JA
722 c = strstr(p, "rate=");
723 if (c) {
724 c += 5;
725 fill_option(c, string);
f737299d 726 td->rate = strtoul(string, NULL, 10);
86184d14
JA
727 }
728
4240cfa1
JA
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
18e0b78c
JA
743 c = strstr(p, "cpumask=");
744 if (c) {
745 c += 8;
746 fill_option(c, string);
747 cpu = strtoul(string, NULL, 10);
f737299d 748 fill_cpu_mask(td->cpumask, cpu);
18e0b78c
JA
749 }
750
4240cfa1
JA
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 }
18e0b78c 757
fc24389f
JA
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
892199bd
JA
765 c = strstr(p, "random");
766 if (c)
f737299d 767 td->sequential = 0;
892199bd
JA
768 c = strstr(p, "sequential");
769 if (c)
f737299d 770 td->sequential = 1;
892199bd 771
4240cfa1
JA
772 if (add_job(td, filename, prioclass, prio))
773 put_job(td);
892199bd
JA
774 }
775
7dd1389e
JA
776 free(string);
777 free(filename);
892199bd
JA
778}
779
5c24b2c4 780static int check_int(char *p, char *name, unsigned int *val)
7dd1389e
JA
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
5c24b2c4 795static int is_empty(char *line)
7dd1389e
JA
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
5c24b2c4 806static int parse_jobs_ini(char *file)
7dd1389e 807{
4240cfa1 808 unsigned int prioclass, prio, cpu;
f737299d 809 struct thread_data *td;
7dd1389e
JA
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");
4240cfa1 818 return 1;
7dd1389e
JA
819 }
820
821 string = malloc(4096);
822 name = malloc(256);
823
7dd1389e
JA
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
f737299d 830 td = get_new_job();
4240cfa1
JA
831 if (!td)
832 break;
f737299d 833
7dd1389e 834 prioclass = 2;
f737299d 835 prio = 4;
7dd1389e
JA
836
837 fgetpos(f, &off);
838 while ((p = fgets(string, 4096, f)) != NULL) {
839 if (is_empty(p))
840 break;
f737299d
JA
841 if (!check_int(p, "bs", &td->bs)) {
842 td->bs <<= 10;
7dd1389e
JA
843 fgetpos(f, &off);
844 continue;
845 }
f737299d 846 if (!check_int(p, "rw", &td->ddir)) {
7dd1389e
JA
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 }
f737299d 858 if (!check_int(p, "direct", &td->odirect)) {
7dd1389e
JA
859 fgetpos(f, &off);
860 continue;
861 }
f737299d 862 if (!check_int(p, "rate", &td->rate)) {
7dd1389e
JA
863 fgetpos(f, &off);
864 continue;
865 }
4240cfa1
JA
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 }
f737299d 874 if (!check_int(p, "delay", &td->delay_sleep)) {
7dd1389e
JA
875 fgetpos(f, &off);
876 continue;
877 }
18e0b78c 878 if (!check_int(p, "cpumask", &cpu)) {
f737299d 879 fill_cpu_mask(td->cpumask, cpu);
18e0b78c
JA
880 fgetpos(f, &off);
881 continue;
882 }
4240cfa1
JA
883 if (!check_int(p, "fsync", &td->fsync_blocks)) {
884 fgetpos(f, &off);
885 continue;
886 }
fc24389f
JA
887 if (!check_int(p, "startdelay", &td->start_delay)) {
888 fgetpos(f, &off);
889 continue;
890 }
7dd1389e 891 if (!strcmp(p, "sequential")) {
f737299d 892 td->sequential = 1;
7dd1389e
JA
893 fgetpos(f, &off);
894 continue;
895 }
896 if (!strcmp(p, "random")) {
f737299d 897 td->sequential = 0;
7dd1389e
JA
898 fgetpos(f, &off);
899 continue;
900 }
901 }
902 fsetpos(f, &off);
903
4240cfa1
JA
904 if (add_job(td, name, prioclass, prio))
905 put_job(td);
7dd1389e
JA
906 }
907
908 free(string);
909 free(name);
4240cfa1 910 return 0;
7dd1389e
JA
911}
912
5c24b2c4 913static int parse_options(int argc, char *argv[])
892199bd 914{
01c4d8de 915 int i;
892199bd
JA
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;
4240cfa1
JA
933 if (!global_bs) {
934 printf("bad block size\n");
935 global_bs = DEF_BS;
936 }
892199bd
JA
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;
892199bd
JA
950 case 'o':
951 parm++;
952 odirect = !!atoi(parm);
953 break;
7dd1389e
JA
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;
892199bd 961 default:
7dd1389e 962 printf("bad option %s\n", argv[i]);
892199bd
JA
963 break;
964 }
965 }
966
892199bd
JA
967 return i;
968}
969
fc24389f
JA
970static 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
892199bd
JA
1030int 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;
4240cfa1 1035 int i;
18e0b78c 1036
4240cfa1 1037 shm_id = shmget(0, MAX_JOBS * sizeof(struct thread_data), IPC_CREAT | 0600);
892199bd
JA
1038 if (shm_id == -1) {
1039 perror("shmget");
1040 return 1;
1041 }
1042
1043 threads = shmat(shm_id, NULL, 0);
86184d14
JA
1044 if (threads == (void *) -1 ) {
1045 perror("shmat");
1046 return 1;
1047 }
892199bd
JA
1048
1049 atexit(free_shm);
1050
4240cfa1
JA
1051 if (sched_getaffinity(getpid(), sizeof(def_cpumask), &def_cpumask) == -1) {
1052 perror("sched_getaffinity");
1053 return 1;
1054 }
1055
892199bd 1056 i = parse_options(argc, argv);
7dd1389e 1057
4240cfa1
JA
1058 if (ini_file) {
1059 if (parse_jobs_ini(ini_file))
1060 return 1;
1061 } else
1062 parse_jobs_cmd(argc, argv, i);
7dd1389e 1063
4240cfa1
JA
1064 if (!thread_number) {
1065 printf("Nothing to do\n");
1066 return 1;
1067 }
7dd1389e
JA
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
fc24389f 1071 run_threads(argv);
892199bd
JA
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)
7dd1389e 1087 goto show_stat;
892199bd
JA
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)
4240cfa1 1095 bw = (td->io_blocks * td->bs) / td->runtime;
892199bd
JA
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
4240cfa1 1105 total_blocks[td->ddir] += td->io_blocks;
892199bd
JA
1106
1107 if (td->ddir == DDIR_READ) {
4240cfa1 1108 read_mb += (td->bs * td->io_blocks) >> 20;
892199bd 1109 if (td->runtime)
4240cfa1 1110 read_agg += (td->io_blocks * td->bs) / td->runtime;
892199bd
JA
1111 }
1112 if (td->ddir == DDIR_WRITE) {
4240cfa1 1113 write_mb += (td->bs * td->io_blocks) >> 20;
892199bd 1114 if (td->runtime)
4240cfa1 1115 write_agg += (td->io_blocks * td->bs) / td->runtime;
892199bd
JA
1116 }
1117
7dd1389e 1118show_stat:
892199bd
JA
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]);
fc24389f 1127
892199bd
JA
1128 return 0;
1129}