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