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