[PATCH] fio: various fixes for using a bdev directly
[disktools.git] / fio-ini.c
CommitLineData
27c32a38
JA
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <fcntl.h>
5#include <ctype.h>
6#include <string.h>
7#include <errno.h>
8#include <sys/ipc.h>
9#include <sys/shm.h>
10
11#include "fio.h"
12
13#define DEF_BS (4096)
14#define DEF_TIMEOUT (0)
15#define DEF_RATE_CYCLE (1000)
16#define DEF_ODIRECT (1)
17#define DEF_SEQUENTIAL (1)
18#define DEF_RAND_REPEAT (1)
19#define DEF_OVERWRITE (0)
20#define DEF_CREATE (1)
21#define DEF_INVALIDATE (1)
22#define DEF_SYNCIO (0)
23#define DEF_RANDSEED (0xb1899bedUL)
24#define DEF_BWAVGTIME (500)
25#define DEF_CREATE_SER (1)
26#define DEF_CREATE_FSYNC (1)
27#define DEF_LOOPS (1)
28#define DEF_VERIFY (0)
29#define DEF_STONEWALL (0)
30#define DEF_NUMJOBS (1)
189873de 31#define DEF_USE_THREAD (0)
27c32a38
JA
32
33static int repeatable = DEF_RAND_REPEAT;
34static char *ini_file;
35static int max_jobs = MAX_JOBS;
36
37struct thread_data def_thread;
38struct thread_data *threads = NULL;
39
40int rate_quit = 0;
41int write_lat_log = 0;
42int write_bw_log = 0;
43int exitall_on_terminate = 0;
44
45static int setup_rate(struct thread_data *td)
46{
47 int nr_reads_per_sec;
48
49 if (!td->rate)
50 return 0;
51
52 if (td->rate < td->ratemin) {
53 fprintf(stderr, "min rate larger than nominal rate\n");
54 return -1;
55 }
56
57 nr_reads_per_sec = (td->rate * 1024) / td->min_bs;
58 td->rate_usec_cycle = 1000000 / nr_reads_per_sec;
59 td->rate_pending_usleep = 0;
60 return 0;
61}
62
63static void setup_log(struct io_log **log)
64{
65 struct io_log *l = malloc(sizeof(*l));
66
67 l->nr_samples = 0;
68 l->max_samples = 1024;
69 l->log = malloc(l->max_samples * sizeof(struct io_sample));
70 *log = l;
71}
72
73void finish_log(struct thread_data *td, struct io_log *log, const char *name)
74{
75 char file_name[128];
76 FILE *f;
77 unsigned int i;
78
79 sprintf(file_name, "client%d_%s.log", td->thread_number, name);
80 f = fopen(file_name, "w");
81 if (!f) {
82 perror("fopen log");
83 return;
84 }
85
86 for (i = 0; i < log->nr_samples; i++)
87 fprintf(f, "%lu, %lu\n", log->log[i].time, log->log[i].val);
88
89 fclose(f);
90 free(log->log);
91 free(log);
92}
93
94static struct thread_data *get_new_job(int global, struct thread_data *parent)
95{
96 struct thread_data *td;
97
98 if (global)
99 return &def_thread;
100 if (thread_number >= max_jobs)
101 return NULL;
102
103 td = &threads[thread_number++];
104 memset(td, 0, sizeof(*td));
105
106 sprintf(td->directory, ".");
107
108 td->fd = -1;
109 td->thread_number = thread_number;
110
111 td->ddir = parent->ddir;
112 td->ioprio = parent->ioprio;
113 td->sequential = parent->sequential;
114 td->bs = parent->bs;
115 td->min_bs = parent->min_bs;
116 td->max_bs = parent->max_bs;
117 td->odirect = parent->odirect;
118 td->thinktime = parent->thinktime;
119 td->fsync_blocks = parent->fsync_blocks;
120 td->start_delay = parent->start_delay;
121 td->timeout = parent->timeout;
122 td->use_aio = parent->use_aio;
123 td->create_file = parent->create_file;
124 td->overwrite = parent->overwrite;
125 td->invalidate_cache = parent->invalidate_cache;
126 td->file_size = parent->file_size;
127 td->file_offset = parent->file_offset;
128 td->rate = parent->rate;
129 td->ratemin = parent->ratemin;
130 td->ratecycle = parent->ratecycle;
131 td->aio_depth = parent->aio_depth;
132 td->sync_io = parent->sync_io;
133 td->mem_type = parent->mem_type;
134 td->bw_avg_time = parent->bw_avg_time;
135 td->create_serialize = parent->create_serialize;
136 td->create_fsync = parent->create_fsync;
137 td->loops = parent->loops;
138 td->verify = parent->verify;
139 td->stonewall = parent->stonewall;
140 td->numjobs = parent->numjobs;
189873de 141 td->use_thread = parent->use_thread;
27c32a38
JA
142 memcpy(&td->cpumask, &parent->cpumask, sizeof(td->cpumask));
143
144 return td;
145}
146
147static void put_job(struct thread_data *td)
148{
149 memset(&threads[td->thread_number - 1], 0, sizeof(*td));
150 thread_number--;
151}
152
153static int add_job(struct thread_data *td, const char *jobname, int prioclass,
154 int prio)
155{
c4c8f7b3 156 struct stat sb;
27c32a38
JA
157 int numjobs;
158
159 if (td == &def_thread)
160 return 0;
161
c4c8f7b3
JA
162 td->filetype = FIO_TYPE_FILE;
163 if (!stat(jobname, &sb) && S_ISBLK(sb.st_mode))
164 td->filetype = FIO_TYPE_BD;
165
166 if (td->filetype == FIO_TYPE_FILE)
167 sprintf(td->file_name, "%s/%s.%d", td->directory, jobname, td->thread_number);
168 else
169 strcpy(td->file_name, jobname);
170
27c32a38
JA
171 sem_init(&td->mutex, 1, 0);
172 td->ioprio = (prioclass << IOPRIO_CLASS_SHIFT) | prio;
173
174 td->clat_stat.min_val = ULONG_MAX;
175 td->slat_stat.min_val = ULONG_MAX;
176 td->bw_stat.min_val = ULONG_MAX;
177
178 run_str[td->thread_number - 1] = 'P';
179
180 if (td->use_aio && !td->aio_depth)
181 td->aio_depth = 1;
182
183 if (td->min_bs == -1U)
184 td->min_bs = td->bs;
185 if (td->max_bs == -1U)
186 td->max_bs = td->bs;
187 if (td_read(td))
188 td->verify = 0;
189
190 if (td->stonewall && td->thread_number > 1)
191 groupid++;
192
193 td->groupid = groupid;
194
195 if (setup_rate(td))
196 goto err;
197
198 if (write_lat_log)
199 setup_log(&td->lat_log);
200 if (write_bw_log)
201 setup_log(&td->bw_log);
202
203 printf("Client%d (g=%d): rw=%d, prio=%d/%d, seq=%d, odir=%d, bs=%d-%d, rate=%d, aio=%d, aio_depth=%d\n", td->thread_number, td->groupid, td->ddir, prioclass, prio, td->sequential, td->odirect, td->min_bs, td->max_bs, td->rate, td->use_aio, td->aio_depth);
204
205 /*
206 * recurse add identical jobs, clear numjobs and stonewall options
207 * as they don't apply to sub-jobs
208 */
209 numjobs = td->numjobs;
210 while (--numjobs) {
211 struct thread_data *td_new = get_new_job(0, td);
212
213 if (!td_new)
214 goto err;
215
216 td_new->numjobs = 1;
217 td_new->stonewall = 0;
218
219 if (add_job(td_new, jobname, prioclass, prio))
220 goto err;
221 }
222 return 0;
223err:
224 put_job(td);
225 return -1;
226}
227
228int init_random_state(struct thread_data *td)
229{
230 unsigned long seed;
231 int fd, num_maps, blocks;
232
233 fd = open("/dev/random", O_RDONLY);
234 if (fd == -1) {
235 td->error = errno;
236 return 1;
237 }
238
239 if (read(fd, &seed, sizeof(seed)) < (int) sizeof(seed)) {
240 td->error = EIO;
241 close(fd);
242 return 1;
243 }
244
245 close(fd);
246
247 srand48_r(seed, &td->bsrange_state);
248 srand48_r(seed, &td->verify_state);
249
250 if (td->sequential)
251 return 0;
252
253 if (repeatable)
254 seed = DEF_RANDSEED;
255
256 blocks = (td->io_size + td->min_bs - 1) / td->min_bs;
257 num_maps = blocks / BLOCKS_PER_MAP;
258 td->file_map = malloc(num_maps * sizeof(long));
259 td->num_maps = num_maps;
260 memset(td->file_map, 0, num_maps * sizeof(long));
261
262 srand48_r(seed, &td->random_state);
263 return 0;
264}
265
266static void fill_cpu_mask(cpu_set_t cpumask, int cpu)
267{
268 unsigned int i;
269
270 CPU_ZERO(&cpumask);
271
272 for (i = 0; i < sizeof(int) * 8; i++) {
273 if ((1 << i) & cpu)
274 CPU_SET(i, &cpumask);
275 }
276}
277
278static unsigned long get_mult(char c)
279{
280 switch (c) {
281 case 'k':
282 case 'K':
283 return 1024;
284 case 'm':
285 case 'M':
286 return 1024 * 1024;
287 case 'g':
288 case 'G':
289 return 1024 * 1024 * 1024;
290 default:
291 return 1;
292 }
293}
294
295/*
296 * convert string after '=' into decimal value, noting any size suffix
297 */
298static int str_cnv(char *p, unsigned long long *val)
299{
300 char *str;
301 int len;
302
303 str = strstr(p, "=");
304 if (!str)
305 return 1;
306
307 str++;
308 len = strlen(str);
309
310 *val = strtoul(str, NULL, 10);
311 if (*val == ULONG_MAX && errno == ERANGE)
312 return 1;
313
314 *val *= get_mult(str[len - 2]);
315 return 0;
316}
317
318static int check_strcnv(char *p, char *name, unsigned long long *val)
319{
320 if (!strstr(p, name))
321 return 1;
322
323 return str_cnv(p, val);
324}
325
326static int check_str(char *p, char *name, char *option)
327{
328 char *s = strstr(p, name);
329
330 if (!s)
331 return 1;
332
333 s += strlen(name);
334 if (strstr(s, option))
335 return 0;
336
337 return 1;
338}
339
340static int check_strstore(char *p, char *name, char *dest)
341{
342 char *s = strstr(p, name);
343
344 if (!s)
345 return 1;
346
347 s = strstr(p, "=");
348 if (!s)
349 return 1;
350
351 s++;
352 while (isblank(*s))
353 s++;
354
355 strcpy(dest, s);
356
357 s = dest + strlen(dest) - 1;
358 while (isblank(*s)) {
359 *s = '\0';
360 s--;
361 }
362
363 return 0;
364}
365
366static int check_range(char *p, char *name, unsigned long *s, unsigned long *e)
367{
368 char str[128];
369 char s1, s2;
370
371 sprintf(str, "%s=%%lu%%c-%%lu%%c", name);
372 if (sscanf(p, str, s, &s1, e, &s2) == 4) {
373 *s *= get_mult(s1);
374 *e *= get_mult(s2);
375 return 0;
376 }
377
378 sprintf(str, "%s = %%lu%%c-%%lu%%c", name);
379 if (sscanf(p, str, s, &s1, e, &s2) == 4) {
380 *s *= get_mult(s1);
381 *e *= get_mult(s2);
382 return 0;
383 }
384
385 sprintf(str, "%s=%%lu-%%lu", name);
386 if (sscanf(p, str, s, e) == 2)
387 return 0;
388
389 sprintf(str, "%s = %%lu-%%lu", name);
390 if (sscanf(p, str, s, e) == 2)
391 return 0;
392
393 return 1;
394
395}
396
397static int check_int(char *p, char *name, unsigned int *val)
398{
399 char str[128];
400
401 sprintf(str, "%s=%%d", name);
402 if (sscanf(p, str, val) == 1)
403 return 0;
404
405 sprintf(str, "%s = %%d", name);
406 if (sscanf(p, str, val) == 1)
407 return 0;
408
409 return 1;
410}
411
1d2123e0
JA
412static int check_strset(char *p, char *name)
413{
5812ff31 414 return strncmp(p, name, strlen(name));
1d2123e0
JA
415}
416
27c32a38
JA
417static int is_empty_or_comment(char *line)
418{
419 unsigned int i;
420
421 for (i = 0; i < strlen(line); i++) {
422 if (line[i] == ';')
423 return 1;
424 if (!isspace(line[i]) && !iscntrl(line[i]))
425 return 0;
426 }
427
428 return 1;
429}
430
431int parse_jobs_ini(char *file)
432{
433 unsigned int prioclass, prio, cpu, global;
434 unsigned long long ull;
435 unsigned long ul1, ul2;
436 struct thread_data *td;
437 char *string, *name;
438 fpos_t off;
439 FILE *f;
440 char *p;
441
442 f = fopen(file, "r");
443 if (!f) {
444 perror("fopen");
445 return 1;
446 }
447
448 string = malloc(4096);
449 name = malloc(256);
450
451 while ((p = fgets(string, 4096, f)) != NULL) {
452 if (is_empty_or_comment(p))
453 continue;
454 if (sscanf(p, "[%s]", name) != 1)
455 continue;
456
457 global = !strncmp(name, "global", 6);
458
459 name[strlen(name) - 1] = '\0';
460
461 td = get_new_job(global, &def_thread);
462 if (!td)
463 return 1;
464
465 prioclass = 2;
466 prio = 4;
467
468 fgetpos(f, &off);
469 while ((p = fgets(string, 4096, f)) != NULL) {
470 if (is_empty_or_comment(p))
471 continue;
472 if (strstr(p, "["))
473 break;
474 if (!check_int(p, "rw", &td->ddir)) {
475 fgetpos(f, &off);
476 continue;
477 }
478 if (!check_int(p, "prio", &prio)) {
479 fgetpos(f, &off);
480 continue;
481 }
482 if (!check_int(p, "prioclass", &prioclass)) {
483 fgetpos(f, &off);
484 continue;
485 }
486 if (!check_int(p, "direct", &td->odirect)) {
487 fgetpos(f, &off);
488 continue;
489 }
490 if (!check_int(p, "rate", &td->rate)) {
491 fgetpos(f, &off);
492 continue;
493 }
494 if (!check_int(p, "ratemin", &td->ratemin)) {
495 fgetpos(f, &off);
496 continue;
497 }
498 if (!check_int(p, "ratecycle", &td->ratecycle)) {
499 fgetpos(f, &off);
500 continue;
501 }
502 if (!check_int(p, "thinktime", &td->thinktime)) {
503 fgetpos(f, &off);
504 continue;
505 }
506 if (!check_int(p, "cpumask", &cpu)) {
507 fill_cpu_mask(td->cpumask, cpu);
508 fgetpos(f, &off);
509 continue;
510 }
511 if (!check_int(p, "fsync", &td->fsync_blocks)) {
512 fgetpos(f, &off);
513 continue;
514 }
515 if (!check_int(p, "startdelay", &td->start_delay)) {
516 fgetpos(f, &off);
517 continue;
518 }
519 if (!check_int(p, "timeout", &td->timeout)) {
520 fgetpos(f, &off);
521 continue;
522 }
523 if (!check_int(p, "invalidate",&td->invalidate_cache)) {
524 fgetpos(f, &off);
525 continue;
526 }
527 if (!check_int(p, "aio_depth", &td->aio_depth)) {
528 fgetpos(f, &off);
529 continue;
530 }
531 if (!check_int(p, "sync", &td->sync_io)) {
532 fgetpos(f, &off);
533 continue;
534 }
535 if (!check_int(p, "bwavgtime", &td->bw_avg_time)) {
536 fgetpos(f, &off);
537 continue;
538 }
539 if (!check_int(p, "create_serialize", &td->create_serialize)) {
540 fgetpos(f, &off);
541 continue;
542 }
543 if (!check_int(p, "create_fsync", &td->create_fsync)) {
544 fgetpos(f, &off);
545 continue;
546 }
547 if (!check_int(p, "loops", &td->loops)) {
548 fgetpos(f, &off);
549 continue;
550 }
551 if (!check_int(p, "verify", &td->verify)) {
552 fgetpos(f, &off);
553 continue;
554 }
555 if (!check_int(p, "numjobs", &td->numjobs)) {
556 fgetpos(f, &off);
557 continue;
558 }
559 if (!check_range(p, "bsrange", &ul1, &ul2)) {
560 if (ul1 & 511)
561 printf("bad min block size, must be a multiple of 512\n");
562 else
563 td->min_bs = ul1;
564 if (ul2 & 511)
565 printf("bad max block size, must be a multiple of 512\n");
566 else
567 td->max_bs = ul2;
568 fgetpos(f, &off);
569 continue;
570 }
571 if (!check_strcnv(p, "bs", &ull)) {
572 if (ull & 511)
573 printf("bad block size, must be a multiple of 512\n");
574 else
575 td->bs = ull;
576 fgetpos(f, &off);
577 continue;
578 }
579 if (!check_strcnv(p, "size", &td->file_size)) {
580 fgetpos(f, &off);
581 continue;
582 }
583 if (!check_strcnv(p, "offset", &td->file_offset)) {
584 fgetpos(f, &off);
585 continue;
586 }
587 if (!check_strstore(p, "directory", td->directory)) {
588 fgetpos(f, &off);
589 continue;
590 }
591 if (!check_str(p, "mem", "malloc")) {
592 td->mem_type = MEM_MALLOC;
593 fgetpos(f, &off);
594 continue;
595 }
596 if (!check_str(p, "mem", "shm")) {
597 td->mem_type = MEM_SHM;
598 fgetpos(f, &off);
599 continue;
600 }
1d2123e0 601 if (!check_strset(p, "sequential")) {
27c32a38
JA
602 td->sequential = 1;
603 fgetpos(f, &off);
604 continue;
605 }
1d2123e0 606 if (!check_strset(p, "random")) {
27c32a38
JA
607 td->sequential = 0;
608 fgetpos(f, &off);
609 continue;
610 }
1d2123e0 611 if (!check_strset(p, "aio")) {
27c32a38
JA
612 td->use_aio = 1;
613 fgetpos(f, &off);
614 continue;
615 }
1d2123e0 616 if (!check_strset(p, "create")) {
27c32a38
JA
617 td->create_file = 1;
618 fgetpos(f, &off);
619 continue;
620 }
1d2123e0 621 if (!check_strset(p, "overwrite")) {
27c32a38
JA
622 td->overwrite = 1;
623 fgetpos(f, &off);
624 continue;
625 }
1d2123e0 626 if (!check_strset(p, "exitall")) {
27c32a38
JA
627 exitall_on_terminate = 1;
628 fgetpos(f, &off);
629 continue;
630 }
1d2123e0 631 if (!check_strset(p, "stonewall")) {
27c32a38
JA
632 td->stonewall = 1;
633 fgetpos(f, &off);
634 continue;
635 }
1d2123e0 636 if (!check_strset(p, "thread")) {
189873de
JA
637 td->use_thread = 1;
638 fgetpos(f, &off);
639 continue;
640 }
641
27c32a38
JA
642 printf("Client%d: bad option %s\n",td->thread_number,p);
643 }
644 fsetpos(f, &off);
645
646 if (add_job(td, name, prioclass, prio))
647 return 1;
648 }
649
650 free(string);
651 free(name);
652 fclose(f);
653 return 0;
654}
655
656static int fill_def_thread(void)
657{
658 memset(&def_thread, 0, sizeof(def_thread));
659
660 if (sched_getaffinity(getpid(), sizeof(cpu_set_t), &def_thread.cpumask) == -1) {
661 perror("sched_getaffinity");
662 return 1;
663 }
664
665 /*
666 * fill globals
667 */
668 def_thread.ddir = DDIR_READ;
669 def_thread.bs = DEF_BS;
670 def_thread.min_bs = -1;
671 def_thread.max_bs = -1;
672 def_thread.odirect = DEF_ODIRECT;
673 def_thread.ratecycle = DEF_RATE_CYCLE;
674 def_thread.sequential = DEF_SEQUENTIAL;
675 def_thread.timeout = DEF_TIMEOUT;
676 def_thread.create_file = DEF_CREATE;
677 def_thread.overwrite = DEF_OVERWRITE;
678 def_thread.invalidate_cache = DEF_INVALIDATE;
679 def_thread.sync_io = DEF_SYNCIO;
680 def_thread.mem_type = MEM_MALLOC;
681 def_thread.bw_avg_time = DEF_BWAVGTIME;
682 def_thread.create_serialize = DEF_CREATE_SER;
683 def_thread.create_fsync = DEF_CREATE_FSYNC;
684 def_thread.loops = DEF_LOOPS;
685 def_thread.verify = DEF_VERIFY;
686 def_thread.stonewall = DEF_STONEWALL;
687 def_thread.numjobs = DEF_NUMJOBS;
189873de 688 def_thread.use_thread = DEF_USE_THREAD;
27c32a38
JA
689
690 return 0;
691}
692
693static void parse_cmd_line(int argc, char *argv[])
694{
695 int i;
696
697 for (i = 1; i < argc; i++) {
698 char *parm = argv[i];
699
700 if (parm[0] != '-')
701 break;
702
703 parm++;
704 switch (*parm) {
705 case 's':
706 parm++;
707 def_thread.sequential = !!atoi(parm);
708 break;
709 case 'b':
710 parm++;
711 def_thread.bs = atoi(parm);
712 def_thread.bs <<= 10;
713 if (!def_thread.bs) {
714 printf("bad block size\n");
715 def_thread.bs = DEF_BS;
716 }
717 break;
718 case 't':
719 parm++;
720 def_thread.timeout = atoi(parm);
721 break;
722 case 'r':
723 parm++;
724 repeatable = !!atoi(parm);
725 break;
726 case 'R':
727 parm++;
728 rate_quit = !!atoi(parm);
729 break;
730 case 'o':
731 parm++;
732 def_thread.odirect = !!atoi(parm);
733 break;
734 case 'f':
735 if (i + 1 >= argc) {
736 printf("-f needs file as arg\n");
737 break;
738 }
739 ini_file = strdup(argv[i+1]);
740 i++;
741 break;
742 case 'l':
743 write_lat_log = 1;
744 break;
745 case 'w':
746 write_bw_log = 1;
747 break;
748 default:
749 printf("bad option %s\n", argv[i]);
750 break;
751 }
752 }
753}
754
755static void free_shm(void)
756{
757 struct shmid_ds sbuf;
758
759 if (threads) {
760 shmdt(threads);
761 threads = NULL;
762 shmctl(shm_id, IPC_RMID, &sbuf);
763 }
764}
765
766static int setup_thread_area(void)
767{
768 /*
769 * 1024 is too much on some machines, scale max_jobs if
770 * we get a failure that looks like too large a shm segment
771 */
772 do {
773 int s = max_jobs * sizeof(struct thread_data);
774
775 shm_id = shmget(0, s, IPC_CREAT | 0600);
776 if (shm_id != -1)
777 break;
778 if (errno != EINVAL) {
779 perror("shmget");
780 break;
781 }
782
783 max_jobs >>= 1;
784 } while (max_jobs);
785
786 if (shm_id == -1)
787 return 1;
788
789 threads = shmat(shm_id, NULL, 0);
790 if (threads == (void *) -1) {
791 perror("shmat");
792 return 1;
793 }
794
795 atexit(free_shm);
796 return 0;
797}
798
799int parse_options(int argc, char *argv[])
800{
801 if (setup_thread_area())
802 return 1;
803 if (fill_def_thread())
804 return 1;
805
806 parse_cmd_line(argc, argv);
807
808 if (!ini_file) {
809 printf("Need job file\n");
810 return 1;
811 }
812
813 if (parse_jobs_ini(ini_file))
814 return 1;
815
816 return 0;
817}