Fio 1.17.2
[fio.git] / options.c
... / ...
CommitLineData
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <ctype.h>
5#include <string.h>
6#include <getopt.h>
7#include <assert.h>
8
9#include "fio.h"
10#include "parse.h"
11#include "fls.h"
12
13#define td_var_offset(var) ((size_t) &((struct thread_options *)0)->var)
14
15/*
16 * Check if mmap/mmaphuge has a :/foo/bar/file at the end. If so, return that.
17 */
18static char *get_opt_postfix(const char *str)
19{
20 char *p = strstr(str, ":");
21
22 if (!p)
23 return NULL;
24
25 p++;
26 strip_blank_front(&p);
27 strip_blank_end(p);
28 return strdup(p);
29}
30
31static int str_rw_cb(void *data, const char *str)
32{
33 struct thread_data *td = data;
34 char *nr = get_opt_postfix(str);
35
36 td->o.ddir_nr = 1;
37 if (nr)
38 td->o.ddir_nr = atoi(nr);
39
40 return 0;
41}
42
43static int str_mem_cb(void *data, const char *mem)
44{
45 struct thread_data *td = data;
46
47 if (td->o.mem_type == MEM_MMAPHUGE || td->o.mem_type == MEM_MMAP) {
48 td->mmapfile = get_opt_postfix(mem);
49 if (td->o.mem_type == MEM_MMAPHUGE && !td->mmapfile) {
50 log_err("fio: mmaphuge:/path/to/file\n");
51 return 1;
52 }
53 }
54
55 return 0;
56}
57
58static int str_lockmem_cb(void fio_unused *data, unsigned long *val)
59{
60 mlock_size = *val;
61 return 0;
62}
63
64#ifdef FIO_HAVE_IOPRIO
65static int str_prioclass_cb(void *data, unsigned int *val)
66{
67 struct thread_data *td = data;
68 unsigned short mask;
69
70 /*
71 * mask off old class bits, str_prio_cb() may have set a default class
72 */
73 mask = (1 << IOPRIO_CLASS_SHIFT) - 1;
74 td->ioprio &= mask;
75
76 td->ioprio |= *val << IOPRIO_CLASS_SHIFT;
77 return 0;
78}
79
80static int str_prio_cb(void *data, unsigned int *val)
81{
82 struct thread_data *td = data;
83
84 td->ioprio |= *val;
85
86 /*
87 * If no class is set, assume BE
88 */
89 if ((td->ioprio >> IOPRIO_CLASS_SHIFT) == 0)
90 td->ioprio |= IOPRIO_CLASS_BE << IOPRIO_CLASS_SHIFT;
91
92 return 0;
93}
94#endif
95
96static int str_exitall_cb(void)
97{
98 exitall_on_terminate = 1;
99 return 0;
100}
101
102#ifdef FIO_HAVE_CPU_AFFINITY
103static int str_cpumask_cb(void *data, unsigned int *val)
104{
105 struct thread_data *td = data;
106 unsigned int i;
107
108 CPU_ZERO(&td->o.cpumask);
109
110 for (i = 0; i < sizeof(int) * 8; i++)
111 if ((1 << i) & *val)
112 CPU_SET(*val, &td->o.cpumask);
113
114 td->o.cpumask_set = 1;
115 return 0;
116}
117
118static int str_cpus_allowed_cb(void *data, const char *input)
119{
120 struct thread_data *td = data;
121 char *cpu, *str, *p;
122
123 CPU_ZERO(&td->o.cpumask);
124
125 p = str = strdup(input);
126
127 strip_blank_front(&str);
128 strip_blank_end(str);
129
130 while ((cpu = strsep(&str, ",")) != NULL) {
131 if (!strlen(cpu))
132 break;
133 CPU_SET(atoi(cpu), &td->o.cpumask);
134 }
135
136 free(p);
137 td->o.cpumask_set = 1;
138 exit(0);
139 return 0;
140}
141#endif
142
143static int str_fst_cb(void *data, const char *str)
144{
145 struct thread_data *td = data;
146 char *nr = get_opt_postfix(str);
147
148 td->file_service_nr = 1;
149 if (nr)
150 td->file_service_nr = atoi(nr);
151
152 return 0;
153}
154
155static int str_filename_cb(void *data, const char *input)
156{
157 struct thread_data *td = data;
158 char *fname, *str, *p;
159
160 p = str = strdup(input);
161
162 strip_blank_front(&str);
163 strip_blank_end(str);
164
165 if (!td->files_index)
166 td->o.nr_files = 0;
167
168 while ((fname = strsep(&str, ":")) != NULL) {
169 if (!strlen(fname))
170 break;
171 add_file(td, fname);
172 td->o.nr_files++;
173 }
174
175 free(p);
176 return 0;
177}
178
179static int str_directory_cb(void *data, const char fio_unused *str)
180{
181 struct thread_data *td = data;
182 struct stat sb;
183
184 if (lstat(td->o.directory, &sb) < 0) {
185 log_err("fio: %s is not a directory\n", td->o.directory);
186 td_verror(td, errno, "lstat");
187 return 1;
188 }
189 if (!S_ISDIR(sb.st_mode)) {
190 log_err("fio: %s is not a directory\n", td->o.directory);
191 return 1;
192 }
193
194 return 0;
195}
196
197static int str_opendir_cb(void *data, const char fio_unused *str)
198{
199 struct thread_data *td = data;
200
201 if (!td->files_index)
202 td->o.nr_files = 0;
203
204 return add_dir_files(td, td->o.opendir);
205}
206
207static int str_verify_offset_cb(void *data, unsigned int *off)
208{
209 struct thread_data *td = data;
210
211 if (*off && *off < sizeof(struct verify_header)) {
212 log_err("fio: verify_offset too small\n");
213 return 1;
214 }
215
216 td->o.verify_offset = *off;
217 return 0;
218}
219
220static int str_verify_cb(void *data, const char *mem)
221{
222 struct thread_data *td = data;
223 unsigned int nr, msb;
224 char *pat;
225
226 if (td->o.verify != VERIFY_PATTERN)
227 return 0;
228
229 pat = get_opt_postfix(mem);
230 if (!pat) {
231 log_err("fio: missing pattern\n");
232 return 1;
233 }
234
235 if (strstr(pat, "0x") || strstr(pat, "0X"))
236 nr = strtol(pat, NULL, 16);
237 else
238 nr = strtol(pat, NULL, 16);
239
240 msb = fls(nr);
241 if (msb <= 8)
242 td->o.verify_pattern_bytes = 1;
243 else if (msb <= 16)
244 td->o.verify_pattern_bytes = 2;
245 else if (msb <= 24)
246 td->o.verify_pattern_bytes = 3;
247 else
248 td->o.verify_pattern_bytes = 4;
249
250 td->o.verify_pattern = nr;
251 return 0;
252}
253
254#define __stringify_1(x) #x
255#define __stringify(x) __stringify_1(x)
256
257/*
258 * Map of job/command line options
259 */
260static struct fio_option options[] = {
261 {
262 .name = "description",
263 .type = FIO_OPT_STR_STORE,
264 .off1 = td_var_offset(description),
265 .help = "Text job description",
266 },
267 {
268 .name = "name",
269 .type = FIO_OPT_STR_STORE,
270 .off1 = td_var_offset(name),
271 .help = "Name of this job",
272 },
273 {
274 .name = "directory",
275 .type = FIO_OPT_STR_STORE,
276 .off1 = td_var_offset(directory),
277 .cb = str_directory_cb,
278 .help = "Directory to store files in",
279 },
280 {
281 .name = "filename",
282 .type = FIO_OPT_STR_STORE,
283 .off1 = td_var_offset(filename),
284 .cb = str_filename_cb,
285 .help = "File(s) to use for the workload",
286 },
287 {
288 .name = "opendir",
289 .type = FIO_OPT_STR_STORE,
290 .off1 = td_var_offset(opendir),
291 .cb = str_opendir_cb,
292 .help = "Recursively add files from this directory and down",
293 },
294 {
295 .name = "rw",
296 .alias = "readwrite",
297 .type = FIO_OPT_STR,
298 .cb = str_rw_cb,
299 .off1 = td_var_offset(td_ddir),
300 .help = "IO direction",
301 .def = "read",
302 .posval = {
303 { .ival = "read",
304 .oval = TD_DDIR_READ,
305 .help = "Sequential read",
306 },
307 { .ival = "write",
308 .oval = TD_DDIR_WRITE,
309 .help = "Sequential write",
310 },
311 { .ival = "randread",
312 .oval = TD_DDIR_RANDREAD,
313 .help = "Random read",
314 },
315 { .ival = "randwrite",
316 .oval = TD_DDIR_RANDWRITE,
317 .help = "Random write",
318 },
319 { .ival = "rw",
320 .oval = TD_DDIR_RW,
321 .help = "Sequential read and write mix",
322 },
323 { .ival = "randrw",
324 .oval = TD_DDIR_RANDRW,
325 .help = "Random read and write mix"
326 },
327 },
328 },
329 {
330 .name = "ioengine",
331 .type = FIO_OPT_STR_STORE,
332 .off1 = td_var_offset(ioengine),
333 .help = "IO engine to use",
334 .def = "sync",
335 .posval = {
336 { .ival = "sync",
337 .help = "Use read/write",
338 },
339 { .ival = "psync",
340 .help = "Use pread/pwrite",
341 },
342#ifdef FIO_HAVE_LIBAIO
343 { .ival = "libaio",
344 .help = "Linux native asynchronous IO",
345 },
346#endif
347#ifdef FIO_HAVE_POSIXAIO
348 { .ival = "posixaio",
349 .help = "POSIX asynchronous IO",
350 },
351#endif
352 { .ival = "mmap",
353 .help = "Memory mapped IO",
354 },
355#ifdef FIO_HAVE_SPLICE
356 { .ival = "splice",
357 .help = "splice/vmsplice based IO",
358 },
359 { .ival = "netsplice",
360 .help = "splice/vmsplice to/from the network",
361 },
362#endif
363#ifdef FIO_HAVE_SGIO
364 { .ival = "sg",
365 .help = "SCSI generic v3 IO",
366 },
367#endif
368 { .ival = "null",
369 .help = "Testing engine (no data transfer)",
370 },
371 { .ival = "net",
372 .help = "Network IO",
373 },
374#ifdef FIO_HAVE_SYSLET
375 { .ival = "syslet-rw",
376 .help = "syslet enabled async pread/pwrite IO",
377 },
378#endif
379 { .ival = "cpuio",
380 .help = "CPU cycler burner engine",
381 },
382#ifdef FIO_HAVE_GUASI
383 { .ival = "guasi",
384 .help = "GUASI IO engine",
385 },
386#endif
387 { .ival = "external",
388 .help = "Load external engine (append name)",
389 },
390 },
391 },
392 {
393 .name = "iodepth",
394 .type = FIO_OPT_INT,
395 .off1 = td_var_offset(iodepth),
396 .help = "Amount of IO buffers to keep in flight",
397 .def = "1",
398 },
399 {
400 .name = "iodepth_batch",
401 .type = FIO_OPT_INT,
402 .off1 = td_var_offset(iodepth_batch),
403 .help = "Number of IO to submit in one go",
404 .parent = "iodepth",
405 },
406 {
407 .name = "iodepth_low",
408 .type = FIO_OPT_INT,
409 .off1 = td_var_offset(iodepth_low),
410 .help = "Low water mark for queuing depth",
411 .parent = "iodepth",
412 },
413 {
414 .name = "size",
415 .type = FIO_OPT_STR_VAL,
416 .off1 = td_var_offset(size),
417 .minval = 1,
418 .help = "Total size of device or files",
419 },
420 {
421 .name = "filesize",
422 .type = FIO_OPT_STR_VAL,
423 .off1 = td_var_offset(file_size_low),
424 .off2 = td_var_offset(file_size_high),
425 .minval = 1,
426 .help = "Size of individual files",
427 },
428 {
429 .name = "offset",
430 .alias = "fileoffset",
431 .type = FIO_OPT_STR_VAL,
432 .off1 = td_var_offset(start_offset),
433 .help = "Start IO from this offset",
434 .def = "0",
435 },
436 {
437 .name = "bs",
438 .alias = "blocksize",
439 .type = FIO_OPT_STR_VAL_INT,
440 .off1 = td_var_offset(bs[DDIR_READ]),
441 .off2 = td_var_offset(bs[DDIR_WRITE]),
442 .minval = 1,
443 .help = "Block size unit",
444 .def = "4k",
445 .parent = "rw",
446 },
447 {
448 .name = "bsrange",
449 .alias = "blocksize_range",
450 .type = FIO_OPT_RANGE,
451 .off1 = td_var_offset(min_bs[DDIR_READ]),
452 .off2 = td_var_offset(max_bs[DDIR_READ]),
453 .off3 = td_var_offset(min_bs[DDIR_WRITE]),
454 .off4 = td_var_offset(max_bs[DDIR_WRITE]),
455 .minval = 1,
456 .help = "Set block size range (in more detail than bs)",
457 .parent = "rw",
458 },
459 {
460 .name = "bs_unaligned",
461 .alias = "blocksize_unaligned",
462 .type = FIO_OPT_STR_SET,
463 .off1 = td_var_offset(bs_unaligned),
464 .help = "Don't sector align IO buffer sizes",
465 .parent = "rw",
466 },
467 {
468 .name = "randrepeat",
469 .type = FIO_OPT_BOOL,
470 .off1 = td_var_offset(rand_repeatable),
471 .help = "Use repeatable random IO pattern",
472 .def = "1",
473 .parent = "rw",
474 },
475 {
476 .name = "norandommap",
477 .type = FIO_OPT_STR_SET,
478 .off1 = td_var_offset(norandommap),
479 .help = "Accept potential duplicate random blocks",
480 .parent = "rw",
481 },
482 {
483 .name = "nrfiles",
484 .type = FIO_OPT_INT,
485 .off1 = td_var_offset(nr_files),
486 .help = "Split job workload between this number of files",
487 .def = "1",
488 },
489 {
490 .name = "openfiles",
491 .type = FIO_OPT_INT,
492 .off1 = td_var_offset(open_files),
493 .help = "Number of files to keep open at the same time",
494 },
495 {
496 .name = "file_service_type",
497 .type = FIO_OPT_STR,
498 .cb = str_fst_cb,
499 .off1 = td_var_offset(file_service_type),
500 .help = "How to select which file to service next",
501 .def = "roundrobin",
502 .posval = {
503 { .ival = "random",
504 .oval = FIO_FSERVICE_RANDOM,
505 .help = "Choose a file at random",
506 },
507 { .ival = "roundrobin",
508 .oval = FIO_FSERVICE_RR,
509 .help = "Round robin select files",
510 },
511 },
512 .parent = "nrfiles",
513 },
514 {
515 .name = "fadvise_hint",
516 .type = FIO_OPT_BOOL,
517 .off1 = td_var_offset(fadvise_hint),
518 .help = "Use fadvise() to advise the kernel on IO pattern",
519 .def = "1",
520 },
521 {
522 .name = "fsync",
523 .type = FIO_OPT_INT,
524 .off1 = td_var_offset(fsync_blocks),
525 .help = "Issue fsync for writes every given number of blocks",
526 .def = "0",
527 },
528 {
529 .name = "direct",
530 .type = FIO_OPT_BOOL,
531 .off1 = td_var_offset(odirect),
532 .help = "Use O_DIRECT IO (negates buffered)",
533 .def = "0",
534 },
535 {
536 .name = "buffered",
537 .type = FIO_OPT_BOOL,
538 .off1 = td_var_offset(odirect),
539 .neg = 1,
540 .help = "Use buffered IO (negates direct)",
541 .def = "1",
542 },
543 {
544 .name = "overwrite",
545 .type = FIO_OPT_BOOL,
546 .off1 = td_var_offset(overwrite),
547 .help = "When writing, set whether to overwrite current data",
548 .def = "0",
549 },
550 {
551 .name = "loops",
552 .type = FIO_OPT_INT,
553 .off1 = td_var_offset(loops),
554 .help = "Number of times to run the job",
555 .def = "1",
556 },
557 {
558 .name = "numjobs",
559 .type = FIO_OPT_INT,
560 .off1 = td_var_offset(numjobs),
561 .help = "Duplicate this job this many times",
562 .def = "1",
563 },
564 {
565 .name = "startdelay",
566 .type = FIO_OPT_INT,
567 .off1 = td_var_offset(start_delay),
568 .help = "Only start job when this period has passed",
569 .def = "0",
570 },
571 {
572 .name = "runtime",
573 .alias = "timeout",
574 .type = FIO_OPT_STR_VAL_TIME,
575 .off1 = td_var_offset(timeout),
576 .help = "Stop workload when this amount of time has passed",
577 .def = "0",
578 },
579 {
580 .name = "time_based",
581 .type = FIO_OPT_STR_SET,
582 .off1 = td_var_offset(time_based),
583 .help = "Keep running until runtime/timeout is met",
584 },
585 {
586 .name = "mem",
587 .alias = "iomem",
588 .type = FIO_OPT_STR,
589 .cb = str_mem_cb,
590 .off1 = td_var_offset(mem_type),
591 .help = "Backing type for IO buffers",
592 .def = "malloc",
593 .posval = {
594 { .ival = "malloc",
595 .oval = MEM_MALLOC,
596 .help = "Use malloc(3) for IO buffers",
597 },
598 { .ival = "shm",
599 .oval = MEM_SHM,
600 .help = "Use shared memory segments for IO buffers",
601 },
602#ifdef FIO_HAVE_HUGETLB
603 { .ival = "shmhuge",
604 .oval = MEM_SHMHUGE,
605 .help = "Like shm, but use huge pages",
606 },
607#endif
608 { .ival = "mmap",
609 .oval = MEM_MMAP,
610 .help = "Use mmap(2) (file or anon) for IO buffers",
611 },
612#ifdef FIO_HAVE_HUGETLB
613 { .ival = "mmaphuge",
614 .oval = MEM_MMAPHUGE,
615 .help = "Like mmap, but use huge pages",
616 },
617#endif
618 },
619 },
620 {
621 .name = "verify",
622 .type = FIO_OPT_STR,
623 .off1 = td_var_offset(verify),
624 .cb = str_verify_cb,
625 .help = "Verify data written",
626 .def = "0",
627 .posval = {
628 { .ival = "0",
629 .oval = VERIFY_NONE,
630 .help = "Don't do IO verification",
631 },
632 { .ival = "md5",
633 .oval = VERIFY_MD5,
634 .help = "Use md5 checksums for verification",
635 },
636 { .ival = "crc64",
637 .oval = VERIFY_CRC64,
638 .help = "Use crc64 checksums for verification",
639 },
640 { .ival = "crc32",
641 .oval = VERIFY_CRC32,
642 .help = "Use crc32 checksums for verification",
643 },
644 { .ival = "crc16",
645 .oval = VERIFY_CRC16,
646 .help = "Use crc16 checksums for verification",
647 },
648 { .ival = "crc7",
649 .oval = VERIFY_CRC7,
650 .help = "Use crc7 checksums for verification",
651 },
652 { .ival = "sha256",
653 .oval = VERIFY_SHA256,
654 .help = "Use sha256 checksums for verification",
655 },
656 { .ival = "sha512",
657 .oval = VERIFY_SHA512,
658 .help = "Use sha512 checksums for verification",
659 },
660 { .ival = "meta",
661 .oval = VERIFY_META,
662 .help = "Use io information",
663 },
664 { .ival = "pattern",
665 .oval = VERIFY_PATTERN,
666 .help = "Verify a specific buffer pattern",
667 },
668 {
669 .ival = "null",
670 .oval = VERIFY_NULL,
671 .help = "Pretend to verify",
672 },
673 },
674 },
675 {
676 .name = "do_verify",
677 .type = FIO_OPT_BOOL,
678 .off1 = td_var_offset(do_verify),
679 .help = "Run verification stage after write",
680 .def = "1",
681 .parent = "verify",
682 },
683 {
684 .name = "verifysort",
685 .type = FIO_OPT_BOOL,
686 .off1 = td_var_offset(verifysort),
687 .help = "Sort written verify blocks for read back",
688 .def = "1",
689 .parent = "verify",
690 },
691 {
692 .name = "verify_interval",
693 .type = FIO_OPT_STR_VAL_INT,
694 .off1 = td_var_offset(verify_interval),
695 .minval = 2 * sizeof(struct verify_header),
696 .help = "Store verify buffer header every N bytes",
697 .parent = "verify",
698 },
699 {
700 .name = "verify_offset",
701 .type = FIO_OPT_STR_VAL_INT,
702 .help = "Offset verify header location by N bytes",
703 .def = "0",
704 .cb = str_verify_offset_cb,
705 .parent = "verify",
706 },
707 {
708 .name = "verify_fatal",
709 .type = FIO_OPT_BOOL,
710 .off1 = td_var_offset(verify_fatal),
711 .def = "0",
712 .help = "Exit on a single verify failure, don't continue",
713 .parent = "verify",
714 },
715 {
716 .name = "write_iolog",
717 .type = FIO_OPT_STR_STORE,
718 .off1 = td_var_offset(write_iolog_file),
719 .help = "Store IO pattern to file",
720 },
721 {
722 .name = "read_iolog",
723 .type = FIO_OPT_STR_STORE,
724 .off1 = td_var_offset(read_iolog_file),
725 .help = "Playback IO pattern from file",
726 },
727 {
728 .name = "exec_prerun",
729 .type = FIO_OPT_STR_STORE,
730 .off1 = td_var_offset(exec_prerun),
731 .help = "Execute this file prior to running job",
732 },
733 {
734 .name = "exec_postrun",
735 .type = FIO_OPT_STR_STORE,
736 .off1 = td_var_offset(exec_postrun),
737 .help = "Execute this file after running job",
738 },
739#ifdef FIO_HAVE_IOSCHED_SWITCH
740 {
741 .name = "ioscheduler",
742 .type = FIO_OPT_STR_STORE,
743 .off1 = td_var_offset(ioscheduler),
744 .help = "Use this IO scheduler on the backing device",
745 },
746#endif
747 {
748 .name = "zonesize",
749 .type = FIO_OPT_STR_VAL,
750 .off1 = td_var_offset(zone_size),
751 .help = "Give size of an IO zone",
752 .def = "0",
753 },
754 {
755 .name = "zoneskip",
756 .type = FIO_OPT_STR_VAL,
757 .off1 = td_var_offset(zone_skip),
758 .help = "Space between IO zones",
759 .def = "0",
760 },
761 {
762 .name = "lockmem",
763 .type = FIO_OPT_STR_VAL,
764 .cb = str_lockmem_cb,
765 .help = "Lock down this amount of memory",
766 .def = "0",
767 },
768 {
769 .name = "rwmixread",
770 .type = FIO_OPT_INT,
771 .off1 = td_var_offset(rwmix[DDIR_READ]),
772 .maxval = 100,
773 .help = "Percentage of mixed workload that is reads",
774 .def = "50",
775 },
776 {
777 .name = "rwmixwrite",
778 .type = FIO_OPT_INT,
779 .off1 = td_var_offset(rwmix[DDIR_WRITE]),
780 .maxval = 100,
781 .help = "Percentage of mixed workload that is writes",
782 .def = "50",
783 },
784 {
785 .name = "rwmixcycle",
786 .type = FIO_OPT_INT,
787 .off1 = td_var_offset(rwmixcycle),
788 .help = "Cycle period for mixed read/write workloads (msec)",
789 .def = "500",
790 .parent = "rwmixread",
791 },
792 {
793 .name = "nice",
794 .type = FIO_OPT_INT,
795 .off1 = td_var_offset(nice),
796 .help = "Set job CPU nice value",
797 .minval = -19,
798 .maxval = 20,
799 .def = "0",
800 },
801#ifdef FIO_HAVE_IOPRIO
802 {
803 .name = "prio",
804 .type = FIO_OPT_INT,
805 .cb = str_prio_cb,
806 .help = "Set job IO priority value",
807 .minval = 0,
808 .maxval = 7,
809 },
810 {
811 .name = "prioclass",
812 .type = FIO_OPT_INT,
813 .cb = str_prioclass_cb,
814 .help = "Set job IO priority class",
815 .minval = 0,
816 .maxval = 3,
817 },
818#endif
819 {
820 .name = "thinktime",
821 .type = FIO_OPT_INT,
822 .off1 = td_var_offset(thinktime),
823 .help = "Idle time between IO buffers (usec)",
824 .def = "0",
825 },
826 {
827 .name = "thinktime_spin",
828 .type = FIO_OPT_INT,
829 .off1 = td_var_offset(thinktime_spin),
830 .help = "Start think time by spinning this amount (usec)",
831 .def = "0",
832 .parent = "thinktime",
833 },
834 {
835 .name = "thinktime_blocks",
836 .type = FIO_OPT_INT,
837 .off1 = td_var_offset(thinktime_blocks),
838 .help = "IO buffer period between 'thinktime'",
839 .def = "1",
840 .parent = "thinktime",
841 },
842 {
843 .name = "rate",
844 .type = FIO_OPT_INT,
845 .off1 = td_var_offset(rate),
846 .help = "Set bandwidth rate",
847 },
848 {
849 .name = "ratemin",
850 .type = FIO_OPT_INT,
851 .off1 = td_var_offset(ratemin),
852 .help = "Job must meet this rate or it will be shutdown",
853 .parent = "rate",
854 },
855 {
856 .name = "rate_iops",
857 .type = FIO_OPT_INT,
858 .off1 = td_var_offset(rate_iops),
859 .help = "Limit IO used to this number of IO operations/sec",
860 },
861 {
862 .name = "rate_iops_min",
863 .type = FIO_OPT_INT,
864 .off1 = td_var_offset(rate_iops_min),
865 .help = "Job must meet this rate or it will be shutdown",
866 .parent = "rate_iops",
867 },
868 {
869 .name = "ratecycle",
870 .type = FIO_OPT_INT,
871 .off1 = td_var_offset(ratecycle),
872 .help = "Window average for rate limits (msec)",
873 .def = "1000",
874 .parent = "rate",
875 },
876 {
877 .name = "invalidate",
878 .type = FIO_OPT_BOOL,
879 .off1 = td_var_offset(invalidate_cache),
880 .help = "Invalidate buffer/page cache prior to running job",
881 .def = "1",
882 },
883 {
884 .name = "sync",
885 .type = FIO_OPT_BOOL,
886 .off1 = td_var_offset(sync_io),
887 .help = "Use O_SYNC for buffered writes",
888 .def = "0",
889 .parent = "buffered",
890 },
891 {
892 .name = "bwavgtime",
893 .type = FIO_OPT_INT,
894 .off1 = td_var_offset(bw_avg_time),
895 .help = "Time window over which to calculate bandwidth (msec)",
896 .def = "500",
897 },
898 {
899 .name = "create_serialize",
900 .type = FIO_OPT_BOOL,
901 .off1 = td_var_offset(create_serialize),
902 .help = "Serialize creating of job files",
903 .def = "1",
904 },
905 {
906 .name = "create_fsync",
907 .type = FIO_OPT_BOOL,
908 .off1 = td_var_offset(create_fsync),
909 .help = "Fsync file after creation",
910 .def = "1",
911 },
912 {
913 .name = "cpuload",
914 .type = FIO_OPT_INT,
915 .off1 = td_var_offset(cpuload),
916 .help = "Use this percentage of CPU",
917 },
918 {
919 .name = "cpuchunks",
920 .type = FIO_OPT_INT,
921 .off1 = td_var_offset(cpucycle),
922 .help = "Length of the CPU burn cycles (usecs)",
923 .def = "50000",
924 .parent = "cpuload",
925 },
926#ifdef FIO_HAVE_CPU_AFFINITY
927 {
928 .name = "cpumask",
929 .type = FIO_OPT_INT,
930 .cb = str_cpumask_cb,
931 .help = "CPU affinity mask",
932 },
933 {
934 .name = "cpus_allowed",
935 .type = FIO_OPT_STR,
936 .cb = str_cpus_allowed_cb,
937 .help = "Set CPUs allowed",
938 },
939#endif
940 {
941 .name = "end_fsync",
942 .type = FIO_OPT_BOOL,
943 .off1 = td_var_offset(end_fsync),
944 .help = "Include fsync at the end of job",
945 .def = "0",
946 },
947 {
948 .name = "fsync_on_close",
949 .type = FIO_OPT_BOOL,
950 .off1 = td_var_offset(fsync_on_close),
951 .help = "fsync files on close",
952 .def = "0",
953 },
954 {
955 .name = "unlink",
956 .type = FIO_OPT_BOOL,
957 .off1 = td_var_offset(unlink),
958 .help = "Unlink created files after job has completed",
959 .def = "0",
960 },
961 {
962 .name = "exitall",
963 .type = FIO_OPT_STR_SET,
964 .cb = str_exitall_cb,
965 .help = "Terminate all jobs when one exits",
966 },
967 {
968 .name = "stonewall",
969 .type = FIO_OPT_STR_SET,
970 .off1 = td_var_offset(stonewall),
971 .help = "Insert a hard barrier between this job and previous",
972 },
973 {
974 .name = "new_group",
975 .type = FIO_OPT_STR_SET,
976 .off1 = td_var_offset(new_group),
977 .help = "Mark the start of a new group (for reporting)",
978 },
979 {
980 .name = "thread",
981 .type = FIO_OPT_STR_SET,
982 .off1 = td_var_offset(use_thread),
983 .help = "Use threads instead of forks",
984 },
985 {
986 .name = "write_bw_log",
987 .type = FIO_OPT_STR_SET,
988 .off1 = td_var_offset(write_bw_log),
989 .help = "Write log of bandwidth during run",
990 },
991 {
992 .name = "write_lat_log",
993 .type = FIO_OPT_STR_SET,
994 .off1 = td_var_offset(write_lat_log),
995 .help = "Write log of latency during run",
996 },
997 {
998 .name = "hugepage-size",
999 .type = FIO_OPT_STR_VAL,
1000 .off1 = td_var_offset(hugepage_size),
1001 .help = "When using hugepages, specify size of each page",
1002 .def = __stringify(FIO_HUGE_PAGE),
1003 },
1004 {
1005 .name = "group_reporting",
1006 .type = FIO_OPT_STR_SET,
1007 .off1 = td_var_offset(group_reporting),
1008 .help = "Do reporting on a per-group basis",
1009 },
1010 {
1011 .name = "zero_buffers",
1012 .type = FIO_OPT_STR_SET,
1013 .off1 = td_var_offset(zero_buffers),
1014 .help = "Init IO buffers to all zeroes",
1015 },
1016#ifdef FIO_HAVE_DISK_UTIL
1017 {
1018 .name = "disk_util",
1019 .type = FIO_OPT_BOOL,
1020 .off1 = td_var_offset(do_disk_util),
1021 .help = "Log disk utilization stats",
1022 .def = "1",
1023 },
1024#endif
1025 {
1026 .name = NULL,
1027 },
1028};
1029
1030void fio_options_dup_and_init(struct option *long_options)
1031{
1032 struct fio_option *o;
1033 unsigned int i;
1034
1035 options_init(options);
1036
1037 i = 0;
1038 while (long_options[i].name)
1039 i++;
1040
1041 o = &options[0];
1042 while (o->name) {
1043 long_options[i].name = o->name;
1044 long_options[i].val = FIO_GETOPT_JOB;
1045 if (o->type == FIO_OPT_STR_SET)
1046 long_options[i].has_arg = no_argument;
1047 else
1048 long_options[i].has_arg = required_argument;
1049
1050 i++;
1051 o++;
1052 assert(i < FIO_NR_OPTIONS);
1053 }
1054}
1055
1056int fio_option_parse(struct thread_data *td, const char *opt)
1057{
1058 return parse_option(opt, options, td);
1059}
1060
1061int fio_cmd_option_parse(struct thread_data *td, const char *opt, char *val)
1062{
1063 return parse_cmd_option(opt, val, options, td);
1064}
1065
1066void fio_fill_default_options(struct thread_data *td)
1067{
1068 fill_default_options(td, options);
1069}
1070
1071int fio_show_option_help(const char *opt)
1072{
1073 return show_cmd_help(options, opt);
1074}
1075
1076static void __options_mem(struct thread_data *td, int alloc)
1077{
1078 struct thread_options *o = &td->o;
1079 struct fio_option *opt;
1080 char **ptr;
1081 int i;
1082
1083 for (i = 0, opt = &options[0]; opt->name; i++, opt = &options[i]) {
1084 if (opt->type != FIO_OPT_STR_STORE)
1085 continue;
1086
1087 ptr = (void *) o + opt->off1;
1088 if (*ptr) {
1089 if (alloc)
1090 *ptr = strdup(*ptr);
1091 else {
1092 free(*ptr);
1093 *ptr = NULL;
1094 }
1095 }
1096 }
1097}
1098
1099/*
1100 * dupe FIO_OPT_STR_STORE options
1101 */
1102void options_mem_dupe(struct thread_data *td)
1103{
1104 __options_mem(td, 1);
1105}
1106
1107void options_mem_free(struct thread_data fio_unused *td)
1108{
1109#if 0
1110 __options_mem(td, 0);
1111#endif
1112}