Let fadvise_hint also apply too mmap engine and madvise
[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 <assert.h>
7#include <libgen.h>
8#include <fcntl.h>
9#include <sys/types.h>
10#include <sys/stat.h>
11#include <netinet/in.h>
12
13#include "fio.h"
14#include "verify.h"
15#include "parse.h"
16#include "lib/fls.h"
17#include "lib/pattern.h"
18#include "options.h"
19#include "optgroup.h"
20
21char client_sockaddr_str[INET6_ADDRSTRLEN] = { 0 };
22
23#define cb_data_to_td(data) container_of(data, struct thread_data, o)
24
25static struct pattern_fmt_desc fmt_desc[] = {
26 {
27 .fmt = "%o",
28 .len = FIELD_SIZE(struct io_u *, offset),
29 .paste = paste_blockoff
30 }
31};
32
33/*
34 * Check if mmap/mmaphuge has a :/foo/bar/file at the end. If so, return that.
35 */
36static char *get_opt_postfix(const char *str)
37{
38 char *p = strstr(str, ":");
39
40 if (!p)
41 return NULL;
42
43 p++;
44 strip_blank_front(&p);
45 strip_blank_end(p);
46 return strdup(p);
47}
48
49static int bs_cmp(const void *p1, const void *p2)
50{
51 const struct bssplit *bsp1 = p1;
52 const struct bssplit *bsp2 = p2;
53
54 return (int) bsp1->perc - (int) bsp2->perc;
55}
56
57struct split {
58 unsigned int nr;
59 unsigned int val1[ZONESPLIT_MAX];
60 unsigned long long val2[ZONESPLIT_MAX];
61};
62
63static int split_parse_ddir(struct thread_options *o, struct split *split,
64 enum fio_ddir ddir, char *str, bool absolute,
65 unsigned int max_splits)
66{
67 unsigned long long perc;
68 unsigned int i;
69 long long val;
70 char *fname;
71
72 split->nr = 0;
73
74 i = 0;
75 while ((fname = strsep(&str, ":")) != NULL) {
76 char *perc_str;
77
78 if (!strlen(fname))
79 break;
80
81 perc_str = strstr(fname, "/");
82 if (perc_str) {
83 *perc_str = '\0';
84 perc_str++;
85 if (absolute) {
86 if (str_to_decimal(perc_str, &val, 1, o, 0, 0)) {
87 log_err("fio: split conversion failed\n");
88 return 1;
89 }
90 perc = val;
91 } else {
92 perc = atoi(perc_str);
93 if (perc > 100)
94 perc = 100;
95 else if (!perc)
96 perc = -1U;
97 }
98 } else {
99 if (absolute)
100 perc = 0;
101 else
102 perc = -1U;
103 }
104
105 if (str_to_decimal(fname, &val, 1, o, 0, 0)) {
106 log_err("fio: split conversion failed\n");
107 return 1;
108 }
109
110 split->val1[i] = val;
111 split->val2[i] = perc;
112 i++;
113 if (i == max_splits) {
114 log_err("fio: hit max of %d split entries\n", i);
115 break;
116 }
117 }
118
119 split->nr = i;
120 return 0;
121}
122
123static int bssplit_ddir(struct thread_options *o, enum fio_ddir ddir, char *str,
124 bool data)
125{
126 unsigned int i, perc, perc_missing;
127 unsigned int max_bs, min_bs;
128 struct split split;
129
130 memset(&split, 0, sizeof(split));
131
132 if (split_parse_ddir(o, &split, ddir, str, data, BSSPLIT_MAX))
133 return 1;
134 if (!split.nr)
135 return 0;
136
137 max_bs = 0;
138 min_bs = -1;
139 o->bssplit[ddir] = malloc(split.nr * sizeof(struct bssplit));
140 o->bssplit_nr[ddir] = split.nr;
141 for (i = 0; i < split.nr; i++) {
142 if (split.val1[i] > max_bs)
143 max_bs = split.val1[i];
144 if (split.val1[i] < min_bs)
145 min_bs = split.val1[i];
146
147 o->bssplit[ddir][i].bs = split.val1[i];
148 o->bssplit[ddir][i].perc =split.val2[i];
149 }
150
151 /*
152 * Now check if the percentages add up, and how much is missing
153 */
154 perc = perc_missing = 0;
155 for (i = 0; i < o->bssplit_nr[ddir]; i++) {
156 struct bssplit *bsp = &o->bssplit[ddir][i];
157
158 if (bsp->perc == -1U)
159 perc_missing++;
160 else
161 perc += bsp->perc;
162 }
163
164 if (perc > 100 && perc_missing > 1) {
165 log_err("fio: bssplit percentages add to more than 100%%\n");
166 free(o->bssplit[ddir]);
167 o->bssplit[ddir] = NULL;
168 return 1;
169 }
170
171 /*
172 * If values didn't have a percentage set, divide the remains between
173 * them.
174 */
175 if (perc_missing) {
176 if (perc_missing == 1 && o->bssplit_nr[ddir] == 1)
177 perc = 100;
178 for (i = 0; i < o->bssplit_nr[ddir]; i++) {
179 struct bssplit *bsp = &o->bssplit[ddir][i];
180
181 if (bsp->perc == -1U)
182 bsp->perc = (100 - perc) / perc_missing;
183 }
184 }
185
186 o->min_bs[ddir] = min_bs;
187 o->max_bs[ddir] = max_bs;
188
189 /*
190 * now sort based on percentages, for ease of lookup
191 */
192 qsort(o->bssplit[ddir], o->bssplit_nr[ddir], sizeof(struct bssplit), bs_cmp);
193 return 0;
194}
195
196typedef int (split_parse_fn)(struct thread_options *, enum fio_ddir, char *, bool);
197
198static int str_split_parse(struct thread_data *td, char *str,
199 split_parse_fn *fn, bool data)
200{
201 char *odir, *ddir;
202 int ret = 0;
203
204 odir = strchr(str, ',');
205 if (odir) {
206 ddir = strchr(odir + 1, ',');
207 if (ddir) {
208 ret = fn(&td->o, DDIR_TRIM, ddir + 1, data);
209 if (!ret)
210 *ddir = '\0';
211 } else {
212 char *op;
213
214 op = strdup(odir + 1);
215 ret = fn(&td->o, DDIR_TRIM, op, data);
216
217 free(op);
218 }
219 if (!ret)
220 ret = fn(&td->o, DDIR_WRITE, odir + 1, data);
221 if (!ret) {
222 *odir = '\0';
223 ret = fn(&td->o, DDIR_READ, str, data);
224 }
225 } else {
226 char *op;
227
228 op = strdup(str);
229 ret = fn(&td->o, DDIR_WRITE, op, data);
230 free(op);
231
232 if (!ret) {
233 op = strdup(str);
234 ret = fn(&td->o, DDIR_TRIM, op, data);
235 free(op);
236 }
237 if (!ret)
238 ret = fn(&td->o, DDIR_READ, str, data);
239 }
240
241 return ret;
242}
243
244static int str_bssplit_cb(void *data, const char *input)
245{
246 struct thread_data *td = cb_data_to_td(data);
247 char *str, *p;
248 int ret = 0;
249
250 p = str = strdup(input);
251
252 strip_blank_front(&str);
253 strip_blank_end(str);
254
255 ret = str_split_parse(td, str, bssplit_ddir, false);
256
257 if (parse_dryrun()) {
258 int i;
259
260 for (i = 0; i < DDIR_RWDIR_CNT; i++) {
261 free(td->o.bssplit[i]);
262 td->o.bssplit[i] = NULL;
263 td->o.bssplit_nr[i] = 0;
264 }
265 }
266
267 free(p);
268 return ret;
269}
270
271static int str2error(char *str)
272{
273 const char *err[] = { "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO",
274 "ENXIO", "E2BIG", "ENOEXEC", "EBADF",
275 "ECHILD", "EAGAIN", "ENOMEM", "EACCES",
276 "EFAULT", "ENOTBLK", "EBUSY", "EEXIST",
277 "EXDEV", "ENODEV", "ENOTDIR", "EISDIR",
278 "EINVAL", "ENFILE", "EMFILE", "ENOTTY",
279 "ETXTBSY","EFBIG", "ENOSPC", "ESPIPE",
280 "EROFS","EMLINK", "EPIPE", "EDOM", "ERANGE" };
281 int i = 0, num = sizeof(err) / sizeof(char *);
282
283 while (i < num) {
284 if (!strcmp(err[i], str))
285 return i + 1;
286 i++;
287 }
288 return 0;
289}
290
291static int ignore_error_type(struct thread_data *td, enum error_type_bit etype,
292 char *str)
293{
294 unsigned int i;
295 int *error;
296 char *fname;
297
298 if (etype >= ERROR_TYPE_CNT) {
299 log_err("Illegal error type\n");
300 return 1;
301 }
302
303 td->o.ignore_error_nr[etype] = 4;
304 error = calloc(4, sizeof(int));
305
306 i = 0;
307 while ((fname = strsep(&str, ":")) != NULL) {
308
309 if (!strlen(fname))
310 break;
311
312 /*
313 * grow struct buffer, if needed
314 */
315 if (i == td->o.ignore_error_nr[etype]) {
316 td->o.ignore_error_nr[etype] <<= 1;
317 error = realloc(error, td->o.ignore_error_nr[etype]
318 * sizeof(int));
319 }
320 if (fname[0] == 'E') {
321 error[i] = str2error(fname);
322 } else {
323 error[i] = atoi(fname);
324 if (error[i] < 0)
325 error[i] = -error[i];
326 }
327 if (!error[i]) {
328 log_err("Unknown error %s, please use number value\n",
329 fname);
330 td->o.ignore_error_nr[etype] = 0;
331 free(error);
332 return 1;
333 }
334 i++;
335 }
336 if (i) {
337 td->o.continue_on_error |= 1 << etype;
338 td->o.ignore_error_nr[etype] = i;
339 td->o.ignore_error[etype] = error;
340 } else {
341 td->o.ignore_error_nr[etype] = 0;
342 free(error);
343 }
344
345 return 0;
346
347}
348
349static int str_ignore_error_cb(void *data, const char *input)
350{
351 struct thread_data *td = cb_data_to_td(data);
352 char *str, *p, *n;
353 int ret = 1;
354 enum error_type_bit type = 0;
355
356 if (parse_dryrun())
357 return 0;
358
359 p = str = strdup(input);
360
361 strip_blank_front(&str);
362 strip_blank_end(str);
363
364 while (p) {
365 n = strchr(p, ',');
366 if (n)
367 *n++ = '\0';
368 ret = ignore_error_type(td, type, p);
369 if (ret)
370 break;
371 p = n;
372 type++;
373 }
374 free(str);
375 return ret;
376}
377
378static int str_rw_cb(void *data, const char *str)
379{
380 struct thread_data *td = cb_data_to_td(data);
381 struct thread_options *o = &td->o;
382 char *nr;
383
384 if (parse_dryrun())
385 return 0;
386
387 o->ddir_seq_nr = 1;
388 o->ddir_seq_add = 0;
389
390 nr = get_opt_postfix(str);
391 if (!nr)
392 return 0;
393
394 if (td_random(td))
395 o->ddir_seq_nr = atoi(nr);
396 else {
397 long long val;
398
399 if (str_to_decimal(nr, &val, 1, o, 0, 0)) {
400 log_err("fio: rw postfix parsing failed\n");
401 free(nr);
402 return 1;
403 }
404
405 o->ddir_seq_add = val;
406 }
407
408 free(nr);
409 return 0;
410}
411
412static int str_mem_cb(void *data, const char *mem)
413{
414 struct thread_data *td = cb_data_to_td(data);
415
416 if (td->o.mem_type == MEM_MMAPHUGE || td->o.mem_type == MEM_MMAP ||
417 td->o.mem_type == MEM_MMAPSHARED)
418 td->o.mmapfile = get_opt_postfix(mem);
419
420 return 0;
421}
422
423static int fio_clock_source_cb(void *data, const char *str)
424{
425 struct thread_data *td = cb_data_to_td(data);
426
427 fio_clock_source = td->o.clocksource;
428 fio_clock_source_set = 1;
429 fio_clock_init();
430 return 0;
431}
432
433static int str_rwmix_read_cb(void *data, unsigned long long *val)
434{
435 struct thread_data *td = cb_data_to_td(data);
436
437 td->o.rwmix[DDIR_READ] = *val;
438 td->o.rwmix[DDIR_WRITE] = 100 - *val;
439 return 0;
440}
441
442static int str_rwmix_write_cb(void *data, unsigned long long *val)
443{
444 struct thread_data *td = cb_data_to_td(data);
445
446 td->o.rwmix[DDIR_WRITE] = *val;
447 td->o.rwmix[DDIR_READ] = 100 - *val;
448 return 0;
449}
450
451static int str_exitall_cb(void)
452{
453 exitall_on_terminate = 1;
454 return 0;
455}
456
457#ifdef FIO_HAVE_CPU_AFFINITY
458int fio_cpus_split(os_cpu_mask_t *mask, unsigned int cpu_index)
459{
460 unsigned int i, index, cpus_in_mask;
461 const long max_cpu = cpus_online();
462
463 cpus_in_mask = fio_cpu_count(mask);
464 cpu_index = cpu_index % cpus_in_mask;
465
466 index = 0;
467 for (i = 0; i < max_cpu; i++) {
468 if (!fio_cpu_isset(mask, i))
469 continue;
470
471 if (cpu_index != index)
472 fio_cpu_clear(mask, i);
473
474 index++;
475 }
476
477 return fio_cpu_count(mask);
478}
479
480static int str_cpumask_cb(void *data, unsigned long long *val)
481{
482 struct thread_data *td = cb_data_to_td(data);
483 unsigned int i;
484 long max_cpu;
485 int ret;
486
487 if (parse_dryrun())
488 return 0;
489
490 ret = fio_cpuset_init(&td->o.cpumask);
491 if (ret < 0) {
492 log_err("fio: cpuset_init failed\n");
493 td_verror(td, ret, "fio_cpuset_init");
494 return 1;
495 }
496
497 max_cpu = cpus_online();
498
499 for (i = 0; i < sizeof(int) * 8; i++) {
500 if ((1 << i) & *val) {
501 if (i >= max_cpu) {
502 log_err("fio: CPU %d too large (max=%ld)\n", i,
503 max_cpu - 1);
504 return 1;
505 }
506 dprint(FD_PARSE, "set cpu allowed %d\n", i);
507 fio_cpu_set(&td->o.cpumask, i);
508 }
509 }
510
511 return 0;
512}
513
514static int set_cpus_allowed(struct thread_data *td, os_cpu_mask_t *mask,
515 const char *input)
516{
517 char *cpu, *str, *p;
518 long max_cpu;
519 int ret = 0;
520
521 ret = fio_cpuset_init(mask);
522 if (ret < 0) {
523 log_err("fio: cpuset_init failed\n");
524 td_verror(td, ret, "fio_cpuset_init");
525 return 1;
526 }
527
528 p = str = strdup(input);
529
530 strip_blank_front(&str);
531 strip_blank_end(str);
532
533 max_cpu = cpus_online();
534
535 while ((cpu = strsep(&str, ",")) != NULL) {
536 char *str2, *cpu2;
537 int icpu, icpu2;
538
539 if (!strlen(cpu))
540 break;
541
542 str2 = cpu;
543 icpu2 = -1;
544 while ((cpu2 = strsep(&str2, "-")) != NULL) {
545 if (!strlen(cpu2))
546 break;
547
548 icpu2 = atoi(cpu2);
549 }
550
551 icpu = atoi(cpu);
552 if (icpu2 == -1)
553 icpu2 = icpu;
554 while (icpu <= icpu2) {
555 if (icpu >= FIO_MAX_CPUS) {
556 log_err("fio: your OS only supports up to"
557 " %d CPUs\n", (int) FIO_MAX_CPUS);
558 ret = 1;
559 break;
560 }
561 if (icpu >= max_cpu) {
562 log_err("fio: CPU %d too large (max=%ld)\n",
563 icpu, max_cpu - 1);
564 ret = 1;
565 break;
566 }
567
568 dprint(FD_PARSE, "set cpu allowed %d\n", icpu);
569 fio_cpu_set(mask, icpu);
570 icpu++;
571 }
572 if (ret)
573 break;
574 }
575
576 free(p);
577 return ret;
578}
579
580static int str_cpus_allowed_cb(void *data, const char *input)
581{
582 struct thread_data *td = cb_data_to_td(data);
583
584 if (parse_dryrun())
585 return 0;
586
587 return set_cpus_allowed(td, &td->o.cpumask, input);
588}
589
590static int str_verify_cpus_allowed_cb(void *data, const char *input)
591{
592 struct thread_data *td = cb_data_to_td(data);
593
594 if (parse_dryrun())
595 return 0;
596
597 return set_cpus_allowed(td, &td->o.verify_cpumask, input);
598}
599
600#ifdef CONFIG_ZLIB
601static int str_log_cpus_allowed_cb(void *data, const char *input)
602{
603 struct thread_data *td = cb_data_to_td(data);
604
605 if (parse_dryrun())
606 return 0;
607
608 return set_cpus_allowed(td, &td->o.log_gz_cpumask, input);
609}
610#endif /* CONFIG_ZLIB */
611
612#endif /* FIO_HAVE_CPU_AFFINITY */
613
614#ifdef CONFIG_LIBNUMA
615static int str_numa_cpunodes_cb(void *data, char *input)
616{
617 struct thread_data *td = cb_data_to_td(data);
618 struct bitmask *verify_bitmask;
619
620 if (parse_dryrun())
621 return 0;
622
623 /* numa_parse_nodestring() parses a character string list
624 * of nodes into a bit mask. The bit mask is allocated by
625 * numa_allocate_nodemask(), so it should be freed by
626 * numa_free_nodemask().
627 */
628 verify_bitmask = numa_parse_nodestring(input);
629 if (verify_bitmask == NULL) {
630 log_err("fio: numa_parse_nodestring failed\n");
631 td_verror(td, 1, "str_numa_cpunodes_cb");
632 return 1;
633 }
634 numa_free_nodemask(verify_bitmask);
635
636 td->o.numa_cpunodes = strdup(input);
637 return 0;
638}
639
640static int str_numa_mpol_cb(void *data, char *input)
641{
642 struct thread_data *td = cb_data_to_td(data);
643 const char * const policy_types[] =
644 { "default", "prefer", "bind", "interleave", "local", NULL };
645 int i;
646 char *nodelist;
647 struct bitmask *verify_bitmask;
648
649 if (parse_dryrun())
650 return 0;
651
652 nodelist = strchr(input, ':');
653 if (nodelist) {
654 /* NUL-terminate mode */
655 *nodelist++ = '\0';
656 }
657
658 for (i = 0; i <= MPOL_LOCAL; i++) {
659 if (!strcmp(input, policy_types[i])) {
660 td->o.numa_mem_mode = i;
661 break;
662 }
663 }
664 if (i > MPOL_LOCAL) {
665 log_err("fio: memory policy should be: default, prefer, bind, interleave, local\n");
666 goto out;
667 }
668
669 switch (td->o.numa_mem_mode) {
670 case MPOL_PREFERRED:
671 /*
672 * Insist on a nodelist of one node only
673 */
674 if (nodelist) {
675 char *rest = nodelist;
676 while (isdigit(*rest))
677 rest++;
678 if (*rest) {
679 log_err("fio: one node only for \'prefer\'\n");
680 goto out;
681 }
682 } else {
683 log_err("fio: one node is needed for \'prefer\'\n");
684 goto out;
685 }
686 break;
687 case MPOL_INTERLEAVE:
688 /*
689 * Default to online nodes with memory if no nodelist
690 */
691 if (!nodelist)
692 nodelist = strdup("all");
693 break;
694 case MPOL_LOCAL:
695 case MPOL_DEFAULT:
696 /*
697 * Don't allow a nodelist
698 */
699 if (nodelist) {
700 log_err("fio: NO nodelist for \'local\'\n");
701 goto out;
702 }
703 break;
704 case MPOL_BIND:
705 /*
706 * Insist on a nodelist
707 */
708 if (!nodelist) {
709 log_err("fio: a nodelist is needed for \'bind\'\n");
710 goto out;
711 }
712 break;
713 }
714
715
716 /* numa_parse_nodestring() parses a character string list
717 * of nodes into a bit mask. The bit mask is allocated by
718 * numa_allocate_nodemask(), so it should be freed by
719 * numa_free_nodemask().
720 */
721 switch (td->o.numa_mem_mode) {
722 case MPOL_PREFERRED:
723 td->o.numa_mem_prefer_node = atoi(nodelist);
724 break;
725 case MPOL_INTERLEAVE:
726 case MPOL_BIND:
727 verify_bitmask = numa_parse_nodestring(nodelist);
728 if (verify_bitmask == NULL) {
729 log_err("fio: numa_parse_nodestring failed\n");
730 td_verror(td, 1, "str_numa_memnodes_cb");
731 return 1;
732 }
733 td->o.numa_memnodes = strdup(nodelist);
734 numa_free_nodemask(verify_bitmask);
735
736 break;
737 case MPOL_LOCAL:
738 case MPOL_DEFAULT:
739 default:
740 break;
741 }
742
743 return 0;
744out:
745 return 1;
746}
747#endif
748
749static int str_fst_cb(void *data, const char *str)
750{
751 struct thread_data *td = cb_data_to_td(data);
752 double val;
753 bool done = false;
754 char *nr;
755
756 td->file_service_nr = 1;
757
758 switch (td->o.file_service_type) {
759 case FIO_FSERVICE_RANDOM:
760 case FIO_FSERVICE_RR:
761 case FIO_FSERVICE_SEQ:
762 nr = get_opt_postfix(str);
763 if (nr) {
764 td->file_service_nr = atoi(nr);
765 free(nr);
766 }
767 done = true;
768 break;
769 case FIO_FSERVICE_ZIPF:
770 val = FIO_DEF_ZIPF;
771 break;
772 case FIO_FSERVICE_PARETO:
773 val = FIO_DEF_PARETO;
774 break;
775 case FIO_FSERVICE_GAUSS:
776 val = 0.0;
777 break;
778 default:
779 log_err("fio: bad file service type: %d\n", td->o.file_service_type);
780 return 1;
781 }
782
783 if (done)
784 return 0;
785
786 nr = get_opt_postfix(str);
787 if (nr && !str_to_float(nr, &val, 0)) {
788 log_err("fio: file service type random postfix parsing failed\n");
789 free(nr);
790 return 1;
791 }
792
793 free(nr);
794
795 switch (td->o.file_service_type) {
796 case FIO_FSERVICE_ZIPF:
797 if (val == 1.00) {
798 log_err("fio: zipf theta must be different than 1.0\n");
799 return 1;
800 }
801 if (parse_dryrun())
802 return 0;
803 td->zipf_theta = val;
804 break;
805 case FIO_FSERVICE_PARETO:
806 if (val <= 0.00 || val >= 1.00) {
807 log_err("fio: pareto input out of range (0 < input < 1.0)\n");
808 return 1;
809 }
810 if (parse_dryrun())
811 return 0;
812 td->pareto_h = val;
813 break;
814 case FIO_FSERVICE_GAUSS:
815 if (val < 0.00 || val >= 100.00) {
816 log_err("fio: normal deviation out of range (0 <= input < 100.0)\n");
817 return 1;
818 }
819 if (parse_dryrun())
820 return 0;
821 td->gauss_dev = val;
822 break;
823 }
824
825 return 0;
826}
827
828#ifdef CONFIG_SYNC_FILE_RANGE
829static int str_sfr_cb(void *data, const char *str)
830{
831 struct thread_data *td = cb_data_to_td(data);
832 char *nr = get_opt_postfix(str);
833
834 td->sync_file_range_nr = 1;
835 if (nr) {
836 td->sync_file_range_nr = atoi(nr);
837 free(nr);
838 }
839
840 return 0;
841}
842#endif
843
844static int zone_split_ddir(struct thread_options *o, enum fio_ddir ddir,
845 char *str, bool absolute)
846{
847 unsigned int i, perc, perc_missing, sperc, sperc_missing;
848 struct split split;
849
850 memset(&split, 0, sizeof(split));
851
852 if (split_parse_ddir(o, &split, ddir, str, absolute, ZONESPLIT_MAX))
853 return 1;
854 if (!split.nr)
855 return 0;
856
857 o->zone_split[ddir] = malloc(split.nr * sizeof(struct zone_split));
858 o->zone_split_nr[ddir] = split.nr;
859 for (i = 0; i < split.nr; i++) {
860 o->zone_split[ddir][i].access_perc = split.val1[i];
861 if (absolute)
862 o->zone_split[ddir][i].size = split.val2[i];
863 else
864 o->zone_split[ddir][i].size_perc = split.val2[i];
865 }
866
867 /*
868 * Now check if the percentages add up, and how much is missing
869 */
870 perc = perc_missing = 0;
871 sperc = sperc_missing = 0;
872 for (i = 0; i < o->zone_split_nr[ddir]; i++) {
873 struct zone_split *zsp = &o->zone_split[ddir][i];
874
875 if (zsp->access_perc == (uint8_t) -1U)
876 perc_missing++;
877 else
878 perc += zsp->access_perc;
879
880 if (!absolute) {
881 if (zsp->size_perc == (uint8_t) -1U)
882 sperc_missing++;
883 else
884 sperc += zsp->size_perc;
885 }
886 }
887
888 if (perc > 100 || sperc > 100) {
889 log_err("fio: zone_split percentages add to more than 100%%\n");
890 free(o->zone_split[ddir]);
891 o->zone_split[ddir] = NULL;
892 return 1;
893 }
894 if (perc < 100) {
895 log_err("fio: access percentage don't add up to 100 for zoned "
896 "random distribution (got=%u)\n", perc);
897 free(o->zone_split[ddir]);
898 o->zone_split[ddir] = NULL;
899 return 1;
900 }
901
902 /*
903 * If values didn't have a percentage set, divide the remains between
904 * them.
905 */
906 if (perc_missing) {
907 if (perc_missing == 1 && o->zone_split_nr[ddir] == 1)
908 perc = 100;
909 for (i = 0; i < o->zone_split_nr[ddir]; i++) {
910 struct zone_split *zsp = &o->zone_split[ddir][i];
911
912 if (zsp->access_perc == (uint8_t) -1U)
913 zsp->access_perc = (100 - perc) / perc_missing;
914 }
915 }
916 if (sperc_missing) {
917 if (sperc_missing == 1 && o->zone_split_nr[ddir] == 1)
918 sperc = 100;
919 for (i = 0; i < o->zone_split_nr[ddir]; i++) {
920 struct zone_split *zsp = &o->zone_split[ddir][i];
921
922 if (zsp->size_perc == (uint8_t) -1U)
923 zsp->size_perc = (100 - sperc) / sperc_missing;
924 }
925 }
926
927 return 0;
928}
929
930static void __td_zone_gen_index(struct thread_data *td, enum fio_ddir ddir)
931{
932 unsigned int i, j, sprev, aprev;
933 uint64_t sprev_sz;
934
935 td->zone_state_index[ddir] = malloc(sizeof(struct zone_split_index) * 100);
936
937 sprev_sz = sprev = aprev = 0;
938 for (i = 0; i < td->o.zone_split_nr[ddir]; i++) {
939 struct zone_split *zsp = &td->o.zone_split[ddir][i];
940
941 for (j = aprev; j < aprev + zsp->access_perc; j++) {
942 struct zone_split_index *zsi = &td->zone_state_index[ddir][j];
943
944 zsi->size_perc = sprev + zsp->size_perc;
945 zsi->size_perc_prev = sprev;
946
947 zsi->size = sprev_sz + zsp->size;
948 zsi->size_prev = sprev_sz;
949 }
950
951 aprev += zsp->access_perc;
952 sprev += zsp->size_perc;
953 sprev_sz += zsp->size;
954 }
955}
956
957/*
958 * Generate state table for indexes, so we don't have to do it inline from
959 * the hot IO path
960 */
961static void td_zone_gen_index(struct thread_data *td)
962{
963 int i;
964
965 td->zone_state_index = malloc(DDIR_RWDIR_CNT *
966 sizeof(struct zone_split_index *));
967
968 for (i = 0; i < DDIR_RWDIR_CNT; i++)
969 __td_zone_gen_index(td, i);
970}
971
972static int parse_zoned_distribution(struct thread_data *td, const char *input,
973 bool absolute)
974{
975 const char *pre = absolute ? "zoned_abs:" : "zoned:";
976 char *str, *p;
977 int i, ret = 0;
978
979 p = str = strdup(input);
980
981 strip_blank_front(&str);
982 strip_blank_end(str);
983
984 /* We expect it to start like that, bail if not */
985 if (strncmp(str, pre, strlen(pre))) {
986 log_err("fio: mismatch in zoned input <%s>\n", str);
987 free(p);
988 return 1;
989 }
990 str += strlen(pre);
991
992 ret = str_split_parse(td, str, zone_split_ddir, absolute);
993
994 free(p);
995
996 for (i = 0; i < DDIR_RWDIR_CNT; i++) {
997 int j;
998
999 dprint(FD_PARSE, "zone ddir %d (nr=%u): \n", i, td->o.zone_split_nr[i]);
1000
1001 for (j = 0; j < td->o.zone_split_nr[i]; j++) {
1002 struct zone_split *zsp = &td->o.zone_split[i][j];
1003
1004 if (absolute) {
1005 dprint(FD_PARSE, "\t%d: %u/%llu\n", j,
1006 zsp->access_perc,
1007 (unsigned long long) zsp->size);
1008 } else {
1009 dprint(FD_PARSE, "\t%d: %u/%u\n", j,
1010 zsp->access_perc,
1011 zsp->size_perc);
1012 }
1013 }
1014 }
1015
1016 if (parse_dryrun()) {
1017 int i;
1018
1019 for (i = 0; i < DDIR_RWDIR_CNT; i++) {
1020 free(td->o.zone_split[i]);
1021 td->o.zone_split[i] = NULL;
1022 td->o.zone_split_nr[i] = 0;
1023 }
1024
1025 return ret;
1026 }
1027
1028 if (!ret)
1029 td_zone_gen_index(td);
1030 else {
1031 for (i = 0; i < DDIR_RWDIR_CNT; i++)
1032 td->o.zone_split_nr[i] = 0;
1033 }
1034
1035 return ret;
1036}
1037
1038static int str_random_distribution_cb(void *data, const char *str)
1039{
1040 struct thread_data *td = cb_data_to_td(data);
1041 double val;
1042 char *nr;
1043
1044 if (td->o.random_distribution == FIO_RAND_DIST_ZIPF)
1045 val = FIO_DEF_ZIPF;
1046 else if (td->o.random_distribution == FIO_RAND_DIST_PARETO)
1047 val = FIO_DEF_PARETO;
1048 else if (td->o.random_distribution == FIO_RAND_DIST_GAUSS)
1049 val = 0.0;
1050 else if (td->o.random_distribution == FIO_RAND_DIST_ZONED)
1051 return parse_zoned_distribution(td, str, false);
1052 else if (td->o.random_distribution == FIO_RAND_DIST_ZONED_ABS)
1053 return parse_zoned_distribution(td, str, true);
1054 else
1055 return 0;
1056
1057 nr = get_opt_postfix(str);
1058 if (nr && !str_to_float(nr, &val, 0)) {
1059 log_err("fio: random postfix parsing failed\n");
1060 free(nr);
1061 return 1;
1062 }
1063
1064 free(nr);
1065
1066 if (td->o.random_distribution == FIO_RAND_DIST_ZIPF) {
1067 if (val == 1.00) {
1068 log_err("fio: zipf theta must different than 1.0\n");
1069 return 1;
1070 }
1071 if (parse_dryrun())
1072 return 0;
1073 td->o.zipf_theta.u.f = val;
1074 } else if (td->o.random_distribution == FIO_RAND_DIST_PARETO) {
1075 if (val <= 0.00 || val >= 1.00) {
1076 log_err("fio: pareto input out of range (0 < input < 1.0)\n");
1077 return 1;
1078 }
1079 if (parse_dryrun())
1080 return 0;
1081 td->o.pareto_h.u.f = val;
1082 } else {
1083 if (val < 0.00 || val >= 100.0) {
1084 log_err("fio: normal deviation out of range (0 <= input < 100.0)\n");
1085 return 1;
1086 }
1087 if (parse_dryrun())
1088 return 0;
1089 td->o.gauss_dev.u.f = val;
1090 }
1091
1092 return 0;
1093}
1094
1095static int str_steadystate_cb(void *data, const char *str)
1096{
1097 struct thread_data *td = cb_data_to_td(data);
1098 double val;
1099 char *nr;
1100 char *pct;
1101 long long ll;
1102
1103 if (td->o.ss_state != FIO_SS_IOPS && td->o.ss_state != FIO_SS_IOPS_SLOPE &&
1104 td->o.ss_state != FIO_SS_BW && td->o.ss_state != FIO_SS_BW_SLOPE) {
1105 /* should be impossible to get here */
1106 log_err("fio: unknown steady state criterion\n");
1107 return 1;
1108 }
1109
1110 nr = get_opt_postfix(str);
1111 if (!nr) {
1112 log_err("fio: steadystate threshold must be specified in addition to criterion\n");
1113 free(nr);
1114 return 1;
1115 }
1116
1117 /* ENHANCEMENT Allow fio to understand size=10.2% and use here */
1118 pct = strstr(nr, "%");
1119 if (pct) {
1120 *pct = '\0';
1121 strip_blank_end(nr);
1122 if (!str_to_float(nr, &val, 0)) {
1123 log_err("fio: could not parse steadystate threshold percentage\n");
1124 free(nr);
1125 return 1;
1126 }
1127
1128 dprint(FD_PARSE, "set steady state threshold to %f%%\n", val);
1129 free(nr);
1130 if (parse_dryrun())
1131 return 0;
1132
1133 td->o.ss_state |= FIO_SS_PCT;
1134 td->o.ss_limit.u.f = val;
1135 } else if (td->o.ss_state & FIO_SS_IOPS) {
1136 if (!str_to_float(nr, &val, 0)) {
1137 log_err("fio: steadystate IOPS threshold postfix parsing failed\n");
1138 free(nr);
1139 return 1;
1140 }
1141
1142 dprint(FD_PARSE, "set steady state IOPS threshold to %f\n", val);
1143 free(nr);
1144 if (parse_dryrun())
1145 return 0;
1146
1147 td->o.ss_limit.u.f = val;
1148 } else { /* bandwidth criterion */
1149 if (str_to_decimal(nr, &ll, 1, td, 0, 0)) {
1150 log_err("fio: steadystate BW threshold postfix parsing failed\n");
1151 free(nr);
1152 return 1;
1153 }
1154
1155 dprint(FD_PARSE, "set steady state BW threshold to %lld\n", ll);
1156 free(nr);
1157 if (parse_dryrun())
1158 return 0;
1159
1160 td->o.ss_limit.u.f = (double) ll;
1161 }
1162
1163 td->ss.state = td->o.ss_state;
1164 return 0;
1165}
1166
1167/*
1168 * Return next name in the string. Files are separated with ':'. If the ':'
1169 * is escaped with a '\', then that ':' is part of the filename and does not
1170 * indicate a new file.
1171 */
1172static char *get_next_name(char **ptr)
1173{
1174 char *str = *ptr;
1175 char *p, *start;
1176
1177 if (!str || !strlen(str))
1178 return NULL;
1179
1180 start = str;
1181 do {
1182 /*
1183 * No colon, we are done
1184 */
1185 p = strchr(str, ':');
1186 if (!p) {
1187 *ptr = NULL;
1188 break;
1189 }
1190
1191 /*
1192 * We got a colon, but it's the first character. Skip and
1193 * continue
1194 */
1195 if (p == start) {
1196 str = ++start;
1197 continue;
1198 }
1199
1200 if (*(p - 1) != '\\') {
1201 *p = '\0';
1202 *ptr = p + 1;
1203 break;
1204 }
1205
1206 memmove(p - 1, p, strlen(p) + 1);
1207 str = p;
1208 } while (1);
1209
1210 return start;
1211}
1212
1213
1214static int get_max_name_idx(char *input)
1215{
1216 unsigned int cur_idx;
1217 char *str, *p;
1218
1219 p = str = strdup(input);
1220 for (cur_idx = 0; ; cur_idx++)
1221 if (get_next_name(&str) == NULL)
1222 break;
1223
1224 free(p);
1225 return cur_idx;
1226}
1227
1228/*
1229 * Returns the directory at the index, indexes > entires will be
1230 * assigned via modulo division of the index
1231 */
1232int set_name_idx(char *target, size_t tlen, char *input, int index,
1233 bool unique_filename)
1234{
1235 unsigned int cur_idx;
1236 int len;
1237 char *fname, *str, *p;
1238
1239 p = str = strdup(input);
1240
1241 index %= get_max_name_idx(input);
1242 for (cur_idx = 0; cur_idx <= index; cur_idx++)
1243 fname = get_next_name(&str);
1244
1245 if (client_sockaddr_str[0] && unique_filename) {
1246 len = snprintf(target, tlen, "%s/%s.", fname,
1247 client_sockaddr_str);
1248 } else
1249 len = snprintf(target, tlen, "%s/", fname);
1250
1251 target[tlen - 1] = '\0';
1252 free(p);
1253
1254 return len;
1255}
1256
1257static int str_filename_cb(void *data, const char *input)
1258{
1259 struct thread_data *td = cb_data_to_td(data);
1260 char *fname, *str, *p;
1261
1262 p = str = strdup(input);
1263
1264 strip_blank_front(&str);
1265 strip_blank_end(str);
1266
1267 /*
1268 * Ignore what we may already have from nrfiles option.
1269 */
1270 if (!td->files_index)
1271 td->o.nr_files = 0;
1272
1273 while ((fname = get_next_name(&str)) != NULL) {
1274 if (!strlen(fname))
1275 break;
1276 add_file(td, fname, 0, 1);
1277 }
1278
1279 free(p);
1280 return 0;
1281}
1282
1283static int str_directory_cb(void *data, const char fio_unused *unused)
1284{
1285 struct thread_data *td = cb_data_to_td(data);
1286 struct stat sb;
1287 char *dirname, *str, *p;
1288 int ret = 0;
1289
1290 if (parse_dryrun())
1291 return 0;
1292
1293 p = str = strdup(td->o.directory);
1294 while ((dirname = get_next_name(&str)) != NULL) {
1295 if (lstat(dirname, &sb) < 0) {
1296 ret = errno;
1297
1298 log_err("fio: %s is not a directory\n", dirname);
1299 td_verror(td, ret, "lstat");
1300 goto out;
1301 }
1302 if (!S_ISDIR(sb.st_mode)) {
1303 log_err("fio: %s is not a directory\n", dirname);
1304 ret = 1;
1305 goto out;
1306 }
1307 }
1308
1309out:
1310 free(p);
1311 return ret;
1312}
1313
1314static int str_opendir_cb(void *data, const char fio_unused *str)
1315{
1316 struct thread_data *td = cb_data_to_td(data);
1317
1318 if (parse_dryrun())
1319 return 0;
1320
1321 if (!td->files_index)
1322 td->o.nr_files = 0;
1323
1324 return add_dir_files(td, td->o.opendir);
1325}
1326
1327static int str_buffer_pattern_cb(void *data, const char *input)
1328{
1329 struct thread_data *td = cb_data_to_td(data);
1330 int ret;
1331
1332 /* FIXME: for now buffer pattern does not support formats */
1333 ret = parse_and_fill_pattern(input, strlen(input), td->o.buffer_pattern,
1334 MAX_PATTERN_SIZE, NULL, 0, NULL, NULL);
1335 if (ret < 0)
1336 return 1;
1337
1338 assert(ret != 0);
1339 td->o.buffer_pattern_bytes = ret;
1340
1341 /*
1342 * If this job is doing any reading or has compression set,
1343 * ensure that we refill buffers for writes or we could be
1344 * invalidating the pattern through reads.
1345 */
1346 if (!td->o.compress_percentage && !td_read(td))
1347 td->o.refill_buffers = 0;
1348 else
1349 td->o.refill_buffers = 1;
1350
1351 td->o.scramble_buffers = 0;
1352 td->o.zero_buffers = 0;
1353
1354 return 0;
1355}
1356
1357static int str_buffer_compress_cb(void *data, unsigned long long *il)
1358{
1359 struct thread_data *td = cb_data_to_td(data);
1360
1361 td->flags |= TD_F_COMPRESS;
1362 td->o.compress_percentage = *il;
1363 return 0;
1364}
1365
1366static int str_dedupe_cb(void *data, unsigned long long *il)
1367{
1368 struct thread_data *td = cb_data_to_td(data);
1369
1370 td->flags |= TD_F_COMPRESS;
1371 td->o.dedupe_percentage = *il;
1372 td->o.refill_buffers = 1;
1373 return 0;
1374}
1375
1376static int str_verify_pattern_cb(void *data, const char *input)
1377{
1378 struct thread_data *td = cb_data_to_td(data);
1379 int ret;
1380
1381 td->o.verify_fmt_sz = ARRAY_SIZE(td->o.verify_fmt);
1382 ret = parse_and_fill_pattern(input, strlen(input), td->o.verify_pattern,
1383 MAX_PATTERN_SIZE, fmt_desc, sizeof(fmt_desc),
1384 td->o.verify_fmt, &td->o.verify_fmt_sz);
1385 if (ret < 0)
1386 return 1;
1387
1388 assert(ret != 0);
1389 td->o.verify_pattern_bytes = ret;
1390 /*
1391 * VERIFY_* could already be set
1392 */
1393 if (!fio_option_is_set(&td->o, verify))
1394 td->o.verify = VERIFY_PATTERN;
1395
1396 return 0;
1397}
1398
1399static int str_gtod_reduce_cb(void *data, int *il)
1400{
1401 struct thread_data *td = cb_data_to_td(data);
1402 int val = *il;
1403
1404 td->o.disable_lat = !!val;
1405 td->o.disable_clat = !!val;
1406 td->o.disable_slat = !!val;
1407 td->o.disable_bw = !!val;
1408 td->o.clat_percentiles = !val;
1409 if (val)
1410 td->ts_cache_mask = 63;
1411
1412 return 0;
1413}
1414
1415static int str_offset_cb(void *data, unsigned long long *__val)
1416{
1417 struct thread_data *td = cb_data_to_td(data);
1418 unsigned long long v = *__val;
1419
1420 if (parse_is_percent(v)) {
1421 td->o.start_offset = 0;
1422 td->o.start_offset_percent = -1ULL - v;
1423 dprint(FD_PARSE, "SET start_offset_percent %d\n",
1424 td->o.start_offset_percent);
1425 } else
1426 td->o.start_offset = v;
1427
1428 return 0;
1429}
1430
1431static int str_size_cb(void *data, unsigned long long *__val)
1432{
1433 struct thread_data *td = cb_data_to_td(data);
1434 unsigned long long v = *__val;
1435
1436 if (parse_is_percent(v)) {
1437 td->o.size = 0;
1438 td->o.size_percent = -1ULL - v;
1439 dprint(FD_PARSE, "SET size_percent %d\n",
1440 td->o.size_percent);
1441 } else
1442 td->o.size = v;
1443
1444 return 0;
1445}
1446
1447static int str_write_bw_log_cb(void *data, const char *str)
1448{
1449 struct thread_data *td = cb_data_to_td(data);
1450
1451 if (str)
1452 td->o.bw_log_file = strdup(str);
1453
1454 td->o.write_bw_log = 1;
1455 return 0;
1456}
1457
1458static int str_write_lat_log_cb(void *data, const char *str)
1459{
1460 struct thread_data *td = cb_data_to_td(data);
1461
1462 if (str)
1463 td->o.lat_log_file = strdup(str);
1464
1465 td->o.write_lat_log = 1;
1466 return 0;
1467}
1468
1469static int str_write_iops_log_cb(void *data, const char *str)
1470{
1471 struct thread_data *td = cb_data_to_td(data);
1472
1473 if (str)
1474 td->o.iops_log_file = strdup(str);
1475
1476 td->o.write_iops_log = 1;
1477 return 0;
1478}
1479
1480static int str_write_hist_log_cb(void *data, const char *str)
1481{
1482 struct thread_data *td = cb_data_to_td(data);
1483
1484 if (str)
1485 td->o.hist_log_file = strdup(str);
1486
1487 td->o.write_hist_log = 1;
1488 return 0;
1489}
1490
1491/*
1492 * str is supposed to be a substring of the strdup'd original string,
1493 * and is valid only if it's a regular file path.
1494 * This function keeps the pointer to the path as needed later.
1495 *
1496 * "external:/path/to/so\0" <- original pointer updated with strdup'd
1497 * "external\0" <- above pointer after parsed, i.e. ->ioengine
1498 * "/path/to/so\0" <- str argument, i.e. ->ioengine_so_path
1499 */
1500static int str_ioengine_external_cb(void *data, const char *str)
1501{
1502 struct thread_data *td = cb_data_to_td(data);
1503 struct stat sb;
1504 char *p;
1505
1506 if (!str) {
1507 log_err("fio: null external ioengine path\n");
1508 return 1;
1509 }
1510
1511 p = (char *)str; /* str is mutable */
1512 strip_blank_front(&p);
1513 strip_blank_end(p);
1514
1515 if (stat(p, &sb) || !S_ISREG(sb.st_mode)) {
1516 log_err("fio: invalid external ioengine path \"%s\"\n", p);
1517 return 1;
1518 }
1519
1520 td->o.ioengine_so_path = p;
1521 return 0;
1522}
1523
1524static int rw_verify(struct fio_option *o, void *data)
1525{
1526 struct thread_data *td = cb_data_to_td(data);
1527
1528 if (read_only && td_write(td)) {
1529 log_err("fio: job <%s> has write bit set, but fio is in"
1530 " read-only mode\n", td->o.name);
1531 return 1;
1532 }
1533
1534 return 0;
1535}
1536
1537static int gtod_cpu_verify(struct fio_option *o, void *data)
1538{
1539#ifndef FIO_HAVE_CPU_AFFINITY
1540 struct thread_data *td = cb_data_to_td(data);
1541
1542 if (td->o.gtod_cpu) {
1543 log_err("fio: platform must support CPU affinity for"
1544 "gettimeofday() offloading\n");
1545 return 1;
1546 }
1547#endif
1548
1549 return 0;
1550}
1551
1552/*
1553 * Map of job/command line options
1554 */
1555struct fio_option fio_options[FIO_MAX_OPTS] = {
1556 {
1557 .name = "description",
1558 .lname = "Description of job",
1559 .type = FIO_OPT_STR_STORE,
1560 .off1 = offsetof(struct thread_options, description),
1561 .help = "Text job description",
1562 .category = FIO_OPT_C_GENERAL,
1563 .group = FIO_OPT_G_DESC,
1564 },
1565 {
1566 .name = "name",
1567 .lname = "Job name",
1568 .type = FIO_OPT_STR_STORE,
1569 .off1 = offsetof(struct thread_options, name),
1570 .help = "Name of this job",
1571 .category = FIO_OPT_C_GENERAL,
1572 .group = FIO_OPT_G_DESC,
1573 },
1574 {
1575 .name = "wait_for",
1576 .lname = "Waitee name",
1577 .type = FIO_OPT_STR_STORE,
1578 .off1 = offsetof(struct thread_options, wait_for),
1579 .help = "Name of the job this one wants to wait for before starting",
1580 .category = FIO_OPT_C_GENERAL,
1581 .group = FIO_OPT_G_DESC,
1582 },
1583 {
1584 .name = "filename",
1585 .lname = "Filename(s)",
1586 .type = FIO_OPT_STR_STORE,
1587 .off1 = offsetof(struct thread_options, filename),
1588 .cb = str_filename_cb,
1589 .prio = -1, /* must come after "directory" */
1590 .help = "File(s) to use for the workload",
1591 .category = FIO_OPT_C_FILE,
1592 .group = FIO_OPT_G_FILENAME,
1593 },
1594 {
1595 .name = "directory",
1596 .lname = "Directory",
1597 .type = FIO_OPT_STR_STORE,
1598 .off1 = offsetof(struct thread_options, directory),
1599 .cb = str_directory_cb,
1600 .help = "Directory to store files in",
1601 .category = FIO_OPT_C_FILE,
1602 .group = FIO_OPT_G_FILENAME,
1603 },
1604 {
1605 .name = "filename_format",
1606 .lname = "Filename Format",
1607 .type = FIO_OPT_STR_STORE,
1608 .off1 = offsetof(struct thread_options, filename_format),
1609 .prio = -1, /* must come after "directory" */
1610 .help = "Override default $jobname.$jobnum.$filenum naming",
1611 .def = "$jobname.$jobnum.$filenum",
1612 .category = FIO_OPT_C_FILE,
1613 .group = FIO_OPT_G_FILENAME,
1614 },
1615 {
1616 .name = "unique_filename",
1617 .lname = "Unique Filename",
1618 .type = FIO_OPT_BOOL,
1619 .off1 = offsetof(struct thread_options, unique_filename),
1620 .help = "For network clients, prefix file with source IP",
1621 .def = "1",
1622 .category = FIO_OPT_C_FILE,
1623 .group = FIO_OPT_G_FILENAME,
1624 },
1625 {
1626 .name = "lockfile",
1627 .lname = "Lockfile",
1628 .type = FIO_OPT_STR,
1629 .off1 = offsetof(struct thread_options, file_lock_mode),
1630 .help = "Lock file when doing IO to it",
1631 .prio = 1,
1632 .parent = "filename",
1633 .hide = 0,
1634 .def = "none",
1635 .category = FIO_OPT_C_FILE,
1636 .group = FIO_OPT_G_FILENAME,
1637 .posval = {
1638 { .ival = "none",
1639 .oval = FILE_LOCK_NONE,
1640 .help = "No file locking",
1641 },
1642 { .ival = "exclusive",
1643 .oval = FILE_LOCK_EXCLUSIVE,
1644 .help = "Exclusive file lock",
1645 },
1646 {
1647 .ival = "readwrite",
1648 .oval = FILE_LOCK_READWRITE,
1649 .help = "Read vs write lock",
1650 },
1651 },
1652 },
1653 {
1654 .name = "opendir",
1655 .lname = "Open directory",
1656 .type = FIO_OPT_STR_STORE,
1657 .off1 = offsetof(struct thread_options, opendir),
1658 .cb = str_opendir_cb,
1659 .help = "Recursively add files from this directory and down",
1660 .category = FIO_OPT_C_FILE,
1661 .group = FIO_OPT_G_FILENAME,
1662 },
1663 {
1664 .name = "rw",
1665 .lname = "Read/write",
1666 .alias = "readwrite",
1667 .type = FIO_OPT_STR,
1668 .cb = str_rw_cb,
1669 .off1 = offsetof(struct thread_options, td_ddir),
1670 .help = "IO direction",
1671 .def = "read",
1672 .verify = rw_verify,
1673 .category = FIO_OPT_C_IO,
1674 .group = FIO_OPT_G_IO_BASIC,
1675 .posval = {
1676 { .ival = "read",
1677 .oval = TD_DDIR_READ,
1678 .help = "Sequential read",
1679 },
1680 { .ival = "write",
1681 .oval = TD_DDIR_WRITE,
1682 .help = "Sequential write",
1683 },
1684 { .ival = "trim",
1685 .oval = TD_DDIR_TRIM,
1686 .help = "Sequential trim",
1687 },
1688 { .ival = "randread",
1689 .oval = TD_DDIR_RANDREAD,
1690 .help = "Random read",
1691 },
1692 { .ival = "randwrite",
1693 .oval = TD_DDIR_RANDWRITE,
1694 .help = "Random write",
1695 },
1696 { .ival = "randtrim",
1697 .oval = TD_DDIR_RANDTRIM,
1698 .help = "Random trim",
1699 },
1700 { .ival = "rw",
1701 .oval = TD_DDIR_RW,
1702 .help = "Sequential read and write mix",
1703 },
1704 { .ival = "readwrite",
1705 .oval = TD_DDIR_RW,
1706 .help = "Sequential read and write mix",
1707 },
1708 { .ival = "randrw",
1709 .oval = TD_DDIR_RANDRW,
1710 .help = "Random read and write mix"
1711 },
1712 { .ival = "trimwrite",
1713 .oval = TD_DDIR_TRIMWRITE,
1714 .help = "Trim and write mix, trims preceding writes"
1715 },
1716 },
1717 },
1718 {
1719 .name = "rw_sequencer",
1720 .lname = "RW Sequencer",
1721 .type = FIO_OPT_STR,
1722 .off1 = offsetof(struct thread_options, rw_seq),
1723 .help = "IO offset generator modifier",
1724 .def = "sequential",
1725 .category = FIO_OPT_C_IO,
1726 .group = FIO_OPT_G_IO_BASIC,
1727 .posval = {
1728 { .ival = "sequential",
1729 .oval = RW_SEQ_SEQ,
1730 .help = "Generate sequential offsets",
1731 },
1732 { .ival = "identical",
1733 .oval = RW_SEQ_IDENT,
1734 .help = "Generate identical offsets",
1735 },
1736 },
1737 },
1738
1739 {
1740 .name = "ioengine",
1741 .lname = "IO Engine",
1742 .type = FIO_OPT_STR_STORE,
1743 .off1 = offsetof(struct thread_options, ioengine),
1744 .help = "IO engine to use",
1745 .def = FIO_PREFERRED_ENGINE,
1746 .category = FIO_OPT_C_IO,
1747 .group = FIO_OPT_G_IO_BASIC,
1748 .posval = {
1749 { .ival = "sync",
1750 .help = "Use read/write",
1751 },
1752 { .ival = "psync",
1753 .help = "Use pread/pwrite",
1754 },
1755 { .ival = "vsync",
1756 .help = "Use readv/writev",
1757 },
1758#ifdef CONFIG_PWRITEV
1759 { .ival = "pvsync",
1760 .help = "Use preadv/pwritev",
1761 },
1762#endif
1763#ifdef FIO_HAVE_PWRITEV2
1764 { .ival = "pvsync2",
1765 .help = "Use preadv2/pwritev2",
1766 },
1767#endif
1768#ifdef CONFIG_LIBAIO
1769 { .ival = "libaio",
1770 .help = "Linux native asynchronous IO",
1771 },
1772#endif
1773#ifdef CONFIG_POSIXAIO
1774 { .ival = "posixaio",
1775 .help = "POSIX asynchronous IO",
1776 },
1777#endif
1778#ifdef CONFIG_SOLARISAIO
1779 { .ival = "solarisaio",
1780 .help = "Solaris native asynchronous IO",
1781 },
1782#endif
1783#ifdef CONFIG_WINDOWSAIO
1784 { .ival = "windowsaio",
1785 .help = "Windows native asynchronous IO"
1786 },
1787#endif
1788#ifdef CONFIG_RBD
1789 { .ival = "rbd",
1790 .help = "Rados Block Device asynchronous IO"
1791 },
1792#endif
1793 { .ival = "mmap",
1794 .help = "Memory mapped IO"
1795 },
1796#ifdef CONFIG_LINUX_SPLICE
1797 { .ival = "splice",
1798 .help = "splice/vmsplice based IO",
1799 },
1800 { .ival = "netsplice",
1801 .help = "splice/vmsplice to/from the network",
1802 },
1803#endif
1804#ifdef FIO_HAVE_SGIO
1805 { .ival = "sg",
1806 .help = "SCSI generic v3 IO",
1807 },
1808#endif
1809 { .ival = "null",
1810 .help = "Testing engine (no data transfer)",
1811 },
1812 { .ival = "net",
1813 .help = "Network IO",
1814 },
1815 { .ival = "cpuio",
1816 .help = "CPU cycle burner engine",
1817 },
1818#ifdef CONFIG_GUASI
1819 { .ival = "guasi",
1820 .help = "GUASI IO engine",
1821 },
1822#endif
1823#ifdef FIO_HAVE_BINJECT
1824 { .ival = "binject",
1825 .help = "binject direct inject block engine",
1826 },
1827#endif
1828#ifdef CONFIG_RDMA
1829 { .ival = "rdma",
1830 .help = "RDMA IO engine",
1831 },
1832#endif
1833#ifdef CONFIG_FUSION_AW
1834 { .ival = "fusion-aw-sync",
1835 .help = "Fusion-io atomic write engine",
1836 },
1837#endif
1838#ifdef CONFIG_LINUX_EXT4_MOVE_EXTENT
1839 { .ival = "e4defrag",
1840 .help = "ext4 defrag engine",
1841 },
1842#endif
1843#ifdef CONFIG_LINUX_FALLOCATE
1844 { .ival = "falloc",
1845 .help = "fallocate() file based engine",
1846 },
1847#endif
1848#ifdef CONFIG_GFAPI
1849 { .ival = "gfapi",
1850 .help = "Glusterfs libgfapi(sync) based engine"
1851 },
1852 { .ival = "gfapi_async",
1853 .help = "Glusterfs libgfapi(async) based engine"
1854 },
1855#endif
1856#ifdef CONFIG_LIBHDFS
1857 { .ival = "libhdfs",
1858 .help = "Hadoop Distributed Filesystem (HDFS) engine"
1859 },
1860#endif
1861#ifdef CONFIG_PMEMBLK
1862 { .ival = "pmemblk",
1863 .help = "NVML libpmemblk based IO engine",
1864 },
1865
1866#endif
1867#ifdef CONFIG_LINUX_DEVDAX
1868 { .ival = "dev-dax",
1869 .help = "DAX Device based IO engine",
1870 },
1871#endif
1872 {
1873 .ival = "filecreate",
1874 .help = "File creation engine",
1875 },
1876 { .ival = "external",
1877 .help = "Load external engine (append name)",
1878 .cb = str_ioengine_external_cb,
1879 },
1880#ifdef CONFIG_LIBPMEM
1881 { .ival = "libpmem",
1882 .help = "NVML libpmem based IO engine",
1883 },
1884#endif
1885 },
1886 },
1887 {
1888 .name = "iodepth",
1889 .lname = "IO Depth",
1890 .type = FIO_OPT_INT,
1891 .off1 = offsetof(struct thread_options, iodepth),
1892 .help = "Number of IO buffers to keep in flight",
1893 .minval = 1,
1894 .interval = 1,
1895 .def = "1",
1896 .category = FIO_OPT_C_IO,
1897 .group = FIO_OPT_G_IO_BASIC,
1898 },
1899 {
1900 .name = "iodepth_batch",
1901 .lname = "IO Depth batch",
1902 .alias = "iodepth_batch_submit",
1903 .type = FIO_OPT_INT,
1904 .off1 = offsetof(struct thread_options, iodepth_batch),
1905 .help = "Number of IO buffers to submit in one go",
1906 .parent = "iodepth",
1907 .hide = 1,
1908 .interval = 1,
1909 .def = "1",
1910 .category = FIO_OPT_C_IO,
1911 .group = FIO_OPT_G_IO_BASIC,
1912 },
1913 {
1914 .name = "iodepth_batch_complete_min",
1915 .lname = "Min IO depth batch complete",
1916 .alias = "iodepth_batch_complete",
1917 .type = FIO_OPT_INT,
1918 .off1 = offsetof(struct thread_options, iodepth_batch_complete_min),
1919 .help = "Min number of IO buffers to retrieve in one go",
1920 .parent = "iodepth",
1921 .hide = 1,
1922 .minval = 0,
1923 .interval = 1,
1924 .def = "1",
1925 .category = FIO_OPT_C_IO,
1926 .group = FIO_OPT_G_IO_BASIC,
1927 },
1928 {
1929 .name = "iodepth_batch_complete_max",
1930 .lname = "Max IO depth batch complete",
1931 .type = FIO_OPT_INT,
1932 .off1 = offsetof(struct thread_options, iodepth_batch_complete_max),
1933 .help = "Max number of IO buffers to retrieve in one go",
1934 .parent = "iodepth",
1935 .hide = 1,
1936 .minval = 0,
1937 .interval = 1,
1938 .category = FIO_OPT_C_IO,
1939 .group = FIO_OPT_G_IO_BASIC,
1940 },
1941 {
1942 .name = "iodepth_low",
1943 .lname = "IO Depth batch low",
1944 .type = FIO_OPT_INT,
1945 .off1 = offsetof(struct thread_options, iodepth_low),
1946 .help = "Low water mark for queuing depth",
1947 .parent = "iodepth",
1948 .hide = 1,
1949 .interval = 1,
1950 .category = FIO_OPT_C_IO,
1951 .group = FIO_OPT_G_IO_BASIC,
1952 },
1953 {
1954 .name = "serialize_overlap",
1955 .lname = "Serialize overlap",
1956 .off1 = offsetof(struct thread_options, serialize_overlap),
1957 .type = FIO_OPT_BOOL,
1958 .help = "Wait for in-flight IOs that collide to complete",
1959 .parent = "iodepth",
1960 .def = "0",
1961 .category = FIO_OPT_C_IO,
1962 .group = FIO_OPT_G_IO_BASIC,
1963 },
1964 {
1965 .name = "io_submit_mode",
1966 .lname = "IO submit mode",
1967 .type = FIO_OPT_STR,
1968 .off1 = offsetof(struct thread_options, io_submit_mode),
1969 .help = "How IO submissions and completions are done",
1970 .def = "inline",
1971 .category = FIO_OPT_C_IO,
1972 .group = FIO_OPT_G_IO_BASIC,
1973 .posval = {
1974 { .ival = "inline",
1975 .oval = IO_MODE_INLINE,
1976 .help = "Submit and complete IO inline",
1977 },
1978 { .ival = "offload",
1979 .oval = IO_MODE_OFFLOAD,
1980 .help = "Offload submit and complete to threads",
1981 },
1982 },
1983 },
1984 {
1985 .name = "size",
1986 .lname = "Size",
1987 .type = FIO_OPT_STR_VAL,
1988 .cb = str_size_cb,
1989 .off1 = offsetof(struct thread_options, size),
1990 .help = "Total size of device or files",
1991 .interval = 1024 * 1024,
1992 .category = FIO_OPT_C_IO,
1993 .group = FIO_OPT_G_INVALID,
1994 },
1995 {
1996 .name = "io_size",
1997 .alias = "io_limit",
1998 .lname = "IO Size",
1999 .type = FIO_OPT_STR_VAL,
2000 .off1 = offsetof(struct thread_options, io_size),
2001 .help = "Total size of I/O to be performed",
2002 .interval = 1024 * 1024,
2003 .category = FIO_OPT_C_IO,
2004 .group = FIO_OPT_G_INVALID,
2005 },
2006 {
2007 .name = "fill_device",
2008 .lname = "Fill device",
2009 .alias = "fill_fs",
2010 .type = FIO_OPT_BOOL,
2011 .off1 = offsetof(struct thread_options, fill_device),
2012 .help = "Write until an ENOSPC error occurs",
2013 .def = "0",
2014 .category = FIO_OPT_C_FILE,
2015 .group = FIO_OPT_G_INVALID,
2016 },
2017 {
2018 .name = "filesize",
2019 .lname = "File size",
2020 .type = FIO_OPT_STR_VAL,
2021 .off1 = offsetof(struct thread_options, file_size_low),
2022 .off2 = offsetof(struct thread_options, file_size_high),
2023 .minval = 1,
2024 .help = "Size of individual files",
2025 .interval = 1024 * 1024,
2026 .category = FIO_OPT_C_FILE,
2027 .group = FIO_OPT_G_INVALID,
2028 },
2029 {
2030 .name = "file_append",
2031 .lname = "File append",
2032 .type = FIO_OPT_BOOL,
2033 .off1 = offsetof(struct thread_options, file_append),
2034 .help = "IO will start at the end of the file(s)",
2035 .def = "0",
2036 .category = FIO_OPT_C_FILE,
2037 .group = FIO_OPT_G_INVALID,
2038 },
2039 {
2040 .name = "offset",
2041 .lname = "IO offset",
2042 .alias = "fileoffset",
2043 .type = FIO_OPT_STR_VAL,
2044 .cb = str_offset_cb,
2045 .off1 = offsetof(struct thread_options, start_offset),
2046 .help = "Start IO from this offset",
2047 .def = "0",
2048 .interval = 1024 * 1024,
2049 .category = FIO_OPT_C_IO,
2050 .group = FIO_OPT_G_INVALID,
2051 },
2052 {
2053 .name = "offset_align",
2054 .lname = "IO offset alignment",
2055 .type = FIO_OPT_INT,
2056 .off1 = offsetof(struct thread_options, start_offset_align),
2057 .help = "Start IO from this offset alignment",
2058 .def = "0",
2059 .interval = 512,
2060 .category = FIO_OPT_C_IO,
2061 .group = FIO_OPT_G_INVALID,
2062 },
2063 {
2064 .name = "offset_increment",
2065 .lname = "IO offset increment",
2066 .type = FIO_OPT_STR_VAL,
2067 .off1 = offsetof(struct thread_options, offset_increment),
2068 .help = "What is the increment from one offset to the next",
2069 .parent = "offset",
2070 .hide = 1,
2071 .def = "0",
2072 .interval = 1024 * 1024,
2073 .category = FIO_OPT_C_IO,
2074 .group = FIO_OPT_G_INVALID,
2075 },
2076 {
2077 .name = "number_ios",
2078 .lname = "Number of IOs to perform",
2079 .type = FIO_OPT_STR_VAL,
2080 .off1 = offsetof(struct thread_options, number_ios),
2081 .help = "Force job completion after this number of IOs",
2082 .def = "0",
2083 .category = FIO_OPT_C_IO,
2084 .group = FIO_OPT_G_INVALID,
2085 },
2086 {
2087 .name = "bs",
2088 .lname = "Block size",
2089 .alias = "blocksize",
2090 .type = FIO_OPT_INT,
2091 .off1 = offsetof(struct thread_options, bs[DDIR_READ]),
2092 .off2 = offsetof(struct thread_options, bs[DDIR_WRITE]),
2093 .off3 = offsetof(struct thread_options, bs[DDIR_TRIM]),
2094 .minval = 1,
2095 .help = "Block size unit",
2096 .def = "4096",
2097 .parent = "rw",
2098 .hide = 1,
2099 .interval = 512,
2100 .category = FIO_OPT_C_IO,
2101 .group = FIO_OPT_G_INVALID,
2102 },
2103 {
2104 .name = "ba",
2105 .lname = "Block size align",
2106 .alias = "blockalign",
2107 .type = FIO_OPT_INT,
2108 .off1 = offsetof(struct thread_options, ba[DDIR_READ]),
2109 .off2 = offsetof(struct thread_options, ba[DDIR_WRITE]),
2110 .off3 = offsetof(struct thread_options, ba[DDIR_TRIM]),
2111 .minval = 1,
2112 .help = "IO block offset alignment",
2113 .parent = "rw",
2114 .hide = 1,
2115 .interval = 512,
2116 .category = FIO_OPT_C_IO,
2117 .group = FIO_OPT_G_INVALID,
2118 },
2119 {
2120 .name = "bsrange",
2121 .lname = "Block size range",
2122 .alias = "blocksize_range",
2123 .type = FIO_OPT_RANGE,
2124 .off1 = offsetof(struct thread_options, min_bs[DDIR_READ]),
2125 .off2 = offsetof(struct thread_options, max_bs[DDIR_READ]),
2126 .off3 = offsetof(struct thread_options, min_bs[DDIR_WRITE]),
2127 .off4 = offsetof(struct thread_options, max_bs[DDIR_WRITE]),
2128 .off5 = offsetof(struct thread_options, min_bs[DDIR_TRIM]),
2129 .off6 = offsetof(struct thread_options, max_bs[DDIR_TRIM]),
2130 .minval = 1,
2131 .help = "Set block size range (in more detail than bs)",
2132 .parent = "rw",
2133 .hide = 1,
2134 .interval = 4096,
2135 .category = FIO_OPT_C_IO,
2136 .group = FIO_OPT_G_INVALID,
2137 },
2138 {
2139 .name = "bssplit",
2140 .lname = "Block size split",
2141 .type = FIO_OPT_STR,
2142 .cb = str_bssplit_cb,
2143 .off1 = offsetof(struct thread_options, bssplit),
2144 .help = "Set a specific mix of block sizes",
2145 .parent = "rw",
2146 .hide = 1,
2147 .category = FIO_OPT_C_IO,
2148 .group = FIO_OPT_G_INVALID,
2149 },
2150 {
2151 .name = "bs_unaligned",
2152 .lname = "Block size unaligned",
2153 .alias = "blocksize_unaligned",
2154 .type = FIO_OPT_STR_SET,
2155 .off1 = offsetof(struct thread_options, bs_unaligned),
2156 .help = "Don't sector align IO buffer sizes",
2157 .parent = "rw",
2158 .hide = 1,
2159 .category = FIO_OPT_C_IO,
2160 .group = FIO_OPT_G_INVALID,
2161 },
2162 {
2163 .name = "bs_is_seq_rand",
2164 .lname = "Block size division is seq/random (not read/write)",
2165 .type = FIO_OPT_BOOL,
2166 .off1 = offsetof(struct thread_options, bs_is_seq_rand),
2167 .help = "Consider any blocksize setting to be sequential,random",
2168 .def = "0",
2169 .parent = "blocksize",
2170 .category = FIO_OPT_C_IO,
2171 .group = FIO_OPT_G_INVALID,
2172 },
2173 {
2174 .name = "randrepeat",
2175 .lname = "Random repeatable",
2176 .type = FIO_OPT_BOOL,
2177 .off1 = offsetof(struct thread_options, rand_repeatable),
2178 .help = "Use repeatable random IO pattern",
2179 .def = "1",
2180 .parent = "rw",
2181 .hide = 1,
2182 .category = FIO_OPT_C_IO,
2183 .group = FIO_OPT_G_RANDOM,
2184 },
2185 {
2186 .name = "randseed",
2187 .lname = "The random generator seed",
2188 .type = FIO_OPT_STR_VAL,
2189 .off1 = offsetof(struct thread_options, rand_seed),
2190 .help = "Set the random generator seed value",
2191 .def = "0x89",
2192 .parent = "rw",
2193 .category = FIO_OPT_C_IO,
2194 .group = FIO_OPT_G_RANDOM,
2195 },
2196 {
2197 .name = "use_os_rand",
2198 .lname = "Use OS random",
2199 .type = FIO_OPT_DEPRECATED,
2200 .off1 = offsetof(struct thread_options, dep_use_os_rand),
2201 .category = FIO_OPT_C_IO,
2202 .group = FIO_OPT_G_RANDOM,
2203 },
2204 {
2205 .name = "norandommap",
2206 .lname = "No randommap",
2207 .type = FIO_OPT_STR_SET,
2208 .off1 = offsetof(struct thread_options, norandommap),
2209 .help = "Accept potential duplicate random blocks",
2210 .parent = "rw",
2211 .hide = 1,
2212 .hide_on_set = 1,
2213 .category = FIO_OPT_C_IO,
2214 .group = FIO_OPT_G_RANDOM,
2215 },
2216 {
2217 .name = "softrandommap",
2218 .lname = "Soft randommap",
2219 .type = FIO_OPT_BOOL,
2220 .off1 = offsetof(struct thread_options, softrandommap),
2221 .help = "Set norandommap if randommap allocation fails",
2222 .parent = "norandommap",
2223 .hide = 1,
2224 .def = "0",
2225 .category = FIO_OPT_C_IO,
2226 .group = FIO_OPT_G_RANDOM,
2227 },
2228 {
2229 .name = "random_generator",
2230 .lname = "Random Generator",
2231 .type = FIO_OPT_STR,
2232 .off1 = offsetof(struct thread_options, random_generator),
2233 .help = "Type of random number generator to use",
2234 .def = "tausworthe",
2235 .posval = {
2236 { .ival = "tausworthe",
2237 .oval = FIO_RAND_GEN_TAUSWORTHE,
2238 .help = "Strong Tausworthe generator",
2239 },
2240 { .ival = "lfsr",
2241 .oval = FIO_RAND_GEN_LFSR,
2242 .help = "Variable length LFSR",
2243 },
2244 {
2245 .ival = "tausworthe64",
2246 .oval = FIO_RAND_GEN_TAUSWORTHE64,
2247 .help = "64-bit Tausworthe variant",
2248 },
2249 },
2250 .category = FIO_OPT_C_IO,
2251 .group = FIO_OPT_G_RANDOM,
2252 },
2253 {
2254 .name = "random_distribution",
2255 .lname = "Random Distribution",
2256 .type = FIO_OPT_STR,
2257 .off1 = offsetof(struct thread_options, random_distribution),
2258 .cb = str_random_distribution_cb,
2259 .help = "Random offset distribution generator",
2260 .def = "random",
2261 .posval = {
2262 { .ival = "random",
2263 .oval = FIO_RAND_DIST_RANDOM,
2264 .help = "Completely random",
2265 },
2266 { .ival = "zipf",
2267 .oval = FIO_RAND_DIST_ZIPF,
2268 .help = "Zipf distribution",
2269 },
2270 { .ival = "pareto",
2271 .oval = FIO_RAND_DIST_PARETO,
2272 .help = "Pareto distribution",
2273 },
2274 { .ival = "normal",
2275 .oval = FIO_RAND_DIST_GAUSS,
2276 .help = "Normal (Gaussian) distribution",
2277 },
2278 { .ival = "zoned",
2279 .oval = FIO_RAND_DIST_ZONED,
2280 .help = "Zoned random distribution",
2281 },
2282 { .ival = "zoned_abs",
2283 .oval = FIO_RAND_DIST_ZONED_ABS,
2284 .help = "Zoned absolute random distribution",
2285 },
2286 },
2287 .category = FIO_OPT_C_IO,
2288 .group = FIO_OPT_G_RANDOM,
2289 },
2290 {
2291 .name = "percentage_random",
2292 .lname = "Percentage Random",
2293 .type = FIO_OPT_INT,
2294 .off1 = offsetof(struct thread_options, perc_rand[DDIR_READ]),
2295 .off2 = offsetof(struct thread_options, perc_rand[DDIR_WRITE]),
2296 .off3 = offsetof(struct thread_options, perc_rand[DDIR_TRIM]),
2297 .maxval = 100,
2298 .help = "Percentage of seq/random mix that should be random",
2299 .def = "100,100,100",
2300 .interval = 5,
2301 .inverse = "percentage_sequential",
2302 .category = FIO_OPT_C_IO,
2303 .group = FIO_OPT_G_RANDOM,
2304 },
2305 {
2306 .name = "percentage_sequential",
2307 .lname = "Percentage Sequential",
2308 .type = FIO_OPT_DEPRECATED,
2309 .category = FIO_OPT_C_IO,
2310 .group = FIO_OPT_G_RANDOM,
2311 },
2312 {
2313 .name = "allrandrepeat",
2314 .lname = "All Random Repeat",
2315 .type = FIO_OPT_BOOL,
2316 .off1 = offsetof(struct thread_options, allrand_repeatable),
2317 .help = "Use repeatable random numbers for everything",
2318 .def = "0",
2319 .category = FIO_OPT_C_IO,
2320 .group = FIO_OPT_G_RANDOM,
2321 },
2322 {
2323 .name = "nrfiles",
2324 .lname = "Number of files",
2325 .alias = "nr_files",
2326 .type = FIO_OPT_INT,
2327 .off1 = offsetof(struct thread_options, nr_files),
2328 .help = "Split job workload between this number of files",
2329 .def = "1",
2330 .interval = 1,
2331 .category = FIO_OPT_C_FILE,
2332 .group = FIO_OPT_G_INVALID,
2333 },
2334 {
2335 .name = "openfiles",
2336 .lname = "Number of open files",
2337 .type = FIO_OPT_INT,
2338 .off1 = offsetof(struct thread_options, open_files),
2339 .help = "Number of files to keep open at the same time",
2340 .category = FIO_OPT_C_FILE,
2341 .group = FIO_OPT_G_INVALID,
2342 },
2343 {
2344 .name = "file_service_type",
2345 .lname = "File service type",
2346 .type = FIO_OPT_STR,
2347 .cb = str_fst_cb,
2348 .off1 = offsetof(struct thread_options, file_service_type),
2349 .help = "How to select which file to service next",
2350 .def = "roundrobin",
2351 .category = FIO_OPT_C_FILE,
2352 .group = FIO_OPT_G_INVALID,
2353 .posval = {
2354 { .ival = "random",
2355 .oval = FIO_FSERVICE_RANDOM,
2356 .help = "Choose a file at random (uniform)",
2357 },
2358 { .ival = "zipf",
2359 .oval = FIO_FSERVICE_ZIPF,
2360 .help = "Zipf randomized",
2361 },
2362 { .ival = "pareto",
2363 .oval = FIO_FSERVICE_PARETO,
2364 .help = "Pareto randomized",
2365 },
2366 { .ival = "normal",
2367 .oval = FIO_FSERVICE_GAUSS,
2368 .help = "Normal (Gaussian) randomized",
2369 },
2370 { .ival = "gauss",
2371 .oval = FIO_FSERVICE_GAUSS,
2372 .help = "Alias for normal",
2373 },
2374 { .ival = "roundrobin",
2375 .oval = FIO_FSERVICE_RR,
2376 .help = "Round robin select files",
2377 },
2378 { .ival = "sequential",
2379 .oval = FIO_FSERVICE_SEQ,
2380 .help = "Finish one file before moving to the next",
2381 },
2382 },
2383 .parent = "nrfiles",
2384 .hide = 1,
2385 },
2386#ifdef FIO_HAVE_ANY_FALLOCATE
2387 {
2388 .name = "fallocate",
2389 .lname = "Fallocate",
2390 .type = FIO_OPT_STR,
2391 .off1 = offsetof(struct thread_options, fallocate_mode),
2392 .help = "Whether pre-allocation is performed when laying out files",
2393 .def = "native",
2394 .category = FIO_OPT_C_FILE,
2395 .group = FIO_OPT_G_INVALID,
2396 .posval = {
2397 { .ival = "none",
2398 .oval = FIO_FALLOCATE_NONE,
2399 .help = "Do not pre-allocate space",
2400 },
2401 { .ival = "native",
2402 .oval = FIO_FALLOCATE_NATIVE,
2403 .help = "Use native pre-allocation if possible",
2404 },
2405#ifdef CONFIG_POSIX_FALLOCATE
2406 { .ival = "posix",
2407 .oval = FIO_FALLOCATE_POSIX,
2408 .help = "Use posix_fallocate()",
2409 },
2410#endif
2411#ifdef CONFIG_LINUX_FALLOCATE
2412 { .ival = "keep",
2413 .oval = FIO_FALLOCATE_KEEP_SIZE,
2414 .help = "Use fallocate(..., FALLOC_FL_KEEP_SIZE, ...)",
2415 },
2416#endif
2417 /* Compatibility with former boolean values */
2418 { .ival = "0",
2419 .oval = FIO_FALLOCATE_NONE,
2420 .help = "Alias for 'none'",
2421 },
2422#ifdef CONFIG_POSIX_FALLOCATE
2423 { .ival = "1",
2424 .oval = FIO_FALLOCATE_POSIX,
2425 .help = "Alias for 'posix'",
2426 },
2427#endif
2428 },
2429 },
2430#else /* FIO_HAVE_ANY_FALLOCATE */
2431 {
2432 .name = "fallocate",
2433 .lname = "Fallocate",
2434 .type = FIO_OPT_UNSUPPORTED,
2435 .help = "Your platform does not support fallocate",
2436 },
2437#endif /* FIO_HAVE_ANY_FALLOCATE */
2438 {
2439 .name = "fadvise_hint",
2440 .lname = "Fadvise hint",
2441 .type = FIO_OPT_STR,
2442 .off1 = offsetof(struct thread_options, fadvise_hint),
2443 .posval = {
2444 { .ival = "0",
2445 .oval = F_ADV_NONE,
2446 .help = "Don't issue fadvise/madvise",
2447 },
2448 { .ival = "1",
2449 .oval = F_ADV_TYPE,
2450 .help = "Advise using fio IO pattern",
2451 },
2452 { .ival = "random",
2453 .oval = F_ADV_RANDOM,
2454 .help = "Advise using FADV_RANDOM",
2455 },
2456 { .ival = "sequential",
2457 .oval = F_ADV_SEQUENTIAL,
2458 .help = "Advise using FADV_SEQUENTIAL",
2459 },
2460 },
2461 .help = "Use fadvise() to advise the kernel on IO pattern",
2462 .def = "1",
2463 .category = FIO_OPT_C_FILE,
2464 .group = FIO_OPT_G_INVALID,
2465 },
2466 {
2467 .name = "fsync",
2468 .lname = "Fsync",
2469 .type = FIO_OPT_INT,
2470 .off1 = offsetof(struct thread_options, fsync_blocks),
2471 .help = "Issue fsync for writes every given number of blocks",
2472 .def = "0",
2473 .interval = 1,
2474 .category = FIO_OPT_C_FILE,
2475 .group = FIO_OPT_G_INVALID,
2476 },
2477 {
2478 .name = "fdatasync",
2479 .lname = "Fdatasync",
2480 .type = FIO_OPT_INT,
2481 .off1 = offsetof(struct thread_options, fdatasync_blocks),
2482 .help = "Issue fdatasync for writes every given number of blocks",
2483 .def = "0",
2484 .interval = 1,
2485 .category = FIO_OPT_C_FILE,
2486 .group = FIO_OPT_G_INVALID,
2487 },
2488 {
2489 .name = "write_barrier",
2490 .lname = "Write barrier",
2491 .type = FIO_OPT_INT,
2492 .off1 = offsetof(struct thread_options, barrier_blocks),
2493 .help = "Make every Nth write a barrier write",
2494 .def = "0",
2495 .interval = 1,
2496 .category = FIO_OPT_C_IO,
2497 .group = FIO_OPT_G_INVALID,
2498 },
2499#ifdef CONFIG_SYNC_FILE_RANGE
2500 {
2501 .name = "sync_file_range",
2502 .lname = "Sync file range",
2503 .posval = {
2504 { .ival = "wait_before",
2505 .oval = SYNC_FILE_RANGE_WAIT_BEFORE,
2506 .help = "SYNC_FILE_RANGE_WAIT_BEFORE",
2507 .orval = 1,
2508 },
2509 { .ival = "write",
2510 .oval = SYNC_FILE_RANGE_WRITE,
2511 .help = "SYNC_FILE_RANGE_WRITE",
2512 .orval = 1,
2513 },
2514 {
2515 .ival = "wait_after",
2516 .oval = SYNC_FILE_RANGE_WAIT_AFTER,
2517 .help = "SYNC_FILE_RANGE_WAIT_AFTER",
2518 .orval = 1,
2519 },
2520 },
2521 .type = FIO_OPT_STR_MULTI,
2522 .cb = str_sfr_cb,
2523 .off1 = offsetof(struct thread_options, sync_file_range),
2524 .help = "Use sync_file_range()",
2525 .category = FIO_OPT_C_FILE,
2526 .group = FIO_OPT_G_INVALID,
2527 },
2528#else
2529 {
2530 .name = "sync_file_range",
2531 .lname = "Sync file range",
2532 .type = FIO_OPT_UNSUPPORTED,
2533 .help = "Your platform does not support sync_file_range",
2534 },
2535#endif
2536 {
2537 .name = "direct",
2538 .lname = "Direct I/O",
2539 .type = FIO_OPT_BOOL,
2540 .off1 = offsetof(struct thread_options, odirect),
2541 .help = "Use O_DIRECT IO (negates buffered)",
2542 .def = "0",
2543 .inverse = "buffered",
2544 .category = FIO_OPT_C_IO,
2545 .group = FIO_OPT_G_IO_TYPE,
2546 },
2547 {
2548 .name = "atomic",
2549 .lname = "Atomic I/O",
2550 .type = FIO_OPT_BOOL,
2551 .off1 = offsetof(struct thread_options, oatomic),
2552 .help = "Use Atomic IO with O_DIRECT (implies O_DIRECT)",
2553 .def = "0",
2554 .category = FIO_OPT_C_IO,
2555 .group = FIO_OPT_G_IO_TYPE,
2556 },
2557 {
2558 .name = "buffered",
2559 .lname = "Buffered I/O",
2560 .type = FIO_OPT_BOOL,
2561 .off1 = offsetof(struct thread_options, odirect),
2562 .neg = 1,
2563 .help = "Use buffered IO (negates direct)",
2564 .def = "1",
2565 .inverse = "direct",
2566 .category = FIO_OPT_C_IO,
2567 .group = FIO_OPT_G_IO_TYPE,
2568 },
2569 {
2570 .name = "overwrite",
2571 .lname = "Overwrite",
2572 .type = FIO_OPT_BOOL,
2573 .off1 = offsetof(struct thread_options, overwrite),
2574 .help = "When writing, set whether to overwrite current data",
2575 .def = "0",
2576 .category = FIO_OPT_C_FILE,
2577 .group = FIO_OPT_G_INVALID,
2578 },
2579 {
2580 .name = "loops",
2581 .lname = "Loops",
2582 .type = FIO_OPT_INT,
2583 .off1 = offsetof(struct thread_options, loops),
2584 .help = "Number of times to run the job",
2585 .def = "1",
2586 .interval = 1,
2587 .category = FIO_OPT_C_GENERAL,
2588 .group = FIO_OPT_G_RUNTIME,
2589 },
2590 {
2591 .name = "numjobs",
2592 .lname = "Number of jobs",
2593 .type = FIO_OPT_INT,
2594 .off1 = offsetof(struct thread_options, numjobs),
2595 .help = "Duplicate this job this many times",
2596 .def = "1",
2597 .interval = 1,
2598 .category = FIO_OPT_C_GENERAL,
2599 .group = FIO_OPT_G_RUNTIME,
2600 },
2601 {
2602 .name = "startdelay",
2603 .lname = "Start delay",
2604 .type = FIO_OPT_STR_VAL_TIME,
2605 .off1 = offsetof(struct thread_options, start_delay),
2606 .off2 = offsetof(struct thread_options, start_delay_high),
2607 .help = "Only start job when this period has passed",
2608 .def = "0",
2609 .is_seconds = 1,
2610 .is_time = 1,
2611 .category = FIO_OPT_C_GENERAL,
2612 .group = FIO_OPT_G_RUNTIME,
2613 },
2614 {
2615 .name = "runtime",
2616 .lname = "Runtime",
2617 .alias = "timeout",
2618 .type = FIO_OPT_STR_VAL_TIME,
2619 .off1 = offsetof(struct thread_options, timeout),
2620 .help = "Stop workload when this amount of time has passed",
2621 .def = "0",
2622 .is_seconds = 1,
2623 .is_time = 1,
2624 .category = FIO_OPT_C_GENERAL,
2625 .group = FIO_OPT_G_RUNTIME,
2626 },
2627 {
2628 .name = "time_based",
2629 .lname = "Time based",
2630 .type = FIO_OPT_STR_SET,
2631 .off1 = offsetof(struct thread_options, time_based),
2632 .help = "Keep running until runtime/timeout is met",
2633 .category = FIO_OPT_C_GENERAL,
2634 .group = FIO_OPT_G_RUNTIME,
2635 },
2636 {
2637 .name = "verify_only",
2638 .lname = "Verify only",
2639 .type = FIO_OPT_STR_SET,
2640 .off1 = offsetof(struct thread_options, verify_only),
2641 .help = "Verifies previously written data is still valid",
2642 .category = FIO_OPT_C_GENERAL,
2643 .group = FIO_OPT_G_RUNTIME,
2644 },
2645 {
2646 .name = "ramp_time",
2647 .lname = "Ramp time",
2648 .type = FIO_OPT_STR_VAL_TIME,
2649 .off1 = offsetof(struct thread_options, ramp_time),
2650 .help = "Ramp up time before measuring performance",
2651 .is_seconds = 1,
2652 .is_time = 1,
2653 .category = FIO_OPT_C_GENERAL,
2654 .group = FIO_OPT_G_RUNTIME,
2655 },
2656 {
2657 .name = "clocksource",
2658 .lname = "Clock source",
2659 .type = FIO_OPT_STR,
2660 .cb = fio_clock_source_cb,
2661 .off1 = offsetof(struct thread_options, clocksource),
2662 .help = "What type of timing source to use",
2663 .category = FIO_OPT_C_GENERAL,
2664 .group = FIO_OPT_G_CLOCK,
2665 .posval = {
2666#ifdef CONFIG_GETTIMEOFDAY
2667 { .ival = "gettimeofday",
2668 .oval = CS_GTOD,
2669 .help = "Use gettimeofday(2) for timing",
2670 },
2671#endif
2672#ifdef CONFIG_CLOCK_GETTIME
2673 { .ival = "clock_gettime",
2674 .oval = CS_CGETTIME,
2675 .help = "Use clock_gettime(2) for timing",
2676 },
2677#endif
2678#ifdef ARCH_HAVE_CPU_CLOCK
2679 { .ival = "cpu",
2680 .oval = CS_CPUCLOCK,
2681 .help = "Use CPU private clock",
2682 },
2683#endif
2684 },
2685 },
2686 {
2687 .name = "mem",
2688 .alias = "iomem",
2689 .lname = "I/O Memory",
2690 .type = FIO_OPT_STR,
2691 .cb = str_mem_cb,
2692 .off1 = offsetof(struct thread_options, mem_type),
2693 .help = "Backing type for IO buffers",
2694 .def = "malloc",
2695 .category = FIO_OPT_C_IO,
2696 .group = FIO_OPT_G_INVALID,
2697 .posval = {
2698 { .ival = "malloc",
2699 .oval = MEM_MALLOC,
2700 .help = "Use malloc(3) for IO buffers",
2701 },
2702#ifndef CONFIG_NO_SHM
2703 { .ival = "shm",
2704 .oval = MEM_SHM,
2705 .help = "Use shared memory segments for IO buffers",
2706 },
2707#ifdef FIO_HAVE_HUGETLB
2708 { .ival = "shmhuge",
2709 .oval = MEM_SHMHUGE,
2710 .help = "Like shm, but use huge pages",
2711 },
2712#endif
2713#endif
2714 { .ival = "mmap",
2715 .oval = MEM_MMAP,
2716 .help = "Use mmap(2) (file or anon) for IO buffers",
2717 },
2718 { .ival = "mmapshared",
2719 .oval = MEM_MMAPSHARED,
2720 .help = "Like mmap, but use the shared flag",
2721 },
2722#ifdef FIO_HAVE_HUGETLB
2723 { .ival = "mmaphuge",
2724 .oval = MEM_MMAPHUGE,
2725 .help = "Like mmap, but use huge pages",
2726 },
2727#endif
2728#ifdef CONFIG_CUDA
2729 { .ival = "cudamalloc",
2730 .oval = MEM_CUDA_MALLOC,
2731 .help = "Allocate GPU device memory for GPUDirect RDMA",
2732 },
2733#endif
2734 },
2735 },
2736 {
2737 .name = "iomem_align",
2738 .alias = "mem_align",
2739 .lname = "I/O memory alignment",
2740 .type = FIO_OPT_INT,
2741 .off1 = offsetof(struct thread_options, mem_align),
2742 .minval = 0,
2743 .help = "IO memory buffer offset alignment",
2744 .def = "0",
2745 .parent = "iomem",
2746 .hide = 1,
2747 .category = FIO_OPT_C_IO,
2748 .group = FIO_OPT_G_INVALID,
2749 },
2750 {
2751 .name = "verify",
2752 .lname = "Verify",
2753 .type = FIO_OPT_STR,
2754 .off1 = offsetof(struct thread_options, verify),
2755 .help = "Verify data written",
2756 .def = "0",
2757 .category = FIO_OPT_C_IO,
2758 .group = FIO_OPT_G_VERIFY,
2759 .posval = {
2760 { .ival = "0",
2761 .oval = VERIFY_NONE,
2762 .help = "Don't do IO verification",
2763 },
2764 { .ival = "md5",
2765 .oval = VERIFY_MD5,
2766 .help = "Use md5 checksums for verification",
2767 },
2768 { .ival = "crc64",
2769 .oval = VERIFY_CRC64,
2770 .help = "Use crc64 checksums for verification",
2771 },
2772 { .ival = "crc32",
2773 .oval = VERIFY_CRC32,
2774 .help = "Use crc32 checksums for verification",
2775 },
2776 { .ival = "crc32c-intel",
2777 .oval = VERIFY_CRC32C,
2778 .help = "Use crc32c checksums for verification (hw assisted, if available)",
2779 },
2780 { .ival = "crc32c",
2781 .oval = VERIFY_CRC32C,
2782 .help = "Use crc32c checksums for verification (hw assisted, if available)",
2783 },
2784 { .ival = "crc16",
2785 .oval = VERIFY_CRC16,
2786 .help = "Use crc16 checksums for verification",
2787 },
2788 { .ival = "crc7",
2789 .oval = VERIFY_CRC7,
2790 .help = "Use crc7 checksums for verification",
2791 },
2792 { .ival = "sha1",
2793 .oval = VERIFY_SHA1,
2794 .help = "Use sha1 checksums for verification",
2795 },
2796 { .ival = "sha256",
2797 .oval = VERIFY_SHA256,
2798 .help = "Use sha256 checksums for verification",
2799 },
2800 { .ival = "sha512",
2801 .oval = VERIFY_SHA512,
2802 .help = "Use sha512 checksums for verification",
2803 },
2804 { .ival = "sha3-224",
2805 .oval = VERIFY_SHA3_224,
2806 .help = "Use sha3-224 checksums for verification",
2807 },
2808 { .ival = "sha3-256",
2809 .oval = VERIFY_SHA3_256,
2810 .help = "Use sha3-256 checksums for verification",
2811 },
2812 { .ival = "sha3-384",
2813 .oval = VERIFY_SHA3_384,
2814 .help = "Use sha3-384 checksums for verification",
2815 },
2816 { .ival = "sha3-512",
2817 .oval = VERIFY_SHA3_512,
2818 .help = "Use sha3-512 checksums for verification",
2819 },
2820 { .ival = "xxhash",
2821 .oval = VERIFY_XXHASH,
2822 .help = "Use xxhash checksums for verification",
2823 },
2824 /* Meta information was included into verify_header,
2825 * 'meta' verification is implied by default. */
2826 { .ival = "meta",
2827 .oval = VERIFY_HDR_ONLY,
2828 .help = "Use io information for verification. "
2829 "Now is implied by default, thus option is obsolete, "
2830 "don't use it",
2831 },
2832 { .ival = "pattern",
2833 .oval = VERIFY_PATTERN_NO_HDR,
2834 .help = "Verify strict pattern",
2835 },
2836 {
2837 .ival = "null",
2838 .oval = VERIFY_NULL,
2839 .help = "Pretend to verify",
2840 },
2841 },
2842 },
2843 {
2844 .name = "do_verify",
2845 .lname = "Perform verify step",
2846 .type = FIO_OPT_BOOL,
2847 .off1 = offsetof(struct thread_options, do_verify),
2848 .help = "Run verification stage after write",
2849 .def = "1",
2850 .parent = "verify",
2851 .hide = 1,
2852 .category = FIO_OPT_C_IO,
2853 .group = FIO_OPT_G_VERIFY,
2854 },
2855 {
2856 .name = "verifysort",
2857 .lname = "Verify sort",
2858 .type = FIO_OPT_BOOL,
2859 .off1 = offsetof(struct thread_options, verifysort),
2860 .help = "Sort written verify blocks for read back",
2861 .def = "1",
2862 .parent = "verify",
2863 .hide = 1,
2864 .category = FIO_OPT_C_IO,
2865 .group = FIO_OPT_G_VERIFY,
2866 },
2867 {
2868 .name = "verifysort_nr",
2869 .lname = "Verify Sort Nr",
2870 .type = FIO_OPT_INT,
2871 .off1 = offsetof(struct thread_options, verifysort_nr),
2872 .help = "Pre-load and sort verify blocks for a read workload",
2873 .minval = 0,
2874 .maxval = 131072,
2875 .def = "1024",
2876 .parent = "verify",
2877 .category = FIO_OPT_C_IO,
2878 .group = FIO_OPT_G_VERIFY,
2879 },
2880 {
2881 .name = "verify_interval",
2882 .lname = "Verify interval",
2883 .type = FIO_OPT_INT,
2884 .off1 = offsetof(struct thread_options, verify_interval),
2885 .minval = 2 * sizeof(struct verify_header),
2886 .help = "Store verify buffer header every N bytes",
2887 .parent = "verify",
2888 .hide = 1,
2889 .interval = 2 * sizeof(struct verify_header),
2890 .category = FIO_OPT_C_IO,
2891 .group = FIO_OPT_G_VERIFY,
2892 },
2893 {
2894 .name = "verify_offset",
2895 .lname = "Verify offset",
2896 .type = FIO_OPT_INT,
2897 .help = "Offset verify header location by N bytes",
2898 .off1 = offsetof(struct thread_options, verify_offset),
2899 .minval = sizeof(struct verify_header),
2900 .parent = "verify",
2901 .hide = 1,
2902 .category = FIO_OPT_C_IO,
2903 .group = FIO_OPT_G_VERIFY,
2904 },
2905 {
2906 .name = "verify_pattern",
2907 .lname = "Verify pattern",
2908 .type = FIO_OPT_STR,
2909 .cb = str_verify_pattern_cb,
2910 .off1 = offsetof(struct thread_options, verify_pattern),
2911 .help = "Fill pattern for IO buffers",
2912 .parent = "verify",
2913 .hide = 1,
2914 .category = FIO_OPT_C_IO,
2915 .group = FIO_OPT_G_VERIFY,
2916 },
2917 {
2918 .name = "verify_fatal",
2919 .lname = "Verify fatal",
2920 .type = FIO_OPT_BOOL,
2921 .off1 = offsetof(struct thread_options, verify_fatal),
2922 .def = "0",
2923 .help = "Exit on a single verify failure, don't continue",
2924 .parent = "verify",
2925 .hide = 1,
2926 .category = FIO_OPT_C_IO,
2927 .group = FIO_OPT_G_VERIFY,
2928 },
2929 {
2930 .name = "verify_dump",
2931 .lname = "Verify dump",
2932 .type = FIO_OPT_BOOL,
2933 .off1 = offsetof(struct thread_options, verify_dump),
2934 .def = "0",
2935 .help = "Dump contents of good and bad blocks on failure",
2936 .parent = "verify",
2937 .hide = 1,
2938 .category = FIO_OPT_C_IO,
2939 .group = FIO_OPT_G_VERIFY,
2940 },
2941 {
2942 .name = "verify_async",
2943 .lname = "Verify asynchronously",
2944 .type = FIO_OPT_INT,
2945 .off1 = offsetof(struct thread_options, verify_async),
2946 .def = "0",
2947 .help = "Number of async verifier threads to use",
2948 .parent = "verify",
2949 .hide = 1,
2950 .category = FIO_OPT_C_IO,
2951 .group = FIO_OPT_G_VERIFY,
2952 },
2953 {
2954 .name = "verify_backlog",
2955 .lname = "Verify backlog",
2956 .type = FIO_OPT_STR_VAL,
2957 .off1 = offsetof(struct thread_options, verify_backlog),
2958 .help = "Verify after this number of blocks are written",
2959 .parent = "verify",
2960 .hide = 1,
2961 .category = FIO_OPT_C_IO,
2962 .group = FIO_OPT_G_VERIFY,
2963 },
2964 {
2965 .name = "verify_backlog_batch",
2966 .lname = "Verify backlog batch",
2967 .type = FIO_OPT_INT,
2968 .off1 = offsetof(struct thread_options, verify_batch),
2969 .help = "Verify this number of IO blocks",
2970 .parent = "verify",
2971 .hide = 1,
2972 .category = FIO_OPT_C_IO,
2973 .group = FIO_OPT_G_VERIFY,
2974 },
2975#ifdef FIO_HAVE_CPU_AFFINITY
2976 {
2977 .name = "verify_async_cpus",
2978 .lname = "Async verify CPUs",
2979 .type = FIO_OPT_STR,
2980 .cb = str_verify_cpus_allowed_cb,
2981 .off1 = offsetof(struct thread_options, verify_cpumask),
2982 .help = "Set CPUs allowed for async verify threads",
2983 .parent = "verify_async",
2984 .hide = 1,
2985 .category = FIO_OPT_C_IO,
2986 .group = FIO_OPT_G_VERIFY,
2987 },
2988#else
2989 {
2990 .name = "verify_async_cpus",
2991 .lname = "Async verify CPUs",
2992 .type = FIO_OPT_UNSUPPORTED,
2993 .help = "Your platform does not support CPU affinities",
2994 },
2995#endif
2996 {
2997 .name = "experimental_verify",
2998 .lname = "Experimental Verify",
2999 .off1 = offsetof(struct thread_options, experimental_verify),
3000 .type = FIO_OPT_BOOL,
3001 .help = "Enable experimental verification",
3002 .parent = "verify",
3003 .category = FIO_OPT_C_IO,
3004 .group = FIO_OPT_G_VERIFY,
3005 },
3006 {
3007 .name = "verify_state_load",
3008 .lname = "Load verify state",
3009 .off1 = offsetof(struct thread_options, verify_state),
3010 .type = FIO_OPT_BOOL,
3011 .help = "Load verify termination state",
3012 .parent = "verify",
3013 .category = FIO_OPT_C_IO,
3014 .group = FIO_OPT_G_VERIFY,
3015 },
3016 {
3017 .name = "verify_state_save",
3018 .lname = "Save verify state",
3019 .off1 = offsetof(struct thread_options, verify_state_save),
3020 .type = FIO_OPT_BOOL,
3021 .def = "1",
3022 .help = "Save verify state on termination",
3023 .parent = "verify",
3024 .category = FIO_OPT_C_IO,
3025 .group = FIO_OPT_G_VERIFY,
3026 },
3027#ifdef FIO_HAVE_TRIM
3028 {
3029 .name = "trim_percentage",
3030 .lname = "Trim percentage",
3031 .type = FIO_OPT_INT,
3032 .off1 = offsetof(struct thread_options, trim_percentage),
3033 .minval = 0,
3034 .maxval = 100,
3035 .help = "Number of verify blocks to trim (i.e., discard)",
3036 .parent = "verify",
3037 .def = "0",
3038 .interval = 1,
3039 .hide = 1,
3040 .category = FIO_OPT_C_IO,
3041 .group = FIO_OPT_G_TRIM,
3042 },
3043 {
3044 .name = "trim_verify_zero",
3045 .lname = "Verify trim zero",
3046 .type = FIO_OPT_BOOL,
3047 .help = "Verify that trimmed (i.e., discarded) blocks are returned as zeroes",
3048 .off1 = offsetof(struct thread_options, trim_zero),
3049 .parent = "trim_percentage",
3050 .hide = 1,
3051 .def = "1",
3052 .category = FIO_OPT_C_IO,
3053 .group = FIO_OPT_G_TRIM,
3054 },
3055 {
3056 .name = "trim_backlog",
3057 .lname = "Trim backlog",
3058 .type = FIO_OPT_STR_VAL,
3059 .off1 = offsetof(struct thread_options, trim_backlog),
3060 .help = "Trim after this number of blocks are written",
3061 .parent = "trim_percentage",
3062 .hide = 1,
3063 .interval = 1,
3064 .category = FIO_OPT_C_IO,
3065 .group = FIO_OPT_G_TRIM,
3066 },
3067 {
3068 .name = "trim_backlog_batch",
3069 .lname = "Trim backlog batch",
3070 .type = FIO_OPT_INT,
3071 .off1 = offsetof(struct thread_options, trim_batch),
3072 .help = "Trim this number of IO blocks",
3073 .parent = "trim_percentage",
3074 .hide = 1,
3075 .interval = 1,
3076 .category = FIO_OPT_C_IO,
3077 .group = FIO_OPT_G_TRIM,
3078 },
3079#else
3080 {
3081 .name = "trim_percentage",
3082 .lname = "Trim percentage",
3083 .type = FIO_OPT_UNSUPPORTED,
3084 .help = "Fio does not support TRIM on your platform",
3085 },
3086 {
3087 .name = "trim_verify_zero",
3088 .lname = "Verify trim zero",
3089 .type = FIO_OPT_UNSUPPORTED,
3090 .help = "Fio does not support TRIM on your platform",
3091 },
3092 {
3093 .name = "trim_backlog",
3094 .lname = "Trim backlog",
3095 .type = FIO_OPT_UNSUPPORTED,
3096 .help = "Fio does not support TRIM on your platform",
3097 },
3098 {
3099 .name = "trim_backlog_batch",
3100 .lname = "Trim backlog batch",
3101 .type = FIO_OPT_UNSUPPORTED,
3102 .help = "Fio does not support TRIM on your platform",
3103 },
3104#endif
3105 {
3106 .name = "write_iolog",
3107 .lname = "Write I/O log",
3108 .type = FIO_OPT_STR_STORE,
3109 .off1 = offsetof(struct thread_options, write_iolog_file),
3110 .help = "Store IO pattern to file",
3111 .category = FIO_OPT_C_IO,
3112 .group = FIO_OPT_G_IOLOG,
3113 },
3114 {
3115 .name = "read_iolog",
3116 .lname = "Read I/O log",
3117 .type = FIO_OPT_STR_STORE,
3118 .off1 = offsetof(struct thread_options, read_iolog_file),
3119 .help = "Playback IO pattern from file",
3120 .category = FIO_OPT_C_IO,
3121 .group = FIO_OPT_G_IOLOG,
3122 },
3123 {
3124 .name = "replay_no_stall",
3125 .lname = "Don't stall on replay",
3126 .type = FIO_OPT_BOOL,
3127 .off1 = offsetof(struct thread_options, no_stall),
3128 .def = "0",
3129 .parent = "read_iolog",
3130 .hide = 1,
3131 .help = "Playback IO pattern file as fast as possible without stalls",
3132 .category = FIO_OPT_C_IO,
3133 .group = FIO_OPT_G_IOLOG,
3134 },
3135 {
3136 .name = "replay_redirect",
3137 .lname = "Redirect device for replay",
3138 .type = FIO_OPT_STR_STORE,
3139 .off1 = offsetof(struct thread_options, replay_redirect),
3140 .parent = "read_iolog",
3141 .hide = 1,
3142 .help = "Replay all I/O onto this device, regardless of trace device",
3143 .category = FIO_OPT_C_IO,
3144 .group = FIO_OPT_G_IOLOG,
3145 },
3146 {
3147 .name = "replay_scale",
3148 .lname = "Replace offset scale factor",
3149 .type = FIO_OPT_INT,
3150 .off1 = offsetof(struct thread_options, replay_scale),
3151 .parent = "read_iolog",
3152 .def = "1",
3153 .help = "Align offsets to this blocksize",
3154 .category = FIO_OPT_C_IO,
3155 .group = FIO_OPT_G_IOLOG,
3156 },
3157 {
3158 .name = "replay_align",
3159 .lname = "Replace alignment",
3160 .type = FIO_OPT_INT,
3161 .off1 = offsetof(struct thread_options, replay_align),
3162 .parent = "read_iolog",
3163 .help = "Scale offset down by this factor",
3164 .category = FIO_OPT_C_IO,
3165 .group = FIO_OPT_G_IOLOG,
3166 .pow2 = 1,
3167 },
3168 {
3169 .name = "exec_prerun",
3170 .lname = "Pre-execute runnable",
3171 .type = FIO_OPT_STR_STORE,
3172 .off1 = offsetof(struct thread_options, exec_prerun),
3173 .help = "Execute this file prior to running job",
3174 .category = FIO_OPT_C_GENERAL,
3175 .group = FIO_OPT_G_INVALID,
3176 },
3177 {
3178 .name = "exec_postrun",
3179 .lname = "Post-execute runnable",
3180 .type = FIO_OPT_STR_STORE,
3181 .off1 = offsetof(struct thread_options, exec_postrun),
3182 .help = "Execute this file after running job",
3183 .category = FIO_OPT_C_GENERAL,
3184 .group = FIO_OPT_G_INVALID,
3185 },
3186#ifdef FIO_HAVE_IOSCHED_SWITCH
3187 {
3188 .name = "ioscheduler",
3189 .lname = "I/O scheduler",
3190 .type = FIO_OPT_STR_STORE,
3191 .off1 = offsetof(struct thread_options, ioscheduler),
3192 .help = "Use this IO scheduler on the backing device",
3193 .category = FIO_OPT_C_FILE,
3194 .group = FIO_OPT_G_INVALID,
3195 },
3196#else
3197 {
3198 .name = "ioscheduler",
3199 .lname = "I/O scheduler",
3200 .type = FIO_OPT_UNSUPPORTED,
3201 .help = "Your platform does not support IO scheduler switching",
3202 },
3203#endif
3204 {
3205 .name = "zonesize",
3206 .lname = "Zone size",
3207 .type = FIO_OPT_STR_VAL,
3208 .off1 = offsetof(struct thread_options, zone_size),
3209 .help = "Amount of data to read per zone",
3210 .def = "0",
3211 .interval = 1024 * 1024,
3212 .category = FIO_OPT_C_IO,
3213 .group = FIO_OPT_G_ZONE,
3214 },
3215 {
3216 .name = "zonerange",
3217 .lname = "Zone range",
3218 .type = FIO_OPT_STR_VAL,
3219 .off1 = offsetof(struct thread_options, zone_range),
3220 .help = "Give size of an IO zone",
3221 .def = "0",
3222 .interval = 1024 * 1024,
3223 .category = FIO_OPT_C_IO,
3224 .group = FIO_OPT_G_ZONE,
3225 },
3226 {
3227 .name = "zoneskip",
3228 .lname = "Zone skip",
3229 .type = FIO_OPT_STR_VAL,
3230 .off1 = offsetof(struct thread_options, zone_skip),
3231 .help = "Space between IO zones",
3232 .def = "0",
3233 .interval = 1024 * 1024,
3234 .category = FIO_OPT_C_IO,
3235 .group = FIO_OPT_G_ZONE,
3236 },
3237 {
3238 .name = "lockmem",
3239 .lname = "Lock memory",
3240 .type = FIO_OPT_STR_VAL,
3241 .off1 = offsetof(struct thread_options, lockmem),
3242 .help = "Lock down this amount of memory (per worker)",
3243 .def = "0",
3244 .interval = 1024 * 1024,
3245 .category = FIO_OPT_C_GENERAL,
3246 .group = FIO_OPT_G_INVALID,
3247 },
3248 {
3249 .name = "rwmixread",
3250 .lname = "Read/write mix read",
3251 .type = FIO_OPT_INT,
3252 .cb = str_rwmix_read_cb,
3253 .off1 = offsetof(struct thread_options, rwmix[DDIR_READ]),
3254 .maxval = 100,
3255 .help = "Percentage of mixed workload that is reads",
3256 .def = "50",
3257 .interval = 5,
3258 .inverse = "rwmixwrite",
3259 .category = FIO_OPT_C_IO,
3260 .group = FIO_OPT_G_RWMIX,
3261 },
3262 {
3263 .name = "rwmixwrite",
3264 .lname = "Read/write mix write",
3265 .type = FIO_OPT_INT,
3266 .cb = str_rwmix_write_cb,
3267 .off1 = offsetof(struct thread_options, rwmix[DDIR_WRITE]),
3268 .maxval = 100,
3269 .help = "Percentage of mixed workload that is writes",
3270 .def = "50",
3271 .interval = 5,
3272 .inverse = "rwmixread",
3273 .category = FIO_OPT_C_IO,
3274 .group = FIO_OPT_G_RWMIX,
3275 },
3276 {
3277 .name = "rwmixcycle",
3278 .lname = "Read/write mix cycle",
3279 .type = FIO_OPT_DEPRECATED,
3280 .category = FIO_OPT_C_IO,
3281 .group = FIO_OPT_G_RWMIX,
3282 },
3283 {
3284 .name = "nice",
3285 .lname = "Nice",
3286 .type = FIO_OPT_INT,
3287 .off1 = offsetof(struct thread_options, nice),
3288 .help = "Set job CPU nice value",
3289 .minval = -20,
3290 .maxval = 19,
3291 .def = "0",
3292 .interval = 1,
3293 .category = FIO_OPT_C_GENERAL,
3294 .group = FIO_OPT_G_CRED,
3295 },
3296#ifdef FIO_HAVE_IOPRIO
3297 {
3298 .name = "prio",
3299 .lname = "I/O nice priority",
3300 .type = FIO_OPT_INT,
3301 .off1 = offsetof(struct thread_options, ioprio),
3302 .help = "Set job IO priority value",
3303 .minval = IOPRIO_MIN_PRIO,
3304 .maxval = IOPRIO_MAX_PRIO,
3305 .interval = 1,
3306 .category = FIO_OPT_C_GENERAL,
3307 .group = FIO_OPT_G_CRED,
3308 },
3309#else
3310 {
3311 .name = "prio",
3312 .lname = "I/O nice priority",
3313 .type = FIO_OPT_UNSUPPORTED,
3314 .help = "Your platform does not support IO priorities",
3315 },
3316#endif
3317#ifdef FIO_HAVE_IOPRIO_CLASS
3318#ifndef FIO_HAVE_IOPRIO
3319#error "FIO_HAVE_IOPRIO_CLASS requires FIO_HAVE_IOPRIO"
3320#endif
3321 {
3322 .name = "prioclass",
3323 .lname = "I/O nice priority class",
3324 .type = FIO_OPT_INT,
3325 .off1 = offsetof(struct thread_options, ioprio_class),
3326 .help = "Set job IO priority class",
3327 .minval = IOPRIO_MIN_PRIO_CLASS,
3328 .maxval = IOPRIO_MAX_PRIO_CLASS,
3329 .interval = 1,
3330 .category = FIO_OPT_C_GENERAL,
3331 .group = FIO_OPT_G_CRED,
3332 },
3333#else
3334 {
3335 .name = "prioclass",
3336 .lname = "I/O nice priority class",
3337 .type = FIO_OPT_UNSUPPORTED,
3338 .help = "Your platform does not support IO priority classes",
3339 },
3340#endif
3341 {
3342 .name = "thinktime",
3343 .lname = "Thinktime",
3344 .type = FIO_OPT_INT,
3345 .off1 = offsetof(struct thread_options, thinktime),
3346 .help = "Idle time between IO buffers (usec)",
3347 .def = "0",
3348 .is_time = 1,
3349 .category = FIO_OPT_C_IO,
3350 .group = FIO_OPT_G_THINKTIME,
3351 },
3352 {
3353 .name = "thinktime_spin",
3354 .lname = "Thinktime spin",
3355 .type = FIO_OPT_INT,
3356 .off1 = offsetof(struct thread_options, thinktime_spin),
3357 .help = "Start think time by spinning this amount (usec)",
3358 .def = "0",
3359 .is_time = 1,
3360 .parent = "thinktime",
3361 .hide = 1,
3362 .category = FIO_OPT_C_IO,
3363 .group = FIO_OPT_G_THINKTIME,
3364 },
3365 {
3366 .name = "thinktime_blocks",
3367 .lname = "Thinktime blocks",
3368 .type = FIO_OPT_INT,
3369 .off1 = offsetof(struct thread_options, thinktime_blocks),
3370 .help = "IO buffer period between 'thinktime'",
3371 .def = "1",
3372 .parent = "thinktime",
3373 .hide = 1,
3374 .category = FIO_OPT_C_IO,
3375 .group = FIO_OPT_G_THINKTIME,
3376 },
3377 {
3378 .name = "rate",
3379 .lname = "I/O rate",
3380 .type = FIO_OPT_INT,
3381 .off1 = offsetof(struct thread_options, rate[DDIR_READ]),
3382 .off2 = offsetof(struct thread_options, rate[DDIR_WRITE]),
3383 .off3 = offsetof(struct thread_options, rate[DDIR_TRIM]),
3384 .help = "Set bandwidth rate",
3385 .category = FIO_OPT_C_IO,
3386 .group = FIO_OPT_G_RATE,
3387 },
3388 {
3389 .name = "rate_min",
3390 .alias = "ratemin",
3391 .lname = "I/O min rate",
3392 .type = FIO_OPT_INT,
3393 .off1 = offsetof(struct thread_options, ratemin[DDIR_READ]),
3394 .off2 = offsetof(struct thread_options, ratemin[DDIR_WRITE]),
3395 .off3 = offsetof(struct thread_options, ratemin[DDIR_TRIM]),
3396 .help = "Job must meet this rate or it will be shutdown",
3397 .parent = "rate",
3398 .hide = 1,
3399 .category = FIO_OPT_C_IO,
3400 .group = FIO_OPT_G_RATE,
3401 },
3402 {
3403 .name = "rate_iops",
3404 .lname = "I/O rate IOPS",
3405 .type = FIO_OPT_INT,
3406 .off1 = offsetof(struct thread_options, rate_iops[DDIR_READ]),
3407 .off2 = offsetof(struct thread_options, rate_iops[DDIR_WRITE]),
3408 .off3 = offsetof(struct thread_options, rate_iops[DDIR_TRIM]),
3409 .help = "Limit IO used to this number of IO operations/sec",
3410 .hide = 1,
3411 .category = FIO_OPT_C_IO,
3412 .group = FIO_OPT_G_RATE,
3413 },
3414 {
3415 .name = "rate_iops_min",
3416 .lname = "I/O min rate IOPS",
3417 .type = FIO_OPT_INT,
3418 .off1 = offsetof(struct thread_options, rate_iops_min[DDIR_READ]),
3419 .off2 = offsetof(struct thread_options, rate_iops_min[DDIR_WRITE]),
3420 .off3 = offsetof(struct thread_options, rate_iops_min[DDIR_TRIM]),
3421 .help = "Job must meet this rate or it will be shut down",
3422 .parent = "rate_iops",
3423 .hide = 1,
3424 .category = FIO_OPT_C_IO,
3425 .group = FIO_OPT_G_RATE,
3426 },
3427 {
3428 .name = "rate_process",
3429 .lname = "Rate Process",
3430 .type = FIO_OPT_STR,
3431 .off1 = offsetof(struct thread_options, rate_process),
3432 .help = "What process controls how rated IO is managed",
3433 .def = "linear",
3434 .category = FIO_OPT_C_IO,
3435 .group = FIO_OPT_G_RATE,
3436 .posval = {
3437 { .ival = "linear",
3438 .oval = RATE_PROCESS_LINEAR,
3439 .help = "Linear rate of IO",
3440 },
3441 {
3442 .ival = "poisson",
3443 .oval = RATE_PROCESS_POISSON,
3444 .help = "Rate follows Poisson process",
3445 },
3446 },
3447 .parent = "rate",
3448 },
3449 {
3450 .name = "rate_cycle",
3451 .alias = "ratecycle",
3452 .lname = "I/O rate cycle",
3453 .type = FIO_OPT_INT,
3454 .off1 = offsetof(struct thread_options, ratecycle),
3455 .help = "Window average for rate limits (msec)",
3456 .def = "1000",
3457 .parent = "rate",
3458 .hide = 1,
3459 .category = FIO_OPT_C_IO,
3460 .group = FIO_OPT_G_RATE,
3461 },
3462 {
3463 .name = "rate_ignore_thinktime",
3464 .lname = "Rate ignore thinktime",
3465 .type = FIO_OPT_BOOL,
3466 .off1 = offsetof(struct thread_options, rate_ign_think),
3467 .help = "Rated IO ignores thinktime settings",
3468 .parent = "rate",
3469 .category = FIO_OPT_C_IO,
3470 .group = FIO_OPT_G_RATE,
3471 },
3472 {
3473 .name = "max_latency",
3474 .lname = "Max Latency (usec)",
3475 .type = FIO_OPT_STR_VAL_TIME,
3476 .off1 = offsetof(struct thread_options, max_latency),
3477 .help = "Maximum tolerated IO latency (usec)",
3478 .is_time = 1,
3479 .category = FIO_OPT_C_IO,
3480 .group = FIO_OPT_G_LATPROF,
3481 },
3482 {
3483 .name = "latency_target",
3484 .lname = "Latency Target (usec)",
3485 .type = FIO_OPT_STR_VAL_TIME,
3486 .off1 = offsetof(struct thread_options, latency_target),
3487 .help = "Ramp to max queue depth supporting this latency",
3488 .is_time = 1,
3489 .category = FIO_OPT_C_IO,
3490 .group = FIO_OPT_G_LATPROF,
3491 },
3492 {
3493 .name = "latency_window",
3494 .lname = "Latency Window (usec)",
3495 .type = FIO_OPT_STR_VAL_TIME,
3496 .off1 = offsetof(struct thread_options, latency_window),
3497 .help = "Time to sustain latency_target",
3498 .is_time = 1,
3499 .category = FIO_OPT_C_IO,
3500 .group = FIO_OPT_G_LATPROF,
3501 },
3502 {
3503 .name = "latency_percentile",
3504 .lname = "Latency Percentile",
3505 .type = FIO_OPT_FLOAT_LIST,
3506 .off1 = offsetof(struct thread_options, latency_percentile),
3507 .help = "Percentile of IOs must be below latency_target",
3508 .def = "100",
3509 .maxlen = 1,
3510 .minfp = 0.0,
3511 .maxfp = 100.0,
3512 .category = FIO_OPT_C_IO,
3513 .group = FIO_OPT_G_LATPROF,
3514 },
3515 {
3516 .name = "invalidate",
3517 .lname = "Cache invalidate",
3518 .type = FIO_OPT_BOOL,
3519 .off1 = offsetof(struct thread_options, invalidate_cache),
3520 .help = "Invalidate buffer/page cache prior to running job",
3521 .def = "1",
3522 .category = FIO_OPT_C_IO,
3523 .group = FIO_OPT_G_IO_TYPE,
3524 },
3525 {
3526 .name = "sync",
3527 .lname = "Synchronous I/O",
3528 .type = FIO_OPT_BOOL,
3529 .off1 = offsetof(struct thread_options, sync_io),
3530 .help = "Use O_SYNC for buffered writes",
3531 .def = "0",
3532 .parent = "buffered",
3533 .hide = 1,
3534 .category = FIO_OPT_C_IO,
3535 .group = FIO_OPT_G_IO_TYPE,
3536 },
3537#ifdef FIO_HAVE_WRITE_HINT
3538 {
3539 .name = "write_hint",
3540 .lname = "Write hint",
3541 .type = FIO_OPT_STR,
3542 .off1 = offsetof(struct thread_options, write_hint),
3543 .help = "Set expected write life time",
3544 .category = FIO_OPT_C_ENGINE,
3545 .group = FIO_OPT_G_INVALID,
3546 .posval = {
3547 { .ival = "none",
3548 .oval = RWH_WRITE_LIFE_NONE,
3549 },
3550 { .ival = "short",
3551 .oval = RWH_WRITE_LIFE_SHORT,
3552 },
3553 { .ival = "medium",
3554 .oval = RWH_WRITE_LIFE_MEDIUM,
3555 },
3556 { .ival = "long",
3557 .oval = RWH_WRITE_LIFE_LONG,
3558 },
3559 { .ival = "extreme",
3560 .oval = RWH_WRITE_LIFE_EXTREME,
3561 },
3562 },
3563 },
3564#endif
3565 {
3566 .name = "create_serialize",
3567 .lname = "Create serialize",
3568 .type = FIO_OPT_BOOL,
3569 .off1 = offsetof(struct thread_options, create_serialize),
3570 .help = "Serialize creation of job files",
3571 .def = "1",
3572 .category = FIO_OPT_C_FILE,
3573 .group = FIO_OPT_G_INVALID,
3574 },
3575 {
3576 .name = "create_fsync",
3577 .lname = "Create fsync",
3578 .type = FIO_OPT_BOOL,
3579 .off1 = offsetof(struct thread_options, create_fsync),
3580 .help = "fsync file after creation",
3581 .def = "1",
3582 .category = FIO_OPT_C_FILE,
3583 .group = FIO_OPT_G_INVALID,
3584 },
3585 {
3586 .name = "create_on_open",
3587 .lname = "Create on open",
3588 .type = FIO_OPT_BOOL,
3589 .off1 = offsetof(struct thread_options, create_on_open),
3590 .help = "Create files when they are opened for IO",
3591 .def = "0",
3592 .category = FIO_OPT_C_FILE,
3593 .group = FIO_OPT_G_INVALID,
3594 },
3595 {
3596 .name = "create_only",
3597 .lname = "Create Only",
3598 .type = FIO_OPT_BOOL,
3599 .off1 = offsetof(struct thread_options, create_only),
3600 .help = "Only perform file creation phase",
3601 .category = FIO_OPT_C_FILE,
3602 .def = "0",
3603 },
3604 {
3605 .name = "allow_file_create",
3606 .lname = "Allow file create",
3607 .type = FIO_OPT_BOOL,
3608 .off1 = offsetof(struct thread_options, allow_create),
3609 .help = "Permit fio to create files, if they don't exist",
3610 .def = "1",
3611 .category = FIO_OPT_C_FILE,
3612 .group = FIO_OPT_G_FILENAME,
3613 },
3614 {
3615 .name = "allow_mounted_write",
3616 .lname = "Allow mounted write",
3617 .type = FIO_OPT_BOOL,
3618 .off1 = offsetof(struct thread_options, allow_mounted_write),
3619 .help = "Allow writes to a mounted partition",
3620 .def = "0",
3621 .category = FIO_OPT_C_FILE,
3622 .group = FIO_OPT_G_FILENAME,
3623 },
3624 {
3625 .name = "pre_read",
3626 .lname = "Pre-read files",
3627 .type = FIO_OPT_BOOL,
3628 .off1 = offsetof(struct thread_options, pre_read),
3629 .help = "Pre-read files before starting official testing",
3630 .def = "0",
3631 .category = FIO_OPT_C_FILE,
3632 .group = FIO_OPT_G_INVALID,
3633 },
3634#ifdef FIO_HAVE_CPU_AFFINITY
3635 {
3636 .name = "cpumask",
3637 .lname = "CPU mask",
3638 .type = FIO_OPT_INT,
3639 .cb = str_cpumask_cb,
3640 .off1 = offsetof(struct thread_options, cpumask),
3641 .help = "CPU affinity mask",
3642 .category = FIO_OPT_C_GENERAL,
3643 .group = FIO_OPT_G_CRED,
3644 },
3645 {
3646 .name = "cpus_allowed",
3647 .lname = "CPUs allowed",
3648 .type = FIO_OPT_STR,
3649 .cb = str_cpus_allowed_cb,
3650 .off1 = offsetof(struct thread_options, cpumask),
3651 .help = "Set CPUs allowed",
3652 .category = FIO_OPT_C_GENERAL,
3653 .group = FIO_OPT_G_CRED,
3654 },
3655 {
3656 .name = "cpus_allowed_policy",
3657 .lname = "CPUs allowed distribution policy",
3658 .type = FIO_OPT_STR,
3659 .off1 = offsetof(struct thread_options, cpus_allowed_policy),
3660 .help = "Distribution policy for cpus_allowed",
3661 .parent = "cpus_allowed",
3662 .prio = 1,
3663 .posval = {
3664 { .ival = "shared",
3665 .oval = FIO_CPUS_SHARED,
3666 .help = "Mask shared between threads",
3667 },
3668 { .ival = "split",
3669 .oval = FIO_CPUS_SPLIT,
3670 .help = "Mask split between threads",
3671 },
3672 },
3673 .category = FIO_OPT_C_GENERAL,
3674 .group = FIO_OPT_G_CRED,
3675 },
3676#else
3677 {
3678 .name = "cpumask",
3679 .lname = "CPU mask",
3680 .type = FIO_OPT_UNSUPPORTED,
3681 .help = "Your platform does not support CPU affinities",
3682 },
3683 {
3684 .name = "cpus_allowed",
3685 .lname = "CPUs allowed",
3686 .type = FIO_OPT_UNSUPPORTED,
3687 .help = "Your platform does not support CPU affinities",
3688 },
3689 {
3690 .name = "cpus_allowed_policy",
3691 .lname = "CPUs allowed distribution policy",
3692 .type = FIO_OPT_UNSUPPORTED,
3693 .help = "Your platform does not support CPU affinities",
3694 },
3695#endif
3696#ifdef CONFIG_LIBNUMA
3697 {
3698 .name = "numa_cpu_nodes",
3699 .lname = "NUMA CPU Nodes",
3700 .type = FIO_OPT_STR,
3701 .cb = str_numa_cpunodes_cb,
3702 .off1 = offsetof(struct thread_options, numa_cpunodes),
3703 .help = "NUMA CPU nodes bind",
3704 .category = FIO_OPT_C_GENERAL,
3705 .group = FIO_OPT_G_INVALID,
3706 },
3707 {
3708 .name = "numa_mem_policy",
3709 .lname = "NUMA Memory Policy",
3710 .type = FIO_OPT_STR,
3711 .cb = str_numa_mpol_cb,
3712 .off1 = offsetof(struct thread_options, numa_memnodes),
3713 .help = "NUMA memory policy setup",
3714 .category = FIO_OPT_C_GENERAL,
3715 .group = FIO_OPT_G_INVALID,
3716 },
3717#else
3718 {
3719 .name = "numa_cpu_nodes",
3720 .lname = "NUMA CPU Nodes",
3721 .type = FIO_OPT_UNSUPPORTED,
3722 .help = "Build fio with libnuma-dev(el) to enable this option",
3723 },
3724 {
3725 .name = "numa_mem_policy",
3726 .lname = "NUMA Memory Policy",
3727 .type = FIO_OPT_UNSUPPORTED,
3728 .help = "Build fio with libnuma-dev(el) to enable this option",
3729 },
3730#endif
3731#ifdef CONFIG_CUDA
3732 {
3733 .name = "gpu_dev_id",
3734 .lname = "GPU device ID",
3735 .type = FIO_OPT_INT,
3736 .off1 = offsetof(struct thread_options, gpu_dev_id),
3737 .help = "Set GPU device ID for GPUDirect RDMA",
3738 .def = "0",
3739 .category = FIO_OPT_C_GENERAL,
3740 .group = FIO_OPT_G_INVALID,
3741 },
3742#endif
3743 {
3744 .name = "end_fsync",
3745 .lname = "End fsync",
3746 .type = FIO_OPT_BOOL,
3747 .off1 = offsetof(struct thread_options, end_fsync),
3748 .help = "Include fsync at the end of job",
3749 .def = "0",
3750 .category = FIO_OPT_C_FILE,
3751 .group = FIO_OPT_G_INVALID,
3752 },
3753 {
3754 .name = "fsync_on_close",
3755 .lname = "Fsync on close",
3756 .type = FIO_OPT_BOOL,
3757 .off1 = offsetof(struct thread_options, fsync_on_close),
3758 .help = "fsync files on close",
3759 .def = "0",
3760 .category = FIO_OPT_C_FILE,
3761 .group = FIO_OPT_G_INVALID,
3762 },
3763 {
3764 .name = "unlink",
3765 .lname = "Unlink file",
3766 .type = FIO_OPT_BOOL,
3767 .off1 = offsetof(struct thread_options, unlink),
3768 .help = "Unlink created files after job has completed",
3769 .def = "0",
3770 .category = FIO_OPT_C_FILE,
3771 .group = FIO_OPT_G_INVALID,
3772 },
3773 {
3774 .name = "unlink_each_loop",
3775 .lname = "Unlink file after each loop of a job",
3776 .type = FIO_OPT_BOOL,
3777 .off1 = offsetof(struct thread_options, unlink_each_loop),
3778 .help = "Unlink created files after each loop in a job has completed",
3779 .def = "0",
3780 .category = FIO_OPT_C_FILE,
3781 .group = FIO_OPT_G_INVALID,
3782 },
3783 {
3784 .name = "exitall",
3785 .lname = "Exit-all on terminate",
3786 .type = FIO_OPT_STR_SET,
3787 .cb = str_exitall_cb,
3788 .help = "Terminate all jobs when one exits",
3789 .category = FIO_OPT_C_GENERAL,
3790 .group = FIO_OPT_G_PROCESS,
3791 },
3792 {
3793 .name = "exitall_on_error",
3794 .lname = "Exit-all on terminate in error",
3795 .type = FIO_OPT_STR_SET,
3796 .off1 = offsetof(struct thread_options, exitall_error),
3797 .help = "Terminate all jobs when one exits in error",
3798 .category = FIO_OPT_C_GENERAL,
3799 .group = FIO_OPT_G_PROCESS,
3800 },
3801 {
3802 .name = "stonewall",
3803 .lname = "Wait for previous",
3804 .alias = "wait_for_previous",
3805 .type = FIO_OPT_STR_SET,
3806 .off1 = offsetof(struct thread_options, stonewall),
3807 .help = "Insert a hard barrier between this job and previous",
3808 .category = FIO_OPT_C_GENERAL,
3809 .group = FIO_OPT_G_PROCESS,
3810 },
3811 {
3812 .name = "new_group",
3813 .lname = "New group",
3814 .type = FIO_OPT_STR_SET,
3815 .off1 = offsetof(struct thread_options, new_group),
3816 .help = "Mark the start of a new group (for reporting)",
3817 .category = FIO_OPT_C_GENERAL,
3818 .group = FIO_OPT_G_PROCESS,
3819 },
3820 {
3821 .name = "thread",
3822 .lname = "Thread",
3823 .type = FIO_OPT_STR_SET,
3824 .off1 = offsetof(struct thread_options, use_thread),
3825 .help = "Use threads instead of processes",
3826#ifdef CONFIG_NO_SHM
3827 .def = "1",
3828 .no_warn_def = 1,
3829#endif
3830 .category = FIO_OPT_C_GENERAL,
3831 .group = FIO_OPT_G_PROCESS,
3832 },
3833 {
3834 .name = "per_job_logs",
3835 .lname = "Per Job Logs",
3836 .type = FIO_OPT_BOOL,
3837 .off1 = offsetof(struct thread_options, per_job_logs),
3838 .help = "Include job number in generated log files or not",
3839 .def = "1",
3840 .category = FIO_OPT_C_LOG,
3841 .group = FIO_OPT_G_INVALID,
3842 },
3843 {
3844 .name = "write_bw_log",
3845 .lname = "Write bandwidth log",
3846 .type = FIO_OPT_STR,
3847 .off1 = offsetof(struct thread_options, bw_log_file),
3848 .cb = str_write_bw_log_cb,
3849 .help = "Write log of bandwidth during run",
3850 .category = FIO_OPT_C_LOG,
3851 .group = FIO_OPT_G_INVALID,
3852 },
3853 {
3854 .name = "write_lat_log",
3855 .lname = "Write latency log",
3856 .type = FIO_OPT_STR,
3857 .off1 = offsetof(struct thread_options, lat_log_file),
3858 .cb = str_write_lat_log_cb,
3859 .help = "Write log of latency during run",
3860 .category = FIO_OPT_C_LOG,
3861 .group = FIO_OPT_G_INVALID,
3862 },
3863 {
3864 .name = "write_iops_log",
3865 .lname = "Write IOPS log",
3866 .type = FIO_OPT_STR,
3867 .off1 = offsetof(struct thread_options, iops_log_file),
3868 .cb = str_write_iops_log_cb,
3869 .help = "Write log of IOPS during run",
3870 .category = FIO_OPT_C_LOG,
3871 .group = FIO_OPT_G_INVALID,
3872 },
3873 {
3874 .name = "log_avg_msec",
3875 .lname = "Log averaging (msec)",
3876 .type = FIO_OPT_INT,
3877 .off1 = offsetof(struct thread_options, log_avg_msec),
3878 .help = "Average bw/iops/lat logs over this period of time",
3879 .def = "0",
3880 .category = FIO_OPT_C_LOG,
3881 .group = FIO_OPT_G_INVALID,
3882 },
3883 {
3884 .name = "log_hist_msec",
3885 .lname = "Log histograms (msec)",
3886 .type = FIO_OPT_INT,
3887 .off1 = offsetof(struct thread_options, log_hist_msec),
3888 .help = "Dump completion latency histograms at frequency of this time value",
3889 .def = "0",
3890 .category = FIO_OPT_C_LOG,
3891 .group = FIO_OPT_G_INVALID,
3892 },
3893 {
3894 .name = "log_hist_coarseness",
3895 .lname = "Histogram logs coarseness",
3896 .type = FIO_OPT_INT,
3897 .off1 = offsetof(struct thread_options, log_hist_coarseness),
3898 .help = "Integer in range [0,6]. Higher coarseness outputs"
3899 " fewer histogram bins per sample. The number of bins for"
3900 " these are [1216, 608, 304, 152, 76, 38, 19] respectively.",
3901 .def = "0",
3902 .category = FIO_OPT_C_LOG,
3903 .group = FIO_OPT_G_INVALID,
3904 },
3905 {
3906 .name = "write_hist_log",
3907 .lname = "Write latency histogram logs",
3908 .type = FIO_OPT_STR,
3909 .off1 = offsetof(struct thread_options, hist_log_file),
3910 .cb = str_write_hist_log_cb,
3911 .help = "Write log of latency histograms during run",
3912 .category = FIO_OPT_C_LOG,
3913 .group = FIO_OPT_G_INVALID,
3914 },
3915 {
3916 .name = "log_max_value",
3917 .lname = "Log maximum instead of average",
3918 .type = FIO_OPT_BOOL,
3919 .off1 = offsetof(struct thread_options, log_max),
3920 .help = "Log max sample in a window instead of average",
3921 .def = "0",
3922 .category = FIO_OPT_C_LOG,
3923 .group = FIO_OPT_G_INVALID,
3924 },
3925 {
3926 .name = "log_offset",
3927 .lname = "Log offset of IO",
3928 .type = FIO_OPT_BOOL,
3929 .off1 = offsetof(struct thread_options, log_offset),
3930 .help = "Include offset of IO for each log entry",
3931 .def = "0",
3932 .category = FIO_OPT_C_LOG,
3933 .group = FIO_OPT_G_INVALID,
3934 },
3935#ifdef CONFIG_ZLIB
3936 {
3937 .name = "log_compression",
3938 .lname = "Log compression",
3939 .type = FIO_OPT_INT,
3940 .off1 = offsetof(struct thread_options, log_gz),
3941 .help = "Log in compressed chunks of this size",
3942 .minval = 1024ULL,
3943 .maxval = 512 * 1024 * 1024ULL,
3944 .category = FIO_OPT_C_LOG,
3945 .group = FIO_OPT_G_INVALID,
3946 },
3947#ifdef FIO_HAVE_CPU_AFFINITY
3948 {
3949 .name = "log_compression_cpus",
3950 .lname = "Log Compression CPUs",
3951 .type = FIO_OPT_STR,
3952 .cb = str_log_cpus_allowed_cb,
3953 .off1 = offsetof(struct thread_options, log_gz_cpumask),
3954 .parent = "log_compression",
3955 .help = "Limit log compression to these CPUs",
3956 .category = FIO_OPT_C_LOG,
3957 .group = FIO_OPT_G_INVALID,
3958 },
3959#else
3960 {
3961 .name = "log_compression_cpus",
3962 .lname = "Log Compression CPUs",
3963 .type = FIO_OPT_UNSUPPORTED,
3964 .help = "Your platform does not support CPU affinities",
3965 },
3966#endif
3967 {
3968 .name = "log_store_compressed",
3969 .lname = "Log store compressed",
3970 .type = FIO_OPT_BOOL,
3971 .off1 = offsetof(struct thread_options, log_gz_store),
3972 .help = "Store logs in a compressed format",
3973 .category = FIO_OPT_C_LOG,
3974 .group = FIO_OPT_G_INVALID,
3975 },
3976#else
3977 {
3978 .name = "log_compression",
3979 .lname = "Log compression",
3980 .type = FIO_OPT_UNSUPPORTED,
3981 .help = "Install libz-dev(el) to get compression support",
3982 },
3983 {
3984 .name = "log_store_compressed",
3985 .lname = "Log store compressed",
3986 .type = FIO_OPT_UNSUPPORTED,
3987 .help = "Install libz-dev(el) to get compression support",
3988 },
3989#endif
3990 {
3991 .name = "log_unix_epoch",
3992 .lname = "Log epoch unix",
3993 .type = FIO_OPT_BOOL,
3994 .off1 = offsetof(struct thread_options, log_unix_epoch),
3995 .help = "Use Unix time in log files",
3996 .category = FIO_OPT_C_LOG,
3997 .group = FIO_OPT_G_INVALID,
3998 },
3999 {
4000 .name = "block_error_percentiles",
4001 .lname = "Block error percentiles",
4002 .type = FIO_OPT_BOOL,
4003 .off1 = offsetof(struct thread_options, block_error_hist),
4004 .help = "Record trim block errors and make a histogram",
4005 .def = "0",
4006 .category = FIO_OPT_C_LOG,
4007 .group = FIO_OPT_G_INVALID,
4008 },
4009 {
4010 .name = "bwavgtime",
4011 .lname = "Bandwidth average time",
4012 .type = FIO_OPT_INT,
4013 .off1 = offsetof(struct thread_options, bw_avg_time),
4014 .help = "Time window over which to calculate bandwidth"
4015 " (msec)",
4016 .def = "500",
4017 .parent = "write_bw_log",
4018 .hide = 1,
4019 .interval = 100,
4020 .category = FIO_OPT_C_LOG,
4021 .group = FIO_OPT_G_INVALID,
4022 },
4023 {
4024 .name = "iopsavgtime",
4025 .lname = "IOPS average time",
4026 .type = FIO_OPT_INT,
4027 .off1 = offsetof(struct thread_options, iops_avg_time),
4028 .help = "Time window over which to calculate IOPS (msec)",
4029 .def = "500",
4030 .parent = "write_iops_log",
4031 .hide = 1,
4032 .interval = 100,
4033 .category = FIO_OPT_C_LOG,
4034 .group = FIO_OPT_G_INVALID,
4035 },
4036 {
4037 .name = "group_reporting",
4038 .lname = "Group reporting",
4039 .type = FIO_OPT_STR_SET,
4040 .off1 = offsetof(struct thread_options, group_reporting),
4041 .help = "Do reporting on a per-group basis",
4042 .category = FIO_OPT_C_STAT,
4043 .group = FIO_OPT_G_INVALID,
4044 },
4045 {
4046 .name = "stats",
4047 .lname = "Stats",
4048 .type = FIO_OPT_BOOL,
4049 .off1 = offsetof(struct thread_options, stats),
4050 .help = "Enable collection of stats",
4051 .def = "1",
4052 .category = FIO_OPT_C_STAT,
4053 .group = FIO_OPT_G_INVALID,
4054 },
4055 {
4056 .name = "zero_buffers",
4057 .lname = "Zero I/O buffers",
4058 .type = FIO_OPT_STR_SET,
4059 .off1 = offsetof(struct thread_options, zero_buffers),
4060 .help = "Init IO buffers to all zeroes",
4061 .category = FIO_OPT_C_IO,
4062 .group = FIO_OPT_G_IO_BUF,
4063 },
4064 {
4065 .name = "refill_buffers",
4066 .lname = "Refill I/O buffers",
4067 .type = FIO_OPT_STR_SET,
4068 .off1 = offsetof(struct thread_options, refill_buffers),
4069 .help = "Refill IO buffers on every IO submit",
4070 .category = FIO_OPT_C_IO,
4071 .group = FIO_OPT_G_IO_BUF,
4072 },
4073 {
4074 .name = "scramble_buffers",
4075 .lname = "Scramble I/O buffers",
4076 .type = FIO_OPT_BOOL,
4077 .off1 = offsetof(struct thread_options, scramble_buffers),
4078 .help = "Slightly scramble buffers on every IO submit",
4079 .def = "1",
4080 .category = FIO_OPT_C_IO,
4081 .group = FIO_OPT_G_IO_BUF,
4082 },
4083 {
4084 .name = "buffer_pattern",
4085 .lname = "Buffer pattern",
4086 .type = FIO_OPT_STR,
4087 .cb = str_buffer_pattern_cb,
4088 .off1 = offsetof(struct thread_options, buffer_pattern),
4089 .help = "Fill pattern for IO buffers",
4090 .category = FIO_OPT_C_IO,
4091 .group = FIO_OPT_G_IO_BUF,
4092 },
4093 {
4094 .name = "buffer_compress_percentage",
4095 .lname = "Buffer compression percentage",
4096 .type = FIO_OPT_INT,
4097 .cb = str_buffer_compress_cb,
4098 .off1 = offsetof(struct thread_options, compress_percentage),
4099 .maxval = 100,
4100 .minval = 0,
4101 .help = "How compressible the buffer is (approximately)",
4102 .interval = 5,
4103 .category = FIO_OPT_C_IO,
4104 .group = FIO_OPT_G_IO_BUF,
4105 },
4106 {
4107 .name = "buffer_compress_chunk",
4108 .lname = "Buffer compression chunk size",
4109 .type = FIO_OPT_INT,
4110 .off1 = offsetof(struct thread_options, compress_chunk),
4111 .parent = "buffer_compress_percentage",
4112 .hide = 1,
4113 .help = "Size of compressible region in buffer",
4114 .def = "512",
4115 .interval = 256,
4116 .category = FIO_OPT_C_IO,
4117 .group = FIO_OPT_G_IO_BUF,
4118 },
4119 {
4120 .name = "dedupe_percentage",
4121 .lname = "Dedupe percentage",
4122 .type = FIO_OPT_INT,
4123 .cb = str_dedupe_cb,
4124 .off1 = offsetof(struct thread_options, dedupe_percentage),
4125 .maxval = 100,
4126 .minval = 0,
4127 .help = "Percentage of buffers that are dedupable",
4128 .interval = 1,
4129 .category = FIO_OPT_C_IO,
4130 .group = FIO_OPT_G_IO_BUF,
4131 },
4132 {
4133 .name = "clat_percentiles",
4134 .lname = "Completion latency percentiles",
4135 .type = FIO_OPT_BOOL,
4136 .off1 = offsetof(struct thread_options, clat_percentiles),
4137 .help = "Enable the reporting of completion latency percentiles",
4138 .def = "1",
4139 .inverse = "lat_percentiles",
4140 .category = FIO_OPT_C_STAT,
4141 .group = FIO_OPT_G_INVALID,
4142 },
4143 {
4144 .name = "lat_percentiles",
4145 .lname = "IO latency percentiles",
4146 .type = FIO_OPT_BOOL,
4147 .off1 = offsetof(struct thread_options, lat_percentiles),
4148 .help = "Enable the reporting of IO latency percentiles",
4149 .def = "0",
4150 .inverse = "clat_percentiles",
4151 .category = FIO_OPT_C_STAT,
4152 .group = FIO_OPT_G_INVALID,
4153 },
4154 {
4155 .name = "percentile_list",
4156 .lname = "Percentile list",
4157 .type = FIO_OPT_FLOAT_LIST,
4158 .off1 = offsetof(struct thread_options, percentile_list),
4159 .off2 = offsetof(struct thread_options, percentile_precision),
4160 .help = "Specify a custom list of percentiles to report for "
4161 "completion latency and block errors",
4162 .def = "1:5:10:20:30:40:50:60:70:80:90:95:99:99.5:99.9:99.95:99.99",
4163 .maxlen = FIO_IO_U_LIST_MAX_LEN,
4164 .minfp = 0.0,
4165 .maxfp = 100.0,
4166 .category = FIO_OPT_C_STAT,
4167 .group = FIO_OPT_G_INVALID,
4168 },
4169 {
4170 .name = "significant_figures",
4171 .lname = "Significant figures",
4172 .type = FIO_OPT_INT,
4173 .off1 = offsetof(struct thread_options, sig_figs),
4174 .maxval = 10,
4175 .minval = 1,
4176 .help = "Significant figures for output-format set to normal",
4177 .def = "4",
4178 .interval = 1,
4179 .category = FIO_OPT_C_STAT,
4180 .group = FIO_OPT_G_INVALID,
4181 },
4182
4183#ifdef FIO_HAVE_DISK_UTIL
4184 {
4185 .name = "disk_util",
4186 .lname = "Disk utilization",
4187 .type = FIO_OPT_BOOL,
4188 .off1 = offsetof(struct thread_options, do_disk_util),
4189 .help = "Log disk utilization statistics",
4190 .def = "1",
4191 .category = FIO_OPT_C_STAT,
4192 .group = FIO_OPT_G_INVALID,
4193 },
4194#else
4195 {
4196 .name = "disk_util",
4197 .lname = "Disk utilization",
4198 .type = FIO_OPT_UNSUPPORTED,
4199 .help = "Your platform does not support disk utilization",
4200 },
4201#endif
4202 {
4203 .name = "gtod_reduce",
4204 .lname = "Reduce gettimeofday() calls",
4205 .type = FIO_OPT_BOOL,
4206 .help = "Greatly reduce number of gettimeofday() calls",
4207 .cb = str_gtod_reduce_cb,
4208 .def = "0",
4209 .hide_on_set = 1,
4210 .category = FIO_OPT_C_STAT,
4211 .group = FIO_OPT_G_INVALID,
4212 },
4213 {
4214 .name = "disable_lat",
4215 .lname = "Disable all latency stats",
4216 .type = FIO_OPT_BOOL,
4217 .off1 = offsetof(struct thread_options, disable_lat),
4218 .help = "Disable latency numbers",
4219 .parent = "gtod_reduce",
4220 .hide = 1,
4221 .def = "0",
4222 .category = FIO_OPT_C_STAT,
4223 .group = FIO_OPT_G_INVALID,
4224 },
4225 {
4226 .name = "disable_clat",
4227 .lname = "Disable completion latency stats",
4228 .type = FIO_OPT_BOOL,
4229 .off1 = offsetof(struct thread_options, disable_clat),
4230 .help = "Disable completion latency numbers",
4231 .parent = "gtod_reduce",
4232 .hide = 1,
4233 .def = "0",
4234 .category = FIO_OPT_C_STAT,
4235 .group = FIO_OPT_G_INVALID,
4236 },
4237 {
4238 .name = "disable_slat",
4239 .lname = "Disable submission latency stats",
4240 .type = FIO_OPT_BOOL,
4241 .off1 = offsetof(struct thread_options, disable_slat),
4242 .help = "Disable submission latency numbers",
4243 .parent = "gtod_reduce",
4244 .hide = 1,
4245 .def = "0",
4246 .category = FIO_OPT_C_STAT,
4247 .group = FIO_OPT_G_INVALID,
4248 },
4249 {
4250 .name = "disable_bw_measurement",
4251 .alias = "disable_bw",
4252 .lname = "Disable bandwidth stats",
4253 .type = FIO_OPT_BOOL,
4254 .off1 = offsetof(struct thread_options, disable_bw),
4255 .help = "Disable bandwidth logging",
4256 .parent = "gtod_reduce",
4257 .hide = 1,
4258 .def = "0",
4259 .category = FIO_OPT_C_STAT,
4260 .group = FIO_OPT_G_INVALID,
4261 },
4262 {
4263 .name = "gtod_cpu",
4264 .lname = "Dedicated gettimeofday() CPU",
4265 .type = FIO_OPT_INT,
4266 .off1 = offsetof(struct thread_options, gtod_cpu),
4267 .help = "Set up dedicated gettimeofday() thread on this CPU",
4268 .verify = gtod_cpu_verify,
4269 .category = FIO_OPT_C_GENERAL,
4270 .group = FIO_OPT_G_CLOCK,
4271 },
4272 {
4273 .name = "unified_rw_reporting",
4274 .lname = "Unified RW Reporting",
4275 .type = FIO_OPT_BOOL,
4276 .off1 = offsetof(struct thread_options, unified_rw_rep),
4277 .help = "Unify reporting across data direction",
4278 .def = "0",
4279 .category = FIO_OPT_C_GENERAL,
4280 .group = FIO_OPT_G_INVALID,
4281 },
4282 {
4283 .name = "continue_on_error",
4284 .lname = "Continue on error",
4285 .type = FIO_OPT_STR,
4286 .off1 = offsetof(struct thread_options, continue_on_error),
4287 .help = "Continue on non-fatal errors during IO",
4288 .def = "none",
4289 .category = FIO_OPT_C_GENERAL,
4290 .group = FIO_OPT_G_ERR,
4291 .posval = {
4292 { .ival = "none",
4293 .oval = ERROR_TYPE_NONE,
4294 .help = "Exit when an error is encountered",
4295 },
4296 { .ival = "read",
4297 .oval = ERROR_TYPE_READ,
4298 .help = "Continue on read errors only",
4299 },
4300 { .ival = "write",
4301 .oval = ERROR_TYPE_WRITE,
4302 .help = "Continue on write errors only",
4303 },
4304 { .ival = "io",
4305 .oval = ERROR_TYPE_READ | ERROR_TYPE_WRITE,
4306 .help = "Continue on any IO errors",
4307 },
4308 { .ival = "verify",
4309 .oval = ERROR_TYPE_VERIFY,
4310 .help = "Continue on verify errors only",
4311 },
4312 { .ival = "all",
4313 .oval = ERROR_TYPE_ANY,
4314 .help = "Continue on all io and verify errors",
4315 },
4316 { .ival = "0",
4317 .oval = ERROR_TYPE_NONE,
4318 .help = "Alias for 'none'",
4319 },
4320 { .ival = "1",
4321 .oval = ERROR_TYPE_ANY,
4322 .help = "Alias for 'all'",
4323 },
4324 },
4325 },
4326 {
4327 .name = "ignore_error",
4328 .lname = "Ignore Error",
4329 .type = FIO_OPT_STR,
4330 .cb = str_ignore_error_cb,
4331 .off1 = offsetof(struct thread_options, ignore_error_nr),
4332 .help = "Set a specific list of errors to ignore",
4333 .parent = "rw",
4334 .category = FIO_OPT_C_GENERAL,
4335 .group = FIO_OPT_G_ERR,
4336 },
4337 {
4338 .name = "error_dump",
4339 .lname = "Error Dump",
4340 .type = FIO_OPT_BOOL,
4341 .off1 = offsetof(struct thread_options, error_dump),
4342 .def = "0",
4343 .help = "Dump info on each error",
4344 .category = FIO_OPT_C_GENERAL,
4345 .group = FIO_OPT_G_ERR,
4346 },
4347 {
4348 .name = "profile",
4349 .lname = "Profile",
4350 .type = FIO_OPT_STR_STORE,
4351 .off1 = offsetof(struct thread_options, profile),
4352 .help = "Select a specific builtin performance test",
4353 .category = FIO_OPT_C_PROFILE,
4354 .group = FIO_OPT_G_INVALID,
4355 },
4356 {
4357 .name = "cgroup",
4358 .lname = "Cgroup",
4359 .type = FIO_OPT_STR_STORE,
4360 .off1 = offsetof(struct thread_options, cgroup),
4361 .help = "Add job to cgroup of this name",
4362 .category = FIO_OPT_C_GENERAL,
4363 .group = FIO_OPT_G_CGROUP,
4364 },
4365 {
4366 .name = "cgroup_nodelete",
4367 .lname = "Cgroup no-delete",
4368 .type = FIO_OPT_BOOL,
4369 .off1 = offsetof(struct thread_options, cgroup_nodelete),
4370 .help = "Do not delete cgroups after job completion",
4371 .def = "0",
4372 .parent = "cgroup",
4373 .category = FIO_OPT_C_GENERAL,
4374 .group = FIO_OPT_G_CGROUP,
4375 },
4376 {
4377 .name = "cgroup_weight",
4378 .lname = "Cgroup weight",
4379 .type = FIO_OPT_INT,
4380 .off1 = offsetof(struct thread_options, cgroup_weight),
4381 .help = "Use given weight for cgroup",
4382 .minval = 100,
4383 .maxval = 1000,
4384 .parent = "cgroup",
4385 .category = FIO_OPT_C_GENERAL,
4386 .group = FIO_OPT_G_CGROUP,
4387 },
4388 {
4389 .name = "uid",
4390 .lname = "User ID",
4391 .type = FIO_OPT_INT,
4392 .off1 = offsetof(struct thread_options, uid),
4393 .help = "Run job with this user ID",
4394 .category = FIO_OPT_C_GENERAL,
4395 .group = FIO_OPT_G_CRED,
4396 },
4397 {
4398 .name = "gid",
4399 .lname = "Group ID",
4400 .type = FIO_OPT_INT,
4401 .off1 = offsetof(struct thread_options, gid),
4402 .help = "Run job with this group ID",
4403 .category = FIO_OPT_C_GENERAL,
4404 .group = FIO_OPT_G_CRED,
4405 },
4406 {
4407 .name = "kb_base",
4408 .lname = "KB Base",
4409 .type = FIO_OPT_INT,
4410 .off1 = offsetof(struct thread_options, kb_base),
4411 .prio = 1,
4412 .def = "1024",
4413 .posval = {
4414 { .ival = "1024",
4415 .oval = 1024,
4416 .help = "Inputs invert IEC and SI prefixes (for compatibility); outputs prefer binary",
4417 },
4418 { .ival = "1000",
4419 .oval = 1000,
4420 .help = "Inputs use IEC and SI prefixes; outputs prefer SI",
4421 },
4422 },
4423 .help = "Unit prefix interpretation for quantities of data (IEC and SI)",
4424 .category = FIO_OPT_C_GENERAL,
4425 .group = FIO_OPT_G_INVALID,
4426 },
4427 {
4428 .name = "unit_base",
4429 .lname = "Unit for quantities of data (Bits or Bytes)",
4430 .type = FIO_OPT_INT,
4431 .off1 = offsetof(struct thread_options, unit_base),
4432 .prio = 1,
4433 .posval = {
4434 { .ival = "0",
4435 .oval = 0,
4436 .help = "Auto-detect",
4437 },
4438 { .ival = "8",
4439 .oval = 8,
4440 .help = "Normal (byte based)",
4441 },
4442 { .ival = "1",
4443 .oval = 1,
4444 .help = "Bit based",
4445 },
4446 },
4447 .help = "Bit multiple of result summary data (8 for byte, 1 for bit)",
4448 .category = FIO_OPT_C_GENERAL,
4449 .group = FIO_OPT_G_INVALID,
4450 },
4451 {
4452 .name = "hugepage-size",
4453 .lname = "Hugepage size",
4454 .type = FIO_OPT_INT,
4455 .off1 = offsetof(struct thread_options, hugepage_size),
4456 .help = "When using hugepages, specify size of each page",
4457 .def = __fio_stringify(FIO_HUGE_PAGE),
4458 .interval = 1024 * 1024,
4459 .category = FIO_OPT_C_GENERAL,
4460 .group = FIO_OPT_G_INVALID,
4461 },
4462 {
4463 .name = "flow_id",
4464 .lname = "I/O flow ID",
4465 .type = FIO_OPT_INT,
4466 .off1 = offsetof(struct thread_options, flow_id),
4467 .help = "The flow index ID to use",
4468 .def = "0",
4469 .category = FIO_OPT_C_IO,
4470 .group = FIO_OPT_G_IO_FLOW,
4471 },
4472 {
4473 .name = "flow",
4474 .lname = "I/O flow weight",
4475 .type = FIO_OPT_INT,
4476 .off1 = offsetof(struct thread_options, flow),
4477 .help = "Weight for flow control of this job",
4478 .parent = "flow_id",
4479 .hide = 1,
4480 .def = "0",
4481 .category = FIO_OPT_C_IO,
4482 .group = FIO_OPT_G_IO_FLOW,
4483 },
4484 {
4485 .name = "flow_watermark",
4486 .lname = "I/O flow watermark",
4487 .type = FIO_OPT_INT,
4488 .off1 = offsetof(struct thread_options, flow_watermark),
4489 .help = "High watermark for flow control. This option"
4490 " should be set to the same value for all threads"
4491 " with non-zero flow.",
4492 .parent = "flow_id",
4493 .hide = 1,
4494 .def = "1024",
4495 .category = FIO_OPT_C_IO,
4496 .group = FIO_OPT_G_IO_FLOW,
4497 },
4498 {
4499 .name = "flow_sleep",
4500 .lname = "I/O flow sleep",
4501 .type = FIO_OPT_INT,
4502 .off1 = offsetof(struct thread_options, flow_sleep),
4503 .help = "How many microseconds to sleep after being held"
4504 " back by the flow control mechanism",
4505 .parent = "flow_id",
4506 .hide = 1,
4507 .def = "0",
4508 .category = FIO_OPT_C_IO,
4509 .group = FIO_OPT_G_IO_FLOW,
4510 },
4511 {
4512 .name = "steadystate",
4513 .lname = "Steady state threshold",
4514 .alias = "ss",
4515 .type = FIO_OPT_STR,
4516 .off1 = offsetof(struct thread_options, ss_state),
4517 .cb = str_steadystate_cb,
4518 .help = "Define the criterion and limit to judge when a job has reached steady state",
4519 .def = "iops_slope:0.01%",
4520 .posval = {
4521 { .ival = "iops",
4522 .oval = FIO_SS_IOPS,
4523 .help = "maximum mean deviation of IOPS measurements",
4524 },
4525 { .ival = "iops_slope",
4526 .oval = FIO_SS_IOPS_SLOPE,
4527 .help = "slope calculated from IOPS measurements",
4528 },
4529 { .ival = "bw",
4530 .oval = FIO_SS_BW,
4531 .help = "maximum mean deviation of bandwidth measurements",
4532 },
4533 {
4534 .ival = "bw_slope",
4535 .oval = FIO_SS_BW_SLOPE,
4536 .help = "slope calculated from bandwidth measurements",
4537 },
4538 },
4539 .category = FIO_OPT_C_GENERAL,
4540 .group = FIO_OPT_G_RUNTIME,
4541 },
4542 {
4543 .name = "steadystate_duration",
4544 .lname = "Steady state duration",
4545 .alias = "ss_dur",
4546 .parent = "steadystate",
4547 .type = FIO_OPT_STR_VAL_TIME,
4548 .off1 = offsetof(struct thread_options, ss_dur),
4549 .help = "Stop workload upon attaining steady state for specified duration",
4550 .def = "0",
4551 .is_seconds = 1,
4552 .is_time = 1,
4553 .category = FIO_OPT_C_GENERAL,
4554 .group = FIO_OPT_G_RUNTIME,
4555 },
4556 {
4557 .name = "steadystate_ramp_time",
4558 .lname = "Steady state ramp time",
4559 .alias = "ss_ramp",
4560 .parent = "steadystate",
4561 .type = FIO_OPT_STR_VAL_TIME,
4562 .off1 = offsetof(struct thread_options, ss_ramp_time),
4563 .help = "Delay before initiation of data collection for steady state job termination testing",
4564 .def = "0",
4565 .is_seconds = 1,
4566 .is_time = 1,
4567 .category = FIO_OPT_C_GENERAL,
4568 .group = FIO_OPT_G_RUNTIME,
4569 },
4570 {
4571 .name = NULL,
4572 },
4573};
4574
4575static void add_to_lopt(struct option *lopt, struct fio_option *o,
4576 const char *name, int val)
4577{
4578 lopt->name = (char *) name;
4579 lopt->val = val;
4580 if (o->type == FIO_OPT_STR_SET)
4581 lopt->has_arg = optional_argument;
4582 else
4583 lopt->has_arg = required_argument;
4584}
4585
4586static void options_to_lopts(struct fio_option *opts,
4587 struct option *long_options,
4588 int i, int option_type)
4589{
4590 struct fio_option *o = &opts[0];
4591 while (o->name) {
4592 add_to_lopt(&long_options[i], o, o->name, option_type);
4593 if (o->alias) {
4594 i++;
4595 add_to_lopt(&long_options[i], o, o->alias, option_type);
4596 }
4597
4598 i++;
4599 o++;
4600 assert(i < FIO_NR_OPTIONS);
4601 }
4602}
4603
4604void fio_options_set_ioengine_opts(struct option *long_options,
4605 struct thread_data *td)
4606{
4607 unsigned int i;
4608
4609 i = 0;
4610 while (long_options[i].name) {
4611 if (long_options[i].val == FIO_GETOPT_IOENGINE) {
4612 memset(&long_options[i], 0, sizeof(*long_options));
4613 break;
4614 }
4615 i++;
4616 }
4617
4618 /*
4619 * Just clear out the prior ioengine options.
4620 */
4621 if (!td || !td->eo)
4622 return;
4623
4624 options_to_lopts(td->io_ops->options, long_options, i,
4625 FIO_GETOPT_IOENGINE);
4626}
4627
4628void fio_options_dup_and_init(struct option *long_options)
4629{
4630 unsigned int i;
4631
4632 options_init(fio_options);
4633
4634 i = 0;
4635 while (long_options[i].name)
4636 i++;
4637
4638 options_to_lopts(fio_options, long_options, i, FIO_GETOPT_JOB);
4639}
4640
4641struct fio_keyword {
4642 const char *word;
4643 const char *desc;
4644 char *replace;
4645};
4646
4647static struct fio_keyword fio_keywords[] = {
4648 {
4649 .word = "$pagesize",
4650 .desc = "Page size in the system",
4651 },
4652 {
4653 .word = "$mb_memory",
4654 .desc = "Megabytes of memory online",
4655 },
4656 {
4657 .word = "$ncpus",
4658 .desc = "Number of CPUs online in the system",
4659 },
4660 {
4661 .word = NULL,
4662 },
4663};
4664
4665void fio_keywords_exit(void)
4666{
4667 struct fio_keyword *kw;
4668
4669 kw = &fio_keywords[0];
4670 while (kw->word) {
4671 free(kw->replace);
4672 kw->replace = NULL;
4673 kw++;
4674 }
4675}
4676
4677void fio_keywords_init(void)
4678{
4679 unsigned long long mb_memory;
4680 char buf[128];
4681 long l;
4682
4683 sprintf(buf, "%lu", (unsigned long) page_size);
4684 fio_keywords[0].replace = strdup(buf);
4685
4686 mb_memory = os_phys_mem() / (1024 * 1024);
4687 sprintf(buf, "%llu", mb_memory);
4688 fio_keywords[1].replace = strdup(buf);
4689
4690 l = cpus_online();
4691 sprintf(buf, "%lu", l);
4692 fio_keywords[2].replace = strdup(buf);
4693}
4694
4695#define BC_APP "bc"
4696
4697static char *bc_calc(char *str)
4698{
4699 char buf[128], *tmp;
4700 FILE *f;
4701 int ret;
4702
4703 /*
4704 * No math, just return string
4705 */
4706 if ((!strchr(str, '+') && !strchr(str, '-') && !strchr(str, '*') &&
4707 !strchr(str, '/')) || strchr(str, '\''))
4708 return str;
4709
4710 /*
4711 * Split option from value, we only need to calculate the value
4712 */
4713 tmp = strchr(str, '=');
4714 if (!tmp)
4715 return str;
4716
4717 tmp++;
4718
4719 /*
4720 * Prevent buffer overflows; such a case isn't reasonable anyway
4721 */
4722 if (strlen(str) >= 128 || strlen(tmp) > 100)
4723 return str;
4724
4725 sprintf(buf, "which %s > /dev/null", BC_APP);
4726 if (system(buf)) {
4727 log_err("fio: bc is needed for performing math\n");
4728 return NULL;
4729 }
4730
4731 sprintf(buf, "echo '%s' | %s", tmp, BC_APP);
4732 f = popen(buf, "r");
4733 if (!f)
4734 return NULL;
4735
4736 ret = fread(&buf[tmp - str], 1, 128 - (tmp - str), f);
4737 if (ret <= 0) {
4738 pclose(f);
4739 return NULL;
4740 }
4741
4742 pclose(f);
4743 buf[(tmp - str) + ret - 1] = '\0';
4744 memcpy(buf, str, tmp - str);
4745 free(str);
4746 return strdup(buf);
4747}
4748
4749/*
4750 * Return a copy of the input string with substrings of the form ${VARNAME}
4751 * substituted with the value of the environment variable VARNAME. The
4752 * substitution always occurs, even if VARNAME is empty or the corresponding
4753 * environment variable undefined.
4754 */
4755static char *option_dup_subs(const char *opt)
4756{
4757 char out[OPT_LEN_MAX+1];
4758 char in[OPT_LEN_MAX+1];
4759 char *outptr = out;
4760 char *inptr = in;
4761 char *ch1, *ch2, *env;
4762 ssize_t nchr = OPT_LEN_MAX;
4763 size_t envlen;
4764
4765 if (strlen(opt) + 1 > OPT_LEN_MAX) {
4766 log_err("OPT_LEN_MAX (%d) is too small\n", OPT_LEN_MAX);
4767 return NULL;
4768 }
4769
4770 in[OPT_LEN_MAX] = '\0';
4771 strncpy(in, opt, OPT_LEN_MAX);
4772
4773 while (*inptr && nchr > 0) {
4774 if (inptr[0] == '$' && inptr[1] == '{') {
4775 ch2 = strchr(inptr, '}');
4776 if (ch2 && inptr+1 < ch2) {
4777 ch1 = inptr+2;
4778 inptr = ch2+1;
4779 *ch2 = '\0';
4780
4781 env = getenv(ch1);
4782 if (env) {
4783 envlen = strlen(env);
4784 if (envlen <= nchr) {
4785 memcpy(outptr, env, envlen);
4786 outptr += envlen;
4787 nchr -= envlen;
4788 }
4789 }
4790
4791 continue;
4792 }
4793 }
4794
4795 *outptr++ = *inptr++;
4796 --nchr;
4797 }
4798
4799 *outptr = '\0';
4800 return strdup(out);
4801}
4802
4803/*
4804 * Look for reserved variable names and replace them with real values
4805 */
4806static char *fio_keyword_replace(char *opt)
4807{
4808 char *s;
4809 int i;
4810 int docalc = 0;
4811
4812 for (i = 0; fio_keywords[i].word != NULL; i++) {
4813 struct fio_keyword *kw = &fio_keywords[i];
4814
4815 while ((s = strstr(opt, kw->word)) != NULL) {
4816 char *new = malloc(strlen(opt) + 1);
4817 char *o_org = opt;
4818 int olen = s - opt;
4819 int len;
4820
4821 /*
4822 * Copy part of the string before the keyword and
4823 * sprintf() the replacement after it.
4824 */
4825 memcpy(new, opt, olen);
4826 len = sprintf(new + olen, "%s", kw->replace);
4827
4828 /*
4829 * If there's more in the original string, copy that
4830 * in too
4831 */
4832 opt += strlen(kw->word) + olen;
4833 if (strlen(opt))
4834 memcpy(new + olen + len, opt, opt - o_org - 1);
4835
4836 /*
4837 * replace opt and free the old opt
4838 */
4839 opt = new;
4840 free(o_org);
4841
4842 docalc = 1;
4843 }
4844 }
4845
4846 /*
4847 * Check for potential math and invoke bc, if possible
4848 */
4849 if (docalc)
4850 opt = bc_calc(opt);
4851
4852 return opt;
4853}
4854
4855static char **dup_and_sub_options(char **opts, int num_opts)
4856{
4857 int i;
4858 char **opts_copy = malloc(num_opts * sizeof(*opts));
4859 for (i = 0; i < num_opts; i++) {
4860 opts_copy[i] = option_dup_subs(opts[i]);
4861 if (!opts_copy[i])
4862 continue;
4863 opts_copy[i] = fio_keyword_replace(opts_copy[i]);
4864 }
4865 return opts_copy;
4866}
4867
4868static void show_closest_option(const char *opt)
4869{
4870 int best_option, best_distance;
4871 int i, distance;
4872 char *name;
4873
4874 if (!strlen(opt))
4875 return;
4876
4877 name = strdup(opt);
4878 i = 0;
4879 while (name[i] != '\0' && name[i] != '=')
4880 i++;
4881 name[i] = '\0';
4882
4883 best_option = -1;
4884 best_distance = INT_MAX;
4885 i = 0;
4886 while (fio_options[i].name) {
4887 distance = string_distance(name, fio_options[i].name);
4888 if (distance < best_distance) {
4889 best_distance = distance;
4890 best_option = i;
4891 }
4892 i++;
4893 }
4894
4895 if (best_option != -1 && string_distance_ok(name, best_distance) &&
4896 fio_options[best_option].type != FIO_OPT_UNSUPPORTED)
4897 log_err("Did you mean %s?\n", fio_options[best_option].name);
4898
4899 free(name);
4900}
4901
4902int fio_options_parse(struct thread_data *td, char **opts, int num_opts)
4903{
4904 int i, ret, unknown;
4905 char **opts_copy;
4906
4907 sort_options(opts, fio_options, num_opts);
4908 opts_copy = dup_and_sub_options(opts, num_opts);
4909
4910 for (ret = 0, i = 0, unknown = 0; i < num_opts; i++) {
4911 struct fio_option *o;
4912 int newret = parse_option(opts_copy[i], opts[i], fio_options,
4913 &o, &td->o, &td->opt_list);
4914
4915 if (!newret && o)
4916 fio_option_mark_set(&td->o, o);
4917
4918 if (opts_copy[i]) {
4919 if (newret && !o) {
4920 unknown++;
4921 continue;
4922 }
4923 free(opts_copy[i]);
4924 opts_copy[i] = NULL;
4925 }
4926
4927 ret |= newret;
4928 }
4929
4930 if (unknown) {
4931 ret |= ioengine_load(td);
4932 if (td->eo) {
4933 sort_options(opts_copy, td->io_ops->options, num_opts);
4934 opts = opts_copy;
4935 }
4936 for (i = 0; i < num_opts; i++) {
4937 struct fio_option *o = NULL;
4938 int newret = 1;
4939
4940 if (!opts_copy[i])
4941 continue;
4942
4943 if (td->eo)
4944 newret = parse_option(opts_copy[i], opts[i],
4945 td->io_ops->options, &o,
4946 td->eo, &td->opt_list);
4947
4948 ret |= newret;
4949 if (!o) {
4950 log_err("Bad option <%s>\n", opts[i]);
4951 show_closest_option(opts[i]);
4952 }
4953 free(opts_copy[i]);
4954 opts_copy[i] = NULL;
4955 }
4956 }
4957
4958 free(opts_copy);
4959 return ret;
4960}
4961
4962int fio_cmd_option_parse(struct thread_data *td, const char *opt, char *val)
4963{
4964 int ret;
4965
4966 ret = parse_cmd_option(opt, val, fio_options, &td->o, &td->opt_list);
4967 if (!ret) {
4968 struct fio_option *o;
4969
4970 o = find_option(fio_options, opt);
4971 if (o)
4972 fio_option_mark_set(&td->o, o);
4973 }
4974
4975 return ret;
4976}
4977
4978int fio_cmd_ioengine_option_parse(struct thread_data *td, const char *opt,
4979 char *val)
4980{
4981 return parse_cmd_option(opt, val, td->io_ops->options, td->eo,
4982 &td->opt_list);
4983}
4984
4985void fio_fill_default_options(struct thread_data *td)
4986{
4987 td->o.magic = OPT_MAGIC;
4988 fill_default_options(&td->o, fio_options);
4989}
4990
4991int fio_show_option_help(const char *opt)
4992{
4993 return show_cmd_help(fio_options, opt);
4994}
4995
4996/*
4997 * dupe FIO_OPT_STR_STORE options
4998 */
4999void fio_options_mem_dupe(struct thread_data *td)
5000{
5001 options_mem_dupe(fio_options, &td->o);
5002
5003 if (td->eo && td->io_ops) {
5004 void *oldeo = td->eo;
5005
5006 td->eo = malloc(td->io_ops->option_struct_size);
5007 memcpy(td->eo, oldeo, td->io_ops->option_struct_size);
5008 options_mem_dupe(td->io_ops->options, td->eo);
5009 }
5010}
5011
5012unsigned int fio_get_kb_base(void *data)
5013{
5014 struct thread_data *td = cb_data_to_td(data);
5015 struct thread_options *o = &td->o;
5016 unsigned int kb_base = 0;
5017
5018 /*
5019 * This is a hack... For private options, *data is not holding
5020 * a pointer to the thread_options, but to private data. This means
5021 * we can't safely dereference it, but magic is first so mem wise
5022 * it is valid. But this also means that if the job first sets
5023 * kb_base and expects that to be honored by private options,
5024 * it will be disappointed. We will return the global default
5025 * for this.
5026 */
5027 if (o && o->magic == OPT_MAGIC)
5028 kb_base = o->kb_base;
5029 if (!kb_base)
5030 kb_base = 1024;
5031
5032 return kb_base;
5033}
5034
5035int add_option(struct fio_option *o)
5036{
5037 struct fio_option *__o;
5038 int opt_index = 0;
5039
5040 __o = fio_options;
5041 while (__o->name) {
5042 opt_index++;
5043 __o++;
5044 }
5045
5046 if (opt_index + 1 == FIO_MAX_OPTS) {
5047 log_err("fio: FIO_MAX_OPTS is too small\n");
5048 return 1;
5049 }
5050
5051 memcpy(&fio_options[opt_index], o, sizeof(*o));
5052 fio_options[opt_index + 1].name = NULL;
5053 return 0;
5054}
5055
5056void invalidate_profile_options(const char *prof_name)
5057{
5058 struct fio_option *o;
5059
5060 o = fio_options;
5061 while (o->name) {
5062 if (o->prof_name && !strcmp(o->prof_name, prof_name)) {
5063 o->type = FIO_OPT_INVALID;
5064 o->prof_name = NULL;
5065 }
5066 o++;
5067 }
5068}
5069
5070void add_opt_posval(const char *optname, const char *ival, const char *help)
5071{
5072 struct fio_option *o;
5073 unsigned int i;
5074
5075 o = find_option(fio_options, optname);
5076 if (!o)
5077 return;
5078
5079 for (i = 0; i < PARSE_MAX_VP; i++) {
5080 if (o->posval[i].ival)
5081 continue;
5082
5083 o->posval[i].ival = ival;
5084 o->posval[i].help = help;
5085 break;
5086 }
5087}
5088
5089void del_opt_posval(const char *optname, const char *ival)
5090{
5091 struct fio_option *o;
5092 unsigned int i;
5093
5094 o = find_option(fio_options, optname);
5095 if (!o)
5096 return;
5097
5098 for (i = 0; i < PARSE_MAX_VP; i++) {
5099 if (!o->posval[i].ival)
5100 continue;
5101 if (strcmp(o->posval[i].ival, ival))
5102 continue;
5103
5104 o->posval[i].ival = NULL;
5105 o->posval[i].help = NULL;
5106 }
5107}
5108
5109void fio_options_free(struct thread_data *td)
5110{
5111 options_free(fio_options, &td->o);
5112 if (td->eo && td->io_ops && td->io_ops->options) {
5113 options_free(td->io_ops->options, td->eo);
5114 free(td->eo);
5115 td->eo = NULL;
5116 }
5117}
5118
5119struct fio_option *fio_option_find(const char *name)
5120{
5121 return find_option(fio_options, name);
5122}
5123
5124static struct fio_option *find_next_opt(struct thread_options *o,
5125 struct fio_option *from,
5126 unsigned int off1)
5127{
5128 struct fio_option *opt;
5129
5130 if (!from)
5131 from = &fio_options[0];
5132 else
5133 from++;
5134
5135 opt = NULL;
5136 do {
5137 if (off1 == from->off1) {
5138 opt = from;
5139 break;
5140 }
5141 from++;
5142 } while (from->name);
5143
5144 return opt;
5145}
5146
5147static int opt_is_set(struct thread_options *o, struct fio_option *opt)
5148{
5149 unsigned int opt_off, index, offset;
5150
5151 opt_off = opt - &fio_options[0];
5152 index = opt_off / (8 * sizeof(uint64_t));
5153 offset = opt_off & ((8 * sizeof(uint64_t)) - 1);
5154 return (o->set_options[index] & ((uint64_t)1 << offset)) != 0;
5155}
5156
5157bool __fio_option_is_set(struct thread_options *o, unsigned int off1)
5158{
5159 struct fio_option *opt, *next;
5160
5161 next = NULL;
5162 while ((opt = find_next_opt(o, next, off1)) != NULL) {
5163 if (opt_is_set(o, opt))
5164 return true;
5165
5166 next = opt;
5167 }
5168
5169 return false;
5170}
5171
5172void fio_option_mark_set(struct thread_options *o, struct fio_option *opt)
5173{
5174 unsigned int opt_off, index, offset;
5175
5176 opt_off = opt - &fio_options[0];
5177 index = opt_off / (8 * sizeof(uint64_t));
5178 offset = opt_off & ((8 * sizeof(uint64_t)) - 1);
5179 o->set_options[index] |= (uint64_t)1 << offset;
5180}