options.c revision ff52be3d9ff93cc5e823a75c41d48f62f842feb8
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <ctype.h>
5#include <string.h>
6#include <assert.h>
7#include <libgen.h>
8#include <fcntl.h>
9#include <sys/types.h>
10#include <sys/stat.h>
11
12#include "fio.h"
13#include "verify.h"
14#include "parse.h"
15#include "lib/fls.h"
16#include "options.h"
17
18#include "crc/crc32c.h"
19
20/*
21 * Check if mmap/mmaphuge has a :/foo/bar/file at the end. If so, return that.
22 */
23static char *get_opt_postfix(const char *str)
24{
25	char *p = strstr(str, ":");
26
27	if (!p)
28		return NULL;
29
30	p++;
31	strip_blank_front(&p);
32	strip_blank_end(p);
33	return strdup(p);
34}
35
36static int converthexchartoint(char a)
37{
38	int base;
39
40	switch (a) {
41	case '0'...'9':
42		base = '0';
43		break;
44	case 'A'...'F':
45		base = 'A' - 10;
46		break;
47	case 'a'...'f':
48		base = 'a' - 10;
49		break;
50	default:
51		base = 0;
52	}
53	return a - base;
54}
55
56static int bs_cmp(const void *p1, const void *p2)
57{
58	const struct bssplit *bsp1 = p1;
59	const struct bssplit *bsp2 = p2;
60
61	return bsp1->perc < bsp2->perc;
62}
63
64static int bssplit_ddir(struct thread_options *o, int ddir, char *str)
65{
66	struct bssplit *bssplit;
67	unsigned int i, perc, perc_missing;
68	unsigned int max_bs, min_bs;
69	long long val;
70	char *fname;
71
72	o->bssplit_nr[ddir] = 4;
73	bssplit = malloc(4 * sizeof(struct bssplit));
74
75	i = 0;
76	max_bs = 0;
77	min_bs = -1;
78	while ((fname = strsep(&str, ":")) != NULL) {
79		char *perc_str;
80
81		if (!strlen(fname))
82			break;
83
84		/*
85		 * grow struct buffer, if needed
86		 */
87		if (i == o->bssplit_nr[ddir]) {
88			o->bssplit_nr[ddir] <<= 1;
89			bssplit = realloc(bssplit, o->bssplit_nr[ddir]
90						  * sizeof(struct bssplit));
91		}
92
93		perc_str = strstr(fname, "/");
94		if (perc_str) {
95			*perc_str = '\0';
96			perc_str++;
97			perc = atoi(perc_str);
98			if (perc > 100)
99				perc = 100;
100			else if (!perc)
101				perc = -1;
102		} else
103			perc = -1;
104
105		if (str_to_decimal(fname, &val, 1, o)) {
106			log_err("fio: bssplit conversion failed\n");
107			free(bssplit);
108			return 1;
109		}
110
111		if (val > max_bs)
112			max_bs = val;
113		if (val < min_bs)
114			min_bs = val;
115
116		bssplit[i].bs = val;
117		bssplit[i].perc = perc;
118		i++;
119	}
120
121	o->bssplit_nr[ddir] = i;
122
123	/*
124	 * Now check if the percentages add up, and how much is missing
125	 */
126	perc = perc_missing = 0;
127	for (i = 0; i < o->bssplit_nr[ddir]; i++) {
128		struct bssplit *bsp = &bssplit[i];
129
130		if (bsp->perc == (unsigned char) -1)
131			perc_missing++;
132		else
133			perc += bsp->perc;
134	}
135
136	if (perc > 100) {
137		log_err("fio: bssplit percentages add to more than 100%%\n");
138		free(bssplit);
139		return 1;
140	}
141	/*
142	 * If values didn't have a percentage set, divide the remains between
143	 * them.
144	 */
145	if (perc_missing) {
146		for (i = 0; i < o->bssplit_nr[ddir]; i++) {
147			struct bssplit *bsp = &bssplit[i];
148
149			if (bsp->perc == (unsigned char) -1)
150				bsp->perc = (100 - perc) / perc_missing;
151		}
152	}
153
154	o->min_bs[ddir] = min_bs;
155	o->max_bs[ddir] = max_bs;
156
157	/*
158	 * now sort based on percentages, for ease of lookup
159	 */
160	qsort(bssplit, o->bssplit_nr[ddir], sizeof(struct bssplit), bs_cmp);
161	o->bssplit[ddir] = bssplit;
162	return 0;
163}
164
165static int str_bssplit_cb(void *data, const char *input)
166{
167	struct thread_data *td = data;
168	char *str, *p, *odir, *ddir;
169	int ret = 0;
170
171	if (parse_dryrun())
172		return 0;
173
174	p = str = strdup(input);
175
176	strip_blank_front(&str);
177	strip_blank_end(str);
178
179	odir = strchr(str, ',');
180	if (odir) {
181		ddir = strchr(odir + 1, ',');
182		if (ddir) {
183			ret = bssplit_ddir(&td->o, DDIR_TRIM, ddir + 1);
184			if (!ret)
185				*ddir = '\0';
186		} else {
187			char *op;
188
189			op = strdup(odir + 1);
190			ret = bssplit_ddir(&td->o, DDIR_TRIM, op);
191
192			free(op);
193		}
194		if (!ret)
195			ret = bssplit_ddir(&td->o, DDIR_WRITE, odir + 1);
196		if (!ret) {
197			*odir = '\0';
198			ret = bssplit_ddir(&td->o, DDIR_READ, str);
199		}
200	} else {
201		char *op;
202
203		op = strdup(str);
204		ret = bssplit_ddir(&td->o, DDIR_WRITE, op);
205		free(op);
206
207		if (!ret) {
208			op = strdup(str);
209			ret = bssplit_ddir(&td->o, DDIR_TRIM, op);
210			free(op);
211		}
212		ret = bssplit_ddir(&td->o, DDIR_READ, str);
213	}
214
215	free(p);
216	return ret;
217}
218
219static int str2error(char *str)
220{
221	const char *err[] = { "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO",
222			    "ENXIO", "E2BIG", "ENOEXEC", "EBADF",
223			    "ECHILD", "EAGAIN", "ENOMEM", "EACCES",
224			    "EFAULT", "ENOTBLK", "EBUSY", "EEXIST",
225			    "EXDEV", "ENODEV", "ENOTDIR", "EISDIR",
226			    "EINVAL", "ENFILE", "EMFILE", "ENOTTY",
227			    "ETXTBSY","EFBIG", "ENOSPC", "ESPIPE",
228			    "EROFS","EMLINK", "EPIPE", "EDOM", "ERANGE" };
229	int i = 0, num = sizeof(err) / sizeof(void *);
230
231	while (i < num) {
232		if (!strcmp(err[i], str))
233			return i + 1;
234		i++;
235	}
236	return 0;
237}
238
239static int ignore_error_type(struct thread_data *td, int etype, char *str)
240{
241	unsigned int i;
242	int *error;
243	char *fname;
244
245	if (etype >= ERROR_TYPE_CNT) {
246		log_err("Illegal error type\n");
247		return 1;
248	}
249
250	td->o.ignore_error_nr[etype] = 4;
251	error = malloc(4 * sizeof(struct bssplit));
252
253	i = 0;
254	while ((fname = strsep(&str, ":")) != NULL) {
255
256		if (!strlen(fname))
257			break;
258
259		/*
260		 * grow struct buffer, if needed
261		 */
262		if (i == td->o.ignore_error_nr[etype]) {
263			td->o.ignore_error_nr[etype] <<= 1;
264			error = realloc(error, td->o.ignore_error_nr[etype]
265						  * sizeof(int));
266		}
267		if (fname[0] == 'E') {
268			error[i] = str2error(fname);
269		} else {
270			error[i] = atoi(fname);
271			if (error[i] < 0)
272				error[i] = error[i];
273		}
274		if (!error[i]) {
275			log_err("Unknown error %s, please use number value \n",
276				  fname);
277			free(error);
278			return 1;
279		}
280		i++;
281	}
282	if (i) {
283		td->o.continue_on_error |= 1 << etype;
284		td->o.ignore_error_nr[etype] = i;
285		td->o.ignore_error[etype] = error;
286	}
287	return 0;
288
289}
290
291static int str_ignore_error_cb(void *data, const char *input)
292{
293	struct thread_data *td = data;
294	char *str, *p, *n;
295	int type = 0, ret = 1;
296
297	if (parse_dryrun())
298		return 0;
299
300	p = str = strdup(input);
301
302	strip_blank_front(&str);
303	strip_blank_end(str);
304
305	while (p) {
306		n = strchr(p, ',');
307		if (n)
308			*n++ = '\0';
309		ret = ignore_error_type(td, type, p);
310		if (ret)
311			break;
312		p = n;
313		type++;
314	}
315	free(str);
316	return ret;
317}
318
319static int str_rw_cb(void *data, const char *str)
320{
321	struct thread_data *td = data;
322	struct thread_options *o = &td->o;
323	char *nr = get_opt_postfix(str);
324
325	if (parse_dryrun())
326		return 0;
327
328	o->ddir_seq_nr = 1;
329	o->ddir_seq_add = 0;
330
331	if (!nr)
332		return 0;
333
334	if (td_random(td))
335		o->ddir_seq_nr = atoi(nr);
336	else {
337		long long val;
338
339		if (str_to_decimal(nr, &val, 1, o)) {
340			log_err("fio: rw postfix parsing failed\n");
341			free(nr);
342			return 1;
343		}
344
345		o->ddir_seq_add = val;
346	}
347
348	free(nr);
349	return 0;
350}
351
352static int str_mem_cb(void *data, const char *mem)
353{
354	struct thread_data *td = data;
355
356	if (td->o.mem_type == MEM_MMAPHUGE || td->o.mem_type == MEM_MMAP)
357		td->o.mmapfile = get_opt_postfix(mem);
358
359	return 0;
360}
361
362static int fio_clock_source_cb(void *data, const char *str)
363{
364	struct thread_data *td = data;
365
366	fio_clock_source = td->o.clocksource;
367	fio_clock_source_set = 1;
368	fio_clock_init();
369	return 0;
370}
371
372static int str_rwmix_read_cb(void *data, unsigned long long *val)
373{
374	struct thread_data *td = data;
375
376	td->o.rwmix[DDIR_READ] = *val;
377	td->o.rwmix[DDIR_WRITE] = 100 - *val;
378	return 0;
379}
380
381static int str_rwmix_write_cb(void *data, unsigned long long *val)
382{
383	struct thread_data *td = data;
384
385	td->o.rwmix[DDIR_WRITE] = *val;
386	td->o.rwmix[DDIR_READ] = 100 - *val;
387	return 0;
388}
389
390static int str_exitall_cb(void)
391{
392	exitall_on_terminate = 1;
393	return 0;
394}
395
396#ifdef FIO_HAVE_CPU_AFFINITY
397static int str_cpumask_cb(void *data, unsigned long long *val)
398{
399	struct thread_data *td = data;
400	unsigned int i;
401	long max_cpu;
402	int ret;
403
404	if (parse_dryrun())
405		return 0;
406
407	ret = fio_cpuset_init(&td->o.cpumask);
408	if (ret < 0) {
409		log_err("fio: cpuset_init failed\n");
410		td_verror(td, ret, "fio_cpuset_init");
411		return 1;
412	}
413
414	max_cpu = cpus_online();
415
416	for (i = 0; i < sizeof(int) * 8; i++) {
417		if ((1 << i) & *val) {
418			if (i > max_cpu) {
419				log_err("fio: CPU %d too large (max=%ld)\n", i,
420								max_cpu);
421				return 1;
422			}
423			dprint(FD_PARSE, "set cpu allowed %d\n", i);
424			fio_cpu_set(&td->o.cpumask, i);
425		}
426	}
427
428	td->o.cpumask_set = 1;
429	return 0;
430}
431
432static int set_cpus_allowed(struct thread_data *td, os_cpu_mask_t *mask,
433			    const char *input)
434{
435	char *cpu, *str, *p;
436	long max_cpu;
437	int ret = 0;
438
439	ret = fio_cpuset_init(mask);
440	if (ret < 0) {
441		log_err("fio: cpuset_init failed\n");
442		td_verror(td, ret, "fio_cpuset_init");
443		return 1;
444	}
445
446	p = str = strdup(input);
447
448	strip_blank_front(&str);
449	strip_blank_end(str);
450
451	max_cpu = cpus_online();
452
453	while ((cpu = strsep(&str, ",")) != NULL) {
454		char *str2, *cpu2;
455		int icpu, icpu2;
456
457		if (!strlen(cpu))
458			break;
459
460		str2 = cpu;
461		icpu2 = -1;
462		while ((cpu2 = strsep(&str2, "-")) != NULL) {
463			if (!strlen(cpu2))
464				break;
465
466			icpu2 = atoi(cpu2);
467		}
468
469		icpu = atoi(cpu);
470		if (icpu2 == -1)
471			icpu2 = icpu;
472		while (icpu <= icpu2) {
473			if (icpu >= FIO_MAX_CPUS) {
474				log_err("fio: your OS only supports up to"
475					" %d CPUs\n", (int) FIO_MAX_CPUS);
476				ret = 1;
477				break;
478			}
479			if (icpu > max_cpu) {
480				log_err("fio: CPU %d too large (max=%ld)\n",
481							icpu, max_cpu);
482				ret = 1;
483				break;
484			}
485
486			dprint(FD_PARSE, "set cpu allowed %d\n", icpu);
487			fio_cpu_set(mask, icpu);
488			icpu++;
489		}
490		if (ret)
491			break;
492	}
493
494	free(p);
495	if (!ret)
496		td->o.cpumask_set = 1;
497	return ret;
498}
499
500static int str_cpus_allowed_cb(void *data, const char *input)
501{
502	struct thread_data *td = data;
503	int ret;
504
505	if (parse_dryrun())
506		return 0;
507
508	ret = set_cpus_allowed(td, &td->o.cpumask, input);
509	if (!ret)
510		td->o.cpumask_set = 1;
511
512	return ret;
513}
514
515static int str_verify_cpus_allowed_cb(void *data, const char *input)
516{
517	struct thread_data *td = data;
518	int ret;
519
520	ret = set_cpus_allowed(td, &td->o.verify_cpumask, input);
521	if (!ret)
522		td->o.verify_cpumask_set = 1;
523
524	return ret;
525}
526#endif
527
528#ifdef CONFIG_LIBNUMA
529static int str_numa_cpunodes_cb(void *data, char *input)
530{
531	struct thread_data *td = data;
532
533	if (parse_dryrun())
534		return 0;
535
536	/* numa_parse_nodestring() parses a character string list
537	 * of nodes into a bit mask. The bit mask is allocated by
538	 * numa_allocate_nodemask(), so it should be freed by
539	 * numa_free_nodemask().
540	 */
541	td->o.numa_cpunodesmask = numa_parse_nodestring(input);
542	if (td->o.numa_cpunodesmask == NULL) {
543		log_err("fio: numa_parse_nodestring failed\n");
544		td_verror(td, 1, "str_numa_cpunodes_cb");
545		return 1;
546	}
547
548	td->o.numa_cpumask_set = 1;
549	return 0;
550}
551
552static int str_numa_mpol_cb(void *data, char *input)
553{
554	struct thread_data *td = data;
555	const char * const policy_types[] =
556		{ "default", "prefer", "bind", "interleave", "local", NULL };
557	int i;
558	char *nodelist;
559
560	if (parse_dryrun())
561		return 0;
562
563	nodelist = strchr(input, ':');
564	if (nodelist) {
565		/* NUL-terminate mode */
566		*nodelist++ = '\0';
567	}
568
569	for (i = 0; i <= MPOL_LOCAL; i++) {
570		if (!strcmp(input, policy_types[i])) {
571			td->o.numa_mem_mode = i;
572			break;
573		}
574	}
575	if (i > MPOL_LOCAL) {
576		log_err("fio: memory policy should be: default, prefer, bind, interleave, local\n");
577		goto out;
578	}
579
580	switch (td->o.numa_mem_mode) {
581	case MPOL_PREFERRED:
582		/*
583		 * Insist on a nodelist of one node only
584		 */
585		if (nodelist) {
586			char *rest = nodelist;
587			while (isdigit(*rest))
588				rest++;
589			if (*rest) {
590				log_err("fio: one node only for \'prefer\'\n");
591				goto out;
592			}
593		} else {
594			log_err("fio: one node is needed for \'prefer\'\n");
595			goto out;
596		}
597		break;
598	case MPOL_INTERLEAVE:
599		/*
600		 * Default to online nodes with memory if no nodelist
601		 */
602		if (!nodelist)
603			nodelist = strdup("all");
604		break;
605	case MPOL_LOCAL:
606	case MPOL_DEFAULT:
607		/*
608		 * Don't allow a nodelist
609		 */
610		if (nodelist) {
611			log_err("fio: NO nodelist for \'local\'\n");
612			goto out;
613		}
614		break;
615	case MPOL_BIND:
616		/*
617		 * Insist on a nodelist
618		 */
619		if (!nodelist) {
620			log_err("fio: a nodelist is needed for \'bind\'\n");
621			goto out;
622		}
623		break;
624	}
625
626
627	/* numa_parse_nodestring() parses a character string list
628	 * of nodes into a bit mask. The bit mask is allocated by
629	 * numa_allocate_nodemask(), so it should be freed by
630	 * numa_free_nodemask().
631	 */
632	switch (td->o.numa_mem_mode) {
633	case MPOL_PREFERRED:
634		td->o.numa_mem_prefer_node = atoi(nodelist);
635		break;
636	case MPOL_INTERLEAVE:
637	case MPOL_BIND:
638		td->o.numa_memnodesmask = numa_parse_nodestring(nodelist);
639		if (td->o.numa_memnodesmask == NULL) {
640			log_err("fio: numa_parse_nodestring failed\n");
641			td_verror(td, 1, "str_numa_memnodes_cb");
642			return 1;
643		}
644		break;
645	case MPOL_LOCAL:
646	case MPOL_DEFAULT:
647	default:
648		break;
649	}
650
651	td->o.numa_memmask_set = 1;
652	return 0;
653
654out:
655	return 1;
656}
657#endif
658
659static int str_fst_cb(void *data, const char *str)
660{
661	struct thread_data *td = data;
662	char *nr = get_opt_postfix(str);
663
664	td->file_service_nr = 1;
665	if (nr) {
666		td->file_service_nr = atoi(nr);
667		free(nr);
668	}
669
670	return 0;
671}
672
673#ifdef CONFIG_SYNC_FILE_RANGE
674static int str_sfr_cb(void *data, const char *str)
675{
676	struct thread_data *td = data;
677	char *nr = get_opt_postfix(str);
678
679	td->sync_file_range_nr = 1;
680	if (nr) {
681		td->sync_file_range_nr = atoi(nr);
682		free(nr);
683	}
684
685	return 0;
686}
687#endif
688
689static int str_random_distribution_cb(void *data, const char *str)
690{
691	struct thread_data *td = data;
692	double val;
693	char *nr;
694
695	if (parse_dryrun())
696		return 0;
697
698	if (td->o.random_distribution == FIO_RAND_DIST_ZIPF)
699		val = 1.1;
700	else if (td->o.random_distribution == FIO_RAND_DIST_PARETO)
701		val = 0.2;
702	else
703		return 0;
704
705	nr = get_opt_postfix(str);
706	if (nr && !str_to_float(nr, &val)) {
707		log_err("fio: random postfix parsing failed\n");
708		free(nr);
709		return 1;
710	}
711
712	free(nr);
713
714	if (td->o.random_distribution == FIO_RAND_DIST_ZIPF) {
715		if (val == 1.00) {
716			log_err("fio: zipf theta must different than 1.0\n");
717			return 1;
718		}
719		td->o.zipf_theta.u.f = val;
720	} else {
721		if (val <= 0.00 || val >= 1.00) {
722			log_err("fio: pareto input out of range (0 < input < 1.0)\n");
723			return 1;
724		}
725		td->o.pareto_h.u.f = val;
726	}
727
728	return 0;
729}
730
731/*
732 * Return next file in the string. Files are separated with ':'. If the ':'
733 * is escaped with a '\', then that ':' is part of the filename and does not
734 * indicate a new file.
735 */
736static char *get_next_file_name(char **ptr)
737{
738	char *str = *ptr;
739	char *p, *start;
740
741	if (!str || !strlen(str))
742		return NULL;
743
744	start = str;
745	do {
746		/*
747		 * No colon, we are done
748		 */
749		p = strchr(str, ':');
750		if (!p) {
751			*ptr = NULL;
752			break;
753		}
754
755		/*
756		 * We got a colon, but it's the first character. Skip and
757		 * continue
758		 */
759		if (p == start) {
760			str = ++start;
761			continue;
762		}
763
764		if (*(p - 1) != '\\') {
765			*p = '\0';
766			*ptr = p + 1;
767			break;
768		}
769
770		memmove(p - 1, p, strlen(p) + 1);
771		str = p;
772	} while (1);
773
774	return start;
775}
776
777static int str_filename_cb(void *data, const char *input)
778{
779	struct thread_data *td = data;
780	char *fname, *str, *p;
781
782	p = str = strdup(input);
783
784	strip_blank_front(&str);
785	strip_blank_end(str);
786
787	if (!td->files_index)
788		td->o.nr_files = 0;
789
790	while ((fname = get_next_file_name(&str)) != NULL) {
791		if (!strlen(fname))
792			break;
793		add_file(td, fname);
794		td->o.nr_files++;
795	}
796
797	free(p);
798	return 0;
799}
800
801static int str_directory_cb(void *data, const char fio_unused *str)
802{
803	struct thread_data *td = data;
804	struct stat sb;
805
806	if (parse_dryrun())
807		return 0;
808
809	if (lstat(td->o.directory, &sb) < 0) {
810		int ret = errno;
811
812		log_err("fio: %s is not a directory\n", td->o.directory);
813		td_verror(td, ret, "lstat");
814		return 1;
815	}
816	if (!S_ISDIR(sb.st_mode)) {
817		log_err("fio: %s is not a directory\n", td->o.directory);
818		return 1;
819	}
820
821	return 0;
822}
823
824static int str_opendir_cb(void *data, const char fio_unused *str)
825{
826	struct thread_data *td = data;
827
828	if (parse_dryrun())
829		return 0;
830
831	if (!td->files_index)
832		td->o.nr_files = 0;
833
834	return add_dir_files(td, td->o.opendir);
835}
836
837static int str_verify_pattern_cb(void *data, const char *input)
838{
839	struct thread_data *td = data;
840	long off;
841	int i = 0, j = 0, len, k, base = 10, pattern_length;
842	char *loc1, *loc2;
843
844	loc1 = strstr(input, "0x");
845	loc2 = strstr(input, "0X");
846	if (loc1 || loc2)
847		base = 16;
848	off = strtol(input, NULL, base);
849	if (off != LONG_MAX || errno != ERANGE) {
850		while (off) {
851			td->o.verify_pattern[i] = off & 0xff;
852			off >>= 8;
853			i++;
854		}
855	} else {
856		len = strlen(input);
857		k = len - 1;
858		if (base == 16) {
859			if (loc1)
860				j = loc1 - input + 2;
861			else
862				j = loc2 - input + 2;
863		} else
864			return 1;
865		if (len - j < MAX_PATTERN_SIZE * 2) {
866			while (k >= j) {
867				off = converthexchartoint(input[k--]);
868				if (k >= j)
869					off += (converthexchartoint(input[k--])
870						* 16);
871				td->o.verify_pattern[i++] = (char) off;
872			}
873		}
874	}
875
876	/*
877	 * Fill the pattern all the way to the end. This greatly reduces
878	 * the number of memcpy's we have to do when verifying the IO.
879	 */
880	pattern_length = i;
881	while (i > 1 && i * 2 <= MAX_PATTERN_SIZE) {
882		memcpy(&td->o.verify_pattern[i], &td->o.verify_pattern[0], i);
883		i *= 2;
884	}
885
886	/*
887	 * Fill remainder, if the pattern multiple ends up not being
888	 * MAX_PATTERN_SIZE.
889	 */
890	while (i > 1 && i < MAX_PATTERN_SIZE) {
891		unsigned int b = min(pattern_length, MAX_PATTERN_SIZE - i);
892
893		memcpy(&td->o.verify_pattern[i], &td->o.verify_pattern[0], b);
894		i += b;
895	}
896
897	if (i == 1) {
898		/*
899		 * The code in verify_io_u_pattern assumes a single byte pattern
900		 * fills the whole verify pattern buffer.
901		 */
902		memset(td->o.verify_pattern, td->o.verify_pattern[0],
903		       MAX_PATTERN_SIZE);
904	}
905
906	td->o.verify_pattern_bytes = i;
907
908	/*
909	 * VERIFY_META could already be set
910	 */
911	if (td->o.verify == VERIFY_NONE)
912		td->o.verify = VERIFY_PATTERN;
913
914	return 0;
915}
916
917static int str_gtod_reduce_cb(void *data, int *il)
918{
919	struct thread_data *td = data;
920	int val = *il;
921
922	td->o.disable_lat = !!val;
923	td->o.disable_clat = !!val;
924	td->o.disable_slat = !!val;
925	td->o.disable_bw = !!val;
926	td->o.clat_percentiles = !val;
927	if (val)
928		td->tv_cache_mask = 63;
929
930	return 0;
931}
932
933static int str_gtod_cpu_cb(void *data, long long *il)
934{
935	struct thread_data *td = data;
936	int val = *il;
937
938	td->o.gtod_cpu = val;
939	td->o.gtod_offload = 1;
940	return 0;
941}
942
943static int str_size_cb(void *data, unsigned long long *__val)
944{
945	struct thread_data *td = data;
946	unsigned long long v = *__val;
947
948	if (parse_is_percent(v)) {
949		td->o.size = 0;
950		td->o.size_percent = -1ULL - v;
951	} else
952		td->o.size = v;
953
954	return 0;
955}
956
957static int rw_verify(struct fio_option *o, void *data)
958{
959	struct thread_data *td = data;
960
961	if (read_only && td_write(td)) {
962		log_err("fio: job <%s> has write bit set, but fio is in"
963			" read-only mode\n", td->o.name);
964		return 1;
965	}
966
967	return 0;
968}
969
970static int gtod_cpu_verify(struct fio_option *o, void *data)
971{
972#ifndef FIO_HAVE_CPU_AFFINITY
973	struct thread_data *td = data;
974
975	if (td->o.gtod_cpu) {
976		log_err("fio: platform must support CPU affinity for"
977			"gettimeofday() offloading\n");
978		return 1;
979	}
980#endif
981
982	return 0;
983}
984
985/*
986 * Option grouping
987 */
988static struct opt_group fio_opt_groups[] = {
989	{
990		.name	= "General",
991		.mask	= FIO_OPT_C_GENERAL,
992	},
993	{
994		.name	= "I/O",
995		.mask	= FIO_OPT_C_IO,
996	},
997	{
998		.name	= "File",
999		.mask	= FIO_OPT_C_FILE,
1000	},
1001	{
1002		.name	= "Statistics",
1003		.mask	= FIO_OPT_C_STAT,
1004	},
1005	{
1006		.name	= "Logging",
1007		.mask	= FIO_OPT_C_LOG,
1008	},
1009	{
1010		.name	= "Profiles",
1011		.mask	= FIO_OPT_C_PROFILE,
1012	},
1013	{
1014		.name	= NULL,
1015	},
1016};
1017
1018static struct opt_group *__opt_group_from_mask(struct opt_group *ogs, unsigned int *mask,
1019					       unsigned int inv_mask)
1020{
1021	struct opt_group *og;
1022	int i;
1023
1024	if (*mask == inv_mask || !*mask)
1025		return NULL;
1026
1027	for (i = 0; ogs[i].name; i++) {
1028		og = &ogs[i];
1029
1030		if (*mask & og->mask) {
1031			*mask &= ~(og->mask);
1032			return og;
1033		}
1034	}
1035
1036	return NULL;
1037}
1038
1039struct opt_group *opt_group_from_mask(unsigned int *mask)
1040{
1041	return __opt_group_from_mask(fio_opt_groups, mask, FIO_OPT_C_INVALID);
1042}
1043
1044static struct opt_group fio_opt_cat_groups[] = {
1045	{
1046		.name	= "Rate",
1047		.mask	= FIO_OPT_G_RATE,
1048	},
1049	{
1050		.name	= "Zone",
1051		.mask	= FIO_OPT_G_ZONE,
1052	},
1053	{
1054		.name	= "Read/write mix",
1055		.mask	= FIO_OPT_G_RWMIX,
1056	},
1057	{
1058		.name	= "Verify",
1059		.mask	= FIO_OPT_G_VERIFY,
1060	},
1061	{
1062		.name	= "Trim",
1063		.mask	= FIO_OPT_G_TRIM,
1064	},
1065	{
1066		.name	= "I/O Logging",
1067		.mask	= FIO_OPT_G_IOLOG,
1068	},
1069	{
1070		.name	= "I/O Depth",
1071		.mask	= FIO_OPT_G_IO_DEPTH,
1072	},
1073	{
1074		.name	= "I/O Flow",
1075		.mask	= FIO_OPT_G_IO_FLOW,
1076	},
1077	{
1078		.name	= "Description",
1079		.mask	= FIO_OPT_G_DESC,
1080	},
1081	{
1082		.name	= "Filename",
1083		.mask	= FIO_OPT_G_FILENAME,
1084	},
1085	{
1086		.name	= "General I/O",
1087		.mask	= FIO_OPT_G_IO_BASIC,
1088	},
1089	{
1090		.name	= "Cgroups",
1091		.mask	= FIO_OPT_G_CGROUP,
1092	},
1093	{
1094		.name	= "Runtime",
1095		.mask	= FIO_OPT_G_RUNTIME,
1096	},
1097	{
1098		.name	= "Process",
1099		.mask	= FIO_OPT_G_PROCESS,
1100	},
1101	{
1102		.name	= "Job credentials / priority",
1103		.mask	= FIO_OPT_G_CRED,
1104	},
1105	{
1106		.name	= "Clock settings",
1107		.mask	= FIO_OPT_G_CLOCK,
1108	},
1109	{
1110		.name	= "I/O Type",
1111		.mask	= FIO_OPT_G_IO_TYPE,
1112	},
1113	{
1114		.name	= "I/O Thinktime",
1115		.mask	= FIO_OPT_G_THINKTIME,
1116	},
1117	{
1118		.name	= "Randomizations",
1119		.mask	= FIO_OPT_G_RANDOM,
1120	},
1121	{
1122		.name	= "I/O buffers",
1123		.mask	= FIO_OPT_G_IO_BUF,
1124	},
1125	{
1126		.name	= "Tiobench profile",
1127		.mask	= FIO_OPT_G_TIOBENCH,
1128	},
1129
1130	{
1131		.name	= NULL,
1132	}
1133};
1134
1135struct opt_group *opt_group_cat_from_mask(unsigned int *mask)
1136{
1137	return __opt_group_from_mask(fio_opt_cat_groups, mask, FIO_OPT_G_INVALID);
1138}
1139
1140/*
1141 * Map of job/command line options
1142 */
1143struct fio_option fio_options[FIO_MAX_OPTS] = {
1144	{
1145		.name	= "description",
1146		.lname	= "Description of job",
1147		.type	= FIO_OPT_STR_STORE,
1148		.off1	= td_var_offset(description),
1149		.help	= "Text job description",
1150		.category = FIO_OPT_C_GENERAL,
1151		.group	= FIO_OPT_G_DESC,
1152	},
1153	{
1154		.name	= "name",
1155		.lname	= "Job name",
1156		.type	= FIO_OPT_STR_STORE,
1157		.off1	= td_var_offset(name),
1158		.help	= "Name of this job",
1159		.category = FIO_OPT_C_GENERAL,
1160		.group	= FIO_OPT_G_DESC,
1161	},
1162	{
1163		.name	= "filename",
1164		.lname	= "Filename(s)",
1165		.type	= FIO_OPT_STR_STORE,
1166		.off1	= td_var_offset(filename),
1167		.cb	= str_filename_cb,
1168		.prio	= -1, /* must come after "directory" */
1169		.help	= "File(s) to use for the workload",
1170		.category = FIO_OPT_C_FILE,
1171		.group	= FIO_OPT_G_FILENAME,
1172	},
1173	{
1174		.name	= "directory",
1175		.lname	= "Directory",
1176		.type	= FIO_OPT_STR_STORE,
1177		.off1	= td_var_offset(directory),
1178		.cb	= str_directory_cb,
1179		.help	= "Directory to store files in",
1180		.category = FIO_OPT_C_FILE,
1181		.group	= FIO_OPT_G_FILENAME,
1182	},
1183	{
1184		.name	= "filename_format",
1185		.type	= FIO_OPT_STR_STORE,
1186		.off1	= td_var_offset(filename_format),
1187		.prio	= -1, /* must come after "directory" */
1188		.help	= "Override default $jobname.$jobnum.$filenum naming",
1189		.def	= "$jobname.$jobnum.$filenum",
1190		.category = FIO_OPT_C_FILE,
1191		.group	= FIO_OPT_G_FILENAME,
1192	},
1193	{
1194		.name	= "lockfile",
1195		.lname	= "Lockfile",
1196		.type	= FIO_OPT_STR,
1197		.off1	= td_var_offset(file_lock_mode),
1198		.help	= "Lock file when doing IO to it",
1199		.parent	= "filename",
1200		.hide	= 0,
1201		.def	= "none",
1202		.category = FIO_OPT_C_FILE,
1203		.group	= FIO_OPT_G_FILENAME,
1204		.posval = {
1205			  { .ival = "none",
1206			    .oval = FILE_LOCK_NONE,
1207			    .help = "No file locking",
1208			  },
1209			  { .ival = "exclusive",
1210			    .oval = FILE_LOCK_EXCLUSIVE,
1211			    .help = "Exclusive file lock",
1212			  },
1213			  {
1214			    .ival = "readwrite",
1215			    .oval = FILE_LOCK_READWRITE,
1216			    .help = "Read vs write lock",
1217			  },
1218		},
1219	},
1220	{
1221		.name	= "opendir",
1222		.lname	= "Open directory",
1223		.type	= FIO_OPT_STR_STORE,
1224		.off1	= td_var_offset(opendir),
1225		.cb	= str_opendir_cb,
1226		.help	= "Recursively add files from this directory and down",
1227		.category = FIO_OPT_C_FILE,
1228		.group	= FIO_OPT_G_FILENAME,
1229	},
1230	{
1231		.name	= "rw",
1232		.lname	= "Read/write",
1233		.alias	= "readwrite",
1234		.type	= FIO_OPT_STR,
1235		.cb	= str_rw_cb,
1236		.off1	= td_var_offset(td_ddir),
1237		.help	= "IO direction",
1238		.def	= "read",
1239		.verify	= rw_verify,
1240		.category = FIO_OPT_C_IO,
1241		.group	= FIO_OPT_G_IO_BASIC,
1242		.posval = {
1243			  { .ival = "read",
1244			    .oval = TD_DDIR_READ,
1245			    .help = "Sequential read",
1246			  },
1247			  { .ival = "write",
1248			    .oval = TD_DDIR_WRITE,
1249			    .help = "Sequential write",
1250			  },
1251			  { .ival = "trim",
1252			    .oval = TD_DDIR_TRIM,
1253			    .help = "Sequential trim",
1254			  },
1255			  { .ival = "randread",
1256			    .oval = TD_DDIR_RANDREAD,
1257			    .help = "Random read",
1258			  },
1259			  { .ival = "randwrite",
1260			    .oval = TD_DDIR_RANDWRITE,
1261			    .help = "Random write",
1262			  },
1263			  { .ival = "randtrim",
1264			    .oval = TD_DDIR_RANDTRIM,
1265			    .help = "Random trim",
1266			  },
1267			  { .ival = "rw",
1268			    .oval = TD_DDIR_RW,
1269			    .help = "Sequential read and write mix",
1270			  },
1271			  { .ival = "readwrite",
1272			    .oval = TD_DDIR_RW,
1273			    .help = "Sequential read and write mix",
1274			  },
1275			  { .ival = "randrw",
1276			    .oval = TD_DDIR_RANDRW,
1277			    .help = "Random read and write mix"
1278			  },
1279		},
1280	},
1281	{
1282		.name	= "rw_sequencer",
1283		.lname	= "RW Sequencer",
1284		.type	= FIO_OPT_STR,
1285		.off1	= td_var_offset(rw_seq),
1286		.help	= "IO offset generator modifier",
1287		.def	= "sequential",
1288		.category = FIO_OPT_C_IO,
1289		.group	= FIO_OPT_G_IO_BASIC,
1290		.posval = {
1291			  { .ival = "sequential",
1292			    .oval = RW_SEQ_SEQ,
1293			    .help = "Generate sequential offsets",
1294			  },
1295			  { .ival = "identical",
1296			    .oval = RW_SEQ_IDENT,
1297			    .help = "Generate identical offsets",
1298			  },
1299		},
1300	},
1301
1302	{
1303		.name	= "ioengine",
1304		.lname	= "IO Engine",
1305		.type	= FIO_OPT_STR_STORE,
1306		.off1	= td_var_offset(ioengine),
1307		.help	= "IO engine to use",
1308		.def	= FIO_PREFERRED_ENGINE,
1309		.category = FIO_OPT_C_IO,
1310		.group	= FIO_OPT_G_IO_BASIC,
1311		.posval	= {
1312			  { .ival = "sync",
1313			    .help = "Use read/write",
1314			  },
1315			  { .ival = "psync",
1316			    .help = "Use pread/pwrite",
1317			  },
1318			  { .ival = "vsync",
1319			    .help = "Use readv/writev",
1320			  },
1321#ifdef CONFIG_PWRITEV
1322			  { .ival = "pvsync",
1323			    .help = "Use preadv/pwritev",
1324			  },
1325#endif
1326#ifdef CONFIG_LIBAIO
1327			  { .ival = "libaio",
1328			    .help = "Linux native asynchronous IO",
1329			  },
1330#endif
1331#ifdef CONFIG_POSIXAIO
1332			  { .ival = "posixaio",
1333			    .help = "POSIX asynchronous IO",
1334			  },
1335#endif
1336#ifdef CONFIG_SOLARISAIO
1337			  { .ival = "solarisaio",
1338			    .help = "Solaris native asynchronous IO",
1339			  },
1340#endif
1341#ifdef CONFIG_WINDOWSAIO
1342			  { .ival = "windowsaio",
1343			    .help = "Windows native asynchronous IO"
1344			  },
1345#endif
1346			  { .ival = "mmap",
1347			    .help = "Memory mapped IO"
1348			  },
1349#ifdef CONFIG_LINUX_SPLICE
1350			  { .ival = "splice",
1351			    .help = "splice/vmsplice based IO",
1352			  },
1353			  { .ival = "netsplice",
1354			    .help = "splice/vmsplice to/from the network",
1355			  },
1356#endif
1357#ifdef FIO_HAVE_SGIO
1358			  { .ival = "sg",
1359			    .help = "SCSI generic v3 IO",
1360			  },
1361#endif
1362			  { .ival = "null",
1363			    .help = "Testing engine (no data transfer)",
1364			  },
1365			  { .ival = "net",
1366			    .help = "Network IO",
1367			  },
1368			  { .ival = "cpuio",
1369			    .help = "CPU cycle burner engine",
1370			  },
1371#ifdef CONFIG_GUASI
1372			  { .ival = "guasi",
1373			    .help = "GUASI IO engine",
1374			  },
1375#endif
1376#ifdef FIO_HAVE_BINJECT
1377			  { .ival = "binject",
1378			    .help = "binject direct inject block engine",
1379			  },
1380#endif
1381#ifdef CONFIG_RDMA
1382			  { .ival = "rdma",
1383			    .help = "RDMA IO engine",
1384			  },
1385#endif
1386#ifdef CONFIG_FUSION_AW
1387			  { .ival = "fusion-aw-sync",
1388			    .help = "Fusion-io atomic write engine",
1389			  },
1390#endif
1391#ifdef CONFIG_LINUX_EXT4_MOVE_EXTENT
1392			  { .ival = "e4defrag",
1393			    .help = "ext4 defrag engine",
1394			  },
1395#endif
1396#ifdef CONFIG_LINUX_FALLOCATE
1397			  { .ival = "falloc",
1398			    .help = "fallocate() file based engine",
1399			  },
1400#endif
1401			  { .ival = "external",
1402			    .help = "Load external engine (append name)",
1403			  },
1404		},
1405	},
1406	{
1407		.name	= "iodepth",
1408		.lname	= "IO Depth",
1409		.type	= FIO_OPT_INT,
1410		.off1	= td_var_offset(iodepth),
1411		.help	= "Number of IO buffers to keep in flight",
1412		.minval = 1,
1413		.interval = 1,
1414		.def	= "1",
1415		.category = FIO_OPT_C_IO,
1416		.group	= FIO_OPT_G_IO_BASIC,
1417	},
1418	{
1419		.name	= "iodepth_batch",
1420		.lname	= "IO Depth batch",
1421		.alias	= "iodepth_batch_submit",
1422		.type	= FIO_OPT_INT,
1423		.off1	= td_var_offset(iodepth_batch),
1424		.help	= "Number of IO buffers to submit in one go",
1425		.parent	= "iodepth",
1426		.hide	= 1,
1427		.minval	= 1,
1428		.interval = 1,
1429		.def	= "1",
1430		.category = FIO_OPT_C_IO,
1431		.group	= FIO_OPT_G_IO_BASIC,
1432	},
1433	{
1434		.name	= "iodepth_batch_complete",
1435		.lname	= "IO Depth batch complete",
1436		.type	= FIO_OPT_INT,
1437		.off1	= td_var_offset(iodepth_batch_complete),
1438		.help	= "Number of IO buffers to retrieve in one go",
1439		.parent	= "iodepth",
1440		.hide	= 1,
1441		.minval	= 0,
1442		.interval = 1,
1443		.def	= "1",
1444		.category = FIO_OPT_C_IO,
1445		.group	= FIO_OPT_G_IO_BASIC,
1446	},
1447	{
1448		.name	= "iodepth_low",
1449		.lname	= "IO Depth batch low",
1450		.type	= FIO_OPT_INT,
1451		.off1	= td_var_offset(iodepth_low),
1452		.help	= "Low water mark for queuing depth",
1453		.parent	= "iodepth",
1454		.hide	= 1,
1455		.interval = 1,
1456		.category = FIO_OPT_C_IO,
1457		.group	= FIO_OPT_G_IO_BASIC,
1458	},
1459	{
1460		.name	= "size",
1461		.lname	= "Size",
1462		.type	= FIO_OPT_STR_VAL,
1463		.cb	= str_size_cb,
1464		.help	= "Total size of device or files",
1465		.interval = 1024 * 1024,
1466		.category = FIO_OPT_C_IO,
1467		.group	= FIO_OPT_G_INVALID,
1468	},
1469	{
1470		.name	= "fill_device",
1471		.lname	= "Fill device",
1472		.alias	= "fill_fs",
1473		.type	= FIO_OPT_BOOL,
1474		.off1	= td_var_offset(fill_device),
1475		.help	= "Write until an ENOSPC error occurs",
1476		.def	= "0",
1477		.category = FIO_OPT_C_FILE,
1478		.group	= FIO_OPT_G_INVALID,
1479	},
1480	{
1481		.name	= "filesize",
1482		.lname	= "File size",
1483		.type	= FIO_OPT_STR_VAL,
1484		.off1	= td_var_offset(file_size_low),
1485		.off2	= td_var_offset(file_size_high),
1486		.minval = 1,
1487		.help	= "Size of individual files",
1488		.interval = 1024 * 1024,
1489		.category = FIO_OPT_C_FILE,
1490		.group	= FIO_OPT_G_INVALID,
1491	},
1492	{
1493		.name	= "offset",
1494		.lname	= "IO offset",
1495		.alias	= "fileoffset",
1496		.type	= FIO_OPT_STR_VAL,
1497		.off1	= td_var_offset(start_offset),
1498		.help	= "Start IO from this offset",
1499		.def	= "0",
1500		.interval = 1024 * 1024,
1501		.category = FIO_OPT_C_IO,
1502		.group	= FIO_OPT_G_INVALID,
1503	},
1504	{
1505		.name	= "offset_increment",
1506		.lname	= "IO offset increment",
1507		.type	= FIO_OPT_STR_VAL,
1508		.off1	= td_var_offset(offset_increment),
1509		.help	= "What is the increment from one offset to the next",
1510		.parent = "offset",
1511		.hide	= 1,
1512		.def	= "0",
1513		.interval = 1024 * 1024,
1514		.category = FIO_OPT_C_IO,
1515		.group	= FIO_OPT_G_INVALID,
1516	},
1517	{
1518		.name	= "number_ios",
1519		.lname	= "Number of IOs to perform",
1520		.type	= FIO_OPT_STR_VAL,
1521		.off1	= td_var_offset(number_ios),
1522		.help	= "Force job completion of this number of IOs",
1523		.def	= "0",
1524		.category = FIO_OPT_C_IO,
1525		.group	= FIO_OPT_G_INVALID,
1526	},
1527	{
1528		.name	= "bs",
1529		.lname	= "Block size",
1530		.alias	= "blocksize",
1531		.type	= FIO_OPT_INT,
1532		.off1	= td_var_offset(bs[DDIR_READ]),
1533		.off2	= td_var_offset(bs[DDIR_WRITE]),
1534		.off3	= td_var_offset(bs[DDIR_TRIM]),
1535		.minval = 1,
1536		.help	= "Block size unit",
1537		.def	= "4k",
1538		.parent = "rw",
1539		.hide	= 1,
1540		.interval = 512,
1541		.category = FIO_OPT_C_IO,
1542		.group	= FIO_OPT_G_INVALID,
1543	},
1544	{
1545		.name	= "ba",
1546		.lname	= "Block size align",
1547		.alias	= "blockalign",
1548		.type	= FIO_OPT_INT,
1549		.off1	= td_var_offset(ba[DDIR_READ]),
1550		.off2	= td_var_offset(ba[DDIR_WRITE]),
1551		.off3	= td_var_offset(ba[DDIR_TRIM]),
1552		.minval	= 1,
1553		.help	= "IO block offset alignment",
1554		.parent	= "rw",
1555		.hide	= 1,
1556		.interval = 512,
1557		.category = FIO_OPT_C_IO,
1558		.group	= FIO_OPT_G_INVALID,
1559	},
1560	{
1561		.name	= "bsrange",
1562		.lname	= "Block size range",
1563		.alias	= "blocksize_range",
1564		.type	= FIO_OPT_RANGE,
1565		.off1	= td_var_offset(min_bs[DDIR_READ]),
1566		.off2	= td_var_offset(max_bs[DDIR_READ]),
1567		.off3	= td_var_offset(min_bs[DDIR_WRITE]),
1568		.off4	= td_var_offset(max_bs[DDIR_WRITE]),
1569		.off5	= td_var_offset(min_bs[DDIR_TRIM]),
1570		.off6	= td_var_offset(max_bs[DDIR_TRIM]),
1571		.minval = 1,
1572		.help	= "Set block size range (in more detail than bs)",
1573		.parent = "rw",
1574		.hide	= 1,
1575		.interval = 4096,
1576		.category = FIO_OPT_C_IO,
1577		.group	= FIO_OPT_G_INVALID,
1578	},
1579	{
1580		.name	= "bssplit",
1581		.lname	= "Block size split",
1582		.type	= FIO_OPT_STR,
1583		.cb	= str_bssplit_cb,
1584		.help	= "Set a specific mix of block sizes",
1585		.parent	= "rw",
1586		.hide	= 1,
1587		.category = FIO_OPT_C_IO,
1588		.group	= FIO_OPT_G_INVALID,
1589	},
1590	{
1591		.name	= "bs_unaligned",
1592		.lname	= "Block size unaligned",
1593		.alias	= "blocksize_unaligned",
1594		.type	= FIO_OPT_STR_SET,
1595		.off1	= td_var_offset(bs_unaligned),
1596		.help	= "Don't sector align IO buffer sizes",
1597		.parent = "rw",
1598		.hide	= 1,
1599		.category = FIO_OPT_C_IO,
1600		.group	= FIO_OPT_G_INVALID,
1601	},
1602	{
1603		.name	= "bs_is_seq_rand",
1604		.lname	= "Block size division is seq/random (not read/write)",
1605		.type	= FIO_OPT_BOOL,
1606		.off1	= td_var_offset(bs_is_seq_rand),
1607		.help	= "Consider any blocksize setting to be sequential,ramdom",
1608		.def	= "0",
1609		.parent = "blocksize",
1610		.category = FIO_OPT_C_IO,
1611		.group	= FIO_OPT_G_INVALID,
1612	},
1613	{
1614		.name	= "randrepeat",
1615		.lname	= "Random repeatable",
1616		.type	= FIO_OPT_BOOL,
1617		.off1	= td_var_offset(rand_repeatable),
1618		.help	= "Use repeatable random IO pattern",
1619		.def	= "1",
1620		.parent = "rw",
1621		.hide	= 1,
1622		.category = FIO_OPT_C_IO,
1623		.group	= FIO_OPT_G_RANDOM,
1624	},
1625	{
1626		.name	= "use_os_rand",
1627		.lname	= "Use OS random",
1628		.type	= FIO_OPT_BOOL,
1629		.off1	= td_var_offset(use_os_rand),
1630		.help	= "Set to use OS random generator",
1631		.def	= "0",
1632		.parent = "rw",
1633		.hide	= 1,
1634		.category = FIO_OPT_C_IO,
1635		.group	= FIO_OPT_G_RANDOM,
1636	},
1637	{
1638		.name	= "norandommap",
1639		.lname	= "No randommap",
1640		.type	= FIO_OPT_STR_SET,
1641		.off1	= td_var_offset(norandommap),
1642		.help	= "Accept potential duplicate random blocks",
1643		.parent = "rw",
1644		.hide	= 1,
1645		.hide_on_set = 1,
1646		.category = FIO_OPT_C_IO,
1647		.group	= FIO_OPT_G_RANDOM,
1648	},
1649	{
1650		.name	= "softrandommap",
1651		.lname	= "Soft randommap",
1652		.type	= FIO_OPT_BOOL,
1653		.off1	= td_var_offset(softrandommap),
1654		.help	= "Set norandommap if randommap allocation fails",
1655		.parent	= "norandommap",
1656		.hide	= 1,
1657		.def	= "0",
1658		.category = FIO_OPT_C_IO,
1659		.group	= FIO_OPT_G_RANDOM,
1660	},
1661	{
1662		.name	= "random_generator",
1663		.type	= FIO_OPT_STR,
1664		.off1	= td_var_offset(random_generator),
1665		.help	= "Type of random number generator to use",
1666		.def	= "tausworthe",
1667		.posval	= {
1668			  { .ival = "tausworthe",
1669			    .oval = FIO_RAND_GEN_TAUSWORTHE,
1670			    .help = "Strong Tausworthe generator",
1671			  },
1672			  { .ival = "lfsr",
1673			    .oval = FIO_RAND_GEN_LFSR,
1674			    .help = "Variable length LFSR",
1675			  },
1676		},
1677		.category = FIO_OPT_C_IO,
1678		.group	= FIO_OPT_G_RANDOM,
1679	},
1680	{
1681		.name	= "random_distribution",
1682		.type	= FIO_OPT_STR,
1683		.off1	= td_var_offset(random_distribution),
1684		.cb	= str_random_distribution_cb,
1685		.help	= "Random offset distribution generator",
1686		.def	= "random",
1687		.posval	= {
1688			  { .ival = "random",
1689			    .oval = FIO_RAND_DIST_RANDOM,
1690			    .help = "Completely random",
1691			  },
1692			  { .ival = "zipf",
1693			    .oval = FIO_RAND_DIST_ZIPF,
1694			    .help = "Zipf distribution",
1695			  },
1696			  { .ival = "pareto",
1697			    .oval = FIO_RAND_DIST_PARETO,
1698			    .help = "Pareto distribution",
1699			  },
1700		},
1701		.category = FIO_OPT_C_IO,
1702		.group	= FIO_OPT_G_RANDOM,
1703	},
1704	{
1705		.name	= "percentage_random",
1706		.lname	= "Percentage Random",
1707		.type	= FIO_OPT_INT,
1708		.off1	= td_var_offset(perc_rand[DDIR_READ]),
1709		.off2	= td_var_offset(perc_rand[DDIR_WRITE]),
1710		.off3	= td_var_offset(perc_rand[DDIR_TRIM]),
1711		.maxval	= 100,
1712		.help	= "Percentage of seq/random mix that should be random",
1713		.def	= "100,100,100",
1714		.interval = 5,
1715		.inverse = "percentage_sequential",
1716		.category = FIO_OPT_C_IO,
1717		.group	= FIO_OPT_G_RANDOM,
1718	},
1719	{
1720		.name	= "percentage_sequential",
1721		.lname	= "Percentage Sequential",
1722		.type	= FIO_OPT_DEPRECATED,
1723		.category = FIO_OPT_C_IO,
1724		.group	= FIO_OPT_G_RANDOM,
1725	},
1726	{
1727		.name	= "nrfiles",
1728		.lname	= "Number of files",
1729		.alias	= "nr_files",
1730		.type	= FIO_OPT_INT,
1731		.off1	= td_var_offset(nr_files),
1732		.help	= "Split job workload between this number of files",
1733		.def	= "1",
1734		.interval = 1,
1735		.category = FIO_OPT_C_FILE,
1736		.group	= FIO_OPT_G_INVALID,
1737	},
1738	{
1739		.name	= "openfiles",
1740		.lname	= "Number of open files",
1741		.type	= FIO_OPT_INT,
1742		.off1	= td_var_offset(open_files),
1743		.help	= "Number of files to keep open at the same time",
1744		.category = FIO_OPT_C_FILE,
1745		.group	= FIO_OPT_G_INVALID,
1746	},
1747	{
1748		.name	= "file_service_type",
1749		.lname	= "File service type",
1750		.type	= FIO_OPT_STR,
1751		.cb	= str_fst_cb,
1752		.off1	= td_var_offset(file_service_type),
1753		.help	= "How to select which file to service next",
1754		.def	= "roundrobin",
1755		.category = FIO_OPT_C_FILE,
1756		.group	= FIO_OPT_G_INVALID,
1757		.posval	= {
1758			  { .ival = "random",
1759			    .oval = FIO_FSERVICE_RANDOM,
1760			    .help = "Choose a file at random",
1761			  },
1762			  { .ival = "roundrobin",
1763			    .oval = FIO_FSERVICE_RR,
1764			    .help = "Round robin select files",
1765			  },
1766			  { .ival = "sequential",
1767			    .oval = FIO_FSERVICE_SEQ,
1768			    .help = "Finish one file before moving to the next",
1769			  },
1770		},
1771		.parent = "nrfiles",
1772		.hide	= 1,
1773	},
1774#ifdef CONFIG_POSIX_FALLOCATE
1775	{
1776		.name	= "fallocate",
1777		.lname	= "Fallocate",
1778		.type	= FIO_OPT_STR,
1779		.off1	= td_var_offset(fallocate_mode),
1780		.help	= "Whether pre-allocation is performed when laying out files",
1781		.def	= "posix",
1782		.category = FIO_OPT_C_FILE,
1783		.group	= FIO_OPT_G_INVALID,
1784		.posval	= {
1785			  { .ival = "none",
1786			    .oval = FIO_FALLOCATE_NONE,
1787			    .help = "Do not pre-allocate space",
1788			  },
1789			  { .ival = "posix",
1790			    .oval = FIO_FALLOCATE_POSIX,
1791			    .help = "Use posix_fallocate()",
1792			  },
1793#ifdef CONFIG_LINUX_FALLOCATE
1794			  { .ival = "keep",
1795			    .oval = FIO_FALLOCATE_KEEP_SIZE,
1796			    .help = "Use fallocate(..., FALLOC_FL_KEEP_SIZE, ...)",
1797			  },
1798#endif
1799			  /* Compatibility with former boolean values */
1800			  { .ival = "0",
1801			    .oval = FIO_FALLOCATE_NONE,
1802			    .help = "Alias for 'none'",
1803			  },
1804			  { .ival = "1",
1805			    .oval = FIO_FALLOCATE_POSIX,
1806			    .help = "Alias for 'posix'",
1807			  },
1808		},
1809	},
1810#endif	/* CONFIG_POSIX_FALLOCATE */
1811	{
1812		.name	= "fadvise_hint",
1813		.lname	= "Fadvise hint",
1814		.type	= FIO_OPT_BOOL,
1815		.off1	= td_var_offset(fadvise_hint),
1816		.help	= "Use fadvise() to advise the kernel on IO pattern",
1817		.def	= "1",
1818		.category = FIO_OPT_C_FILE,
1819		.group	= FIO_OPT_G_INVALID,
1820	},
1821	{
1822		.name	= "fsync",
1823		.lname	= "Fsync",
1824		.type	= FIO_OPT_INT,
1825		.off1	= td_var_offset(fsync_blocks),
1826		.help	= "Issue fsync for writes every given number of blocks",
1827		.def	= "0",
1828		.interval = 1,
1829		.category = FIO_OPT_C_FILE,
1830		.group	= FIO_OPT_G_INVALID,
1831	},
1832	{
1833		.name	= "fdatasync",
1834		.lname	= "Fdatasync",
1835		.type	= FIO_OPT_INT,
1836		.off1	= td_var_offset(fdatasync_blocks),
1837		.help	= "Issue fdatasync for writes every given number of blocks",
1838		.def	= "0",
1839		.interval = 1,
1840		.category = FIO_OPT_C_FILE,
1841		.group	= FIO_OPT_G_INVALID,
1842	},
1843	{
1844		.name	= "write_barrier",
1845		.lname	= "Write barrier",
1846		.type	= FIO_OPT_INT,
1847		.off1	= td_var_offset(barrier_blocks),
1848		.help	= "Make every Nth write a barrier write",
1849		.def	= "0",
1850		.interval = 1,
1851		.category = FIO_OPT_C_IO,
1852		.group	= FIO_OPT_G_INVALID,
1853	},
1854#ifdef CONFIG_SYNC_FILE_RANGE
1855	{
1856		.name	= "sync_file_range",
1857		.lname	= "Sync file range",
1858		.posval	= {
1859			  { .ival = "wait_before",
1860			    .oval = SYNC_FILE_RANGE_WAIT_BEFORE,
1861			    .help = "SYNC_FILE_RANGE_WAIT_BEFORE",
1862			    .or	  = 1,
1863			  },
1864			  { .ival = "write",
1865			    .oval = SYNC_FILE_RANGE_WRITE,
1866			    .help = "SYNC_FILE_RANGE_WRITE",
1867			    .or	  = 1,
1868			  },
1869			  {
1870			    .ival = "wait_after",
1871			    .oval = SYNC_FILE_RANGE_WAIT_AFTER,
1872			    .help = "SYNC_FILE_RANGE_WAIT_AFTER",
1873			    .or	  = 1,
1874			  },
1875		},
1876		.type	= FIO_OPT_STR_MULTI,
1877		.cb	= str_sfr_cb,
1878		.off1	= td_var_offset(sync_file_range),
1879		.help	= "Use sync_file_range()",
1880		.category = FIO_OPT_C_FILE,
1881		.group	= FIO_OPT_G_INVALID,
1882	},
1883#endif
1884	{
1885		.name	= "direct",
1886		.lname	= "Direct I/O",
1887		.type	= FIO_OPT_BOOL,
1888		.off1	= td_var_offset(odirect),
1889		.help	= "Use O_DIRECT IO (negates buffered)",
1890		.def	= "0",
1891		.inverse = "buffered",
1892		.category = FIO_OPT_C_IO,
1893		.group	= FIO_OPT_G_IO_TYPE,
1894	},
1895	{
1896		.name	= "atomic",
1897		.lname	= "Atomic I/O",
1898		.type	= FIO_OPT_BOOL,
1899		.off1	= td_var_offset(oatomic),
1900		.help	= "Use Atomic IO with O_DIRECT (implies O_DIRECT)",
1901		.def	= "0",
1902		.category = FIO_OPT_C_IO,
1903		.group	= FIO_OPT_G_IO_TYPE,
1904	},
1905	{
1906		.name	= "buffered",
1907		.lname	= "Buffered I/O",
1908		.type	= FIO_OPT_BOOL,
1909		.off1	= td_var_offset(odirect),
1910		.neg	= 1,
1911		.help	= "Use buffered IO (negates direct)",
1912		.def	= "1",
1913		.inverse = "direct",
1914		.category = FIO_OPT_C_IO,
1915		.group	= FIO_OPT_G_IO_TYPE,
1916	},
1917	{
1918		.name	= "overwrite",
1919		.lname	= "Overwrite",
1920		.type	= FIO_OPT_BOOL,
1921		.off1	= td_var_offset(overwrite),
1922		.help	= "When writing, set whether to overwrite current data",
1923		.def	= "0",
1924		.category = FIO_OPT_C_FILE,
1925		.group	= FIO_OPT_G_INVALID,
1926	},
1927	{
1928		.name	= "loops",
1929		.lname	= "Loops",
1930		.type	= FIO_OPT_INT,
1931		.off1	= td_var_offset(loops),
1932		.help	= "Number of times to run the job",
1933		.def	= "1",
1934		.interval = 1,
1935		.category = FIO_OPT_C_GENERAL,
1936		.group	= FIO_OPT_G_RUNTIME,
1937	},
1938	{
1939		.name	= "numjobs",
1940		.lname	= "Number of jobs",
1941		.type	= FIO_OPT_INT,
1942		.off1	= td_var_offset(numjobs),
1943		.help	= "Duplicate this job this many times",
1944		.def	= "1",
1945		.interval = 1,
1946		.category = FIO_OPT_C_GENERAL,
1947		.group	= FIO_OPT_G_RUNTIME,
1948	},
1949	{
1950		.name	= "startdelay",
1951		.lname	= "Start delay",
1952		.type	= FIO_OPT_STR_VAL_TIME,
1953		.off1	= td_var_offset(start_delay),
1954		.help	= "Only start job when this period has passed",
1955		.def	= "0",
1956		.category = FIO_OPT_C_GENERAL,
1957		.group	= FIO_OPT_G_RUNTIME,
1958	},
1959	{
1960		.name	= "runtime",
1961		.lname	= "Runtime",
1962		.alias	= "timeout",
1963		.type	= FIO_OPT_STR_VAL_TIME,
1964		.off1	= td_var_offset(timeout),
1965		.help	= "Stop workload when this amount of time has passed",
1966		.def	= "0",
1967		.category = FIO_OPT_C_GENERAL,
1968		.group	= FIO_OPT_G_RUNTIME,
1969	},
1970	{
1971		.name	= "time_based",
1972		.lname	= "Time based",
1973		.type	= FIO_OPT_STR_SET,
1974		.off1	= td_var_offset(time_based),
1975		.help	= "Keep running until runtime/timeout is met",
1976		.category = FIO_OPT_C_GENERAL,
1977		.group	= FIO_OPT_G_RUNTIME,
1978	},
1979	{
1980		.name	= "ramp_time",
1981		.lname	= "Ramp time",
1982		.type	= FIO_OPT_STR_VAL_TIME,
1983		.off1	= td_var_offset(ramp_time),
1984		.help	= "Ramp up time before measuring performance",
1985		.category = FIO_OPT_C_GENERAL,
1986		.group	= FIO_OPT_G_RUNTIME,
1987	},
1988	{
1989		.name	= "clocksource",
1990		.lname	= "Clock source",
1991		.type	= FIO_OPT_STR,
1992		.cb	= fio_clock_source_cb,
1993		.off1	= td_var_offset(clocksource),
1994		.help	= "What type of timing source to use",
1995		.category = FIO_OPT_C_GENERAL,
1996		.group	= FIO_OPT_G_CLOCK,
1997		.posval	= {
1998#ifdef CONFIG_GETTIMEOFDAY
1999			  { .ival = "gettimeofday",
2000			    .oval = CS_GTOD,
2001			    .help = "Use gettimeofday(2) for timing",
2002			  },
2003#endif
2004#ifdef CONFIG_CLOCK_GETTIME
2005			  { .ival = "clock_gettime",
2006			    .oval = CS_CGETTIME,
2007			    .help = "Use clock_gettime(2) for timing",
2008			  },
2009#endif
2010#ifdef ARCH_HAVE_CPU_CLOCK
2011			  { .ival = "cpu",
2012			    .oval = CS_CPUCLOCK,
2013			    .help = "Use CPU private clock",
2014			  },
2015#endif
2016		},
2017	},
2018	{
2019		.name	= "mem",
2020		.alias	= "iomem",
2021		.lname	= "I/O Memory",
2022		.type	= FIO_OPT_STR,
2023		.cb	= str_mem_cb,
2024		.off1	= td_var_offset(mem_type),
2025		.help	= "Backing type for IO buffers",
2026		.def	= "malloc",
2027		.category = FIO_OPT_C_IO,
2028		.group	= FIO_OPT_G_INVALID,
2029		.posval	= {
2030			  { .ival = "malloc",
2031			    .oval = MEM_MALLOC,
2032			    .help = "Use malloc(3) for IO buffers",
2033			  },
2034			  { .ival = "shm",
2035			    .oval = MEM_SHM,
2036			    .help = "Use shared memory segments for IO buffers",
2037			  },
2038#ifdef FIO_HAVE_HUGETLB
2039			  { .ival = "shmhuge",
2040			    .oval = MEM_SHMHUGE,
2041			    .help = "Like shm, but use huge pages",
2042			  },
2043#endif
2044			  { .ival = "mmap",
2045			    .oval = MEM_MMAP,
2046			    .help = "Use mmap(2) (file or anon) for IO buffers",
2047			  },
2048#ifdef FIO_HAVE_HUGETLB
2049			  { .ival = "mmaphuge",
2050			    .oval = MEM_MMAPHUGE,
2051			    .help = "Like mmap, but use huge pages",
2052			  },
2053#endif
2054		  },
2055	},
2056	{
2057		.name	= "iomem_align",
2058		.alias	= "mem_align",
2059		.lname	= "I/O memory alignment",
2060		.type	= FIO_OPT_INT,
2061		.off1	= td_var_offset(mem_align),
2062		.minval	= 0,
2063		.help	= "IO memory buffer offset alignment",
2064		.def	= "0",
2065		.parent	= "iomem",
2066		.hide	= 1,
2067		.category = FIO_OPT_C_IO,
2068		.group	= FIO_OPT_G_INVALID,
2069	},
2070	{
2071		.name	= "verify",
2072		.lname	= "Verify",
2073		.type	= FIO_OPT_STR,
2074		.off1	= td_var_offset(verify),
2075		.help	= "Verify data written",
2076		.def	= "0",
2077		.category = FIO_OPT_C_IO,
2078		.group	= FIO_OPT_G_VERIFY,
2079		.posval = {
2080			  { .ival = "0",
2081			    .oval = VERIFY_NONE,
2082			    .help = "Don't do IO verification",
2083			  },
2084			  { .ival = "md5",
2085			    .oval = VERIFY_MD5,
2086			    .help = "Use md5 checksums for verification",
2087			  },
2088			  { .ival = "crc64",
2089			    .oval = VERIFY_CRC64,
2090			    .help = "Use crc64 checksums for verification",
2091			  },
2092			  { .ival = "crc32",
2093			    .oval = VERIFY_CRC32,
2094			    .help = "Use crc32 checksums for verification",
2095			  },
2096			  { .ival = "crc32c-intel",
2097			    .oval = VERIFY_CRC32C,
2098			    .help = "Use crc32c checksums for verification (hw assisted, if available)",
2099			  },
2100			  { .ival = "crc32c",
2101			    .oval = VERIFY_CRC32C,
2102			    .help = "Use crc32c checksums for verification (hw assisted, if available)",
2103			  },
2104			  { .ival = "crc16",
2105			    .oval = VERIFY_CRC16,
2106			    .help = "Use crc16 checksums for verification",
2107			  },
2108			  { .ival = "crc7",
2109			    .oval = VERIFY_CRC7,
2110			    .help = "Use crc7 checksums for verification",
2111			  },
2112			  { .ival = "sha1",
2113			    .oval = VERIFY_SHA1,
2114			    .help = "Use sha1 checksums for verification",
2115			  },
2116			  { .ival = "sha256",
2117			    .oval = VERIFY_SHA256,
2118			    .help = "Use sha256 checksums for verification",
2119			  },
2120			  { .ival = "sha512",
2121			    .oval = VERIFY_SHA512,
2122			    .help = "Use sha512 checksums for verification",
2123			  },
2124			  { .ival = "meta",
2125			    .oval = VERIFY_META,
2126			    .help = "Use io information",
2127			  },
2128			  {
2129			    .ival = "null",
2130			    .oval = VERIFY_NULL,
2131			    .help = "Pretend to verify",
2132			  },
2133		},
2134	},
2135	{
2136		.name	= "do_verify",
2137		.lname	= "Perform verify step",
2138		.type	= FIO_OPT_BOOL,
2139		.off1	= td_var_offset(do_verify),
2140		.help	= "Run verification stage after write",
2141		.def	= "1",
2142		.parent = "verify",
2143		.hide	= 1,
2144		.category = FIO_OPT_C_IO,
2145		.group	= FIO_OPT_G_VERIFY,
2146	},
2147	{
2148		.name	= "verifysort",
2149		.lname	= "Verify sort",
2150		.type	= FIO_OPT_BOOL,
2151		.off1	= td_var_offset(verifysort),
2152		.help	= "Sort written verify blocks for read back",
2153		.def	= "1",
2154		.parent = "verify",
2155		.hide	= 1,
2156		.category = FIO_OPT_C_IO,
2157		.group	= FIO_OPT_G_VERIFY,
2158	},
2159	{
2160		.name	= "verifysort_nr",
2161		.type	= FIO_OPT_INT,
2162		.off1	= td_var_offset(verifysort_nr),
2163		.help	= "Pre-load and sort verify blocks for a read workload",
2164		.minval	= 0,
2165		.maxval	= 131072,
2166		.def	= "1024",
2167		.parent = "verify",
2168		.category = FIO_OPT_C_IO,
2169		.group	= FIO_OPT_G_VERIFY,
2170	},
2171	{
2172		.name   = "verify_interval",
2173		.lname	= "Verify interval",
2174		.type   = FIO_OPT_INT,
2175		.off1   = td_var_offset(verify_interval),
2176		.minval	= 2 * sizeof(struct verify_header),
2177		.help   = "Store verify buffer header every N bytes",
2178		.parent	= "verify",
2179		.hide	= 1,
2180		.interval = 2 * sizeof(struct verify_header),
2181		.category = FIO_OPT_C_IO,
2182		.group	= FIO_OPT_G_VERIFY,
2183	},
2184	{
2185		.name	= "verify_offset",
2186		.lname	= "Verify offset",
2187		.type	= FIO_OPT_INT,
2188		.help	= "Offset verify header location by N bytes",
2189		.off1	= td_var_offset(verify_offset),
2190		.minval	= sizeof(struct verify_header),
2191		.parent	= "verify",
2192		.hide	= 1,
2193		.category = FIO_OPT_C_IO,
2194		.group	= FIO_OPT_G_VERIFY,
2195	},
2196	{
2197		.name	= "verify_pattern",
2198		.lname	= "Verify pattern",
2199		.type	= FIO_OPT_STR,
2200		.cb	= str_verify_pattern_cb,
2201		.help	= "Fill pattern for IO buffers",
2202		.parent	= "verify",
2203		.hide	= 1,
2204		.category = FIO_OPT_C_IO,
2205		.group	= FIO_OPT_G_VERIFY,
2206	},
2207	{
2208		.name	= "verify_fatal",
2209		.lname	= "Verify fatal",
2210		.type	= FIO_OPT_BOOL,
2211		.off1	= td_var_offset(verify_fatal),
2212		.def	= "0",
2213		.help	= "Exit on a single verify failure, don't continue",
2214		.parent = "verify",
2215		.hide	= 1,
2216		.category = FIO_OPT_C_IO,
2217		.group	= FIO_OPT_G_VERIFY,
2218	},
2219	{
2220		.name	= "verify_dump",
2221		.lname	= "Verify dump",
2222		.type	= FIO_OPT_BOOL,
2223		.off1	= td_var_offset(verify_dump),
2224		.def	= "0",
2225		.help	= "Dump contents of good and bad blocks on failure",
2226		.parent = "verify",
2227		.hide	= 1,
2228		.category = FIO_OPT_C_IO,
2229		.group	= FIO_OPT_G_VERIFY,
2230	},
2231	{
2232		.name	= "verify_async",
2233		.lname	= "Verify asynchronously",
2234		.type	= FIO_OPT_INT,
2235		.off1	= td_var_offset(verify_async),
2236		.def	= "0",
2237		.help	= "Number of async verifier threads to use",
2238		.parent	= "verify",
2239		.hide	= 1,
2240		.category = FIO_OPT_C_IO,
2241		.group	= FIO_OPT_G_VERIFY,
2242	},
2243	{
2244		.name	= "verify_backlog",
2245		.lname	= "Verify backlog",
2246		.type	= FIO_OPT_STR_VAL,
2247		.off1	= td_var_offset(verify_backlog),
2248		.help	= "Verify after this number of blocks are written",
2249		.parent	= "verify",
2250		.hide	= 1,
2251		.category = FIO_OPT_C_IO,
2252		.group	= FIO_OPT_G_VERIFY,
2253	},
2254	{
2255		.name	= "verify_backlog_batch",
2256		.lname	= "Verify backlog batch",
2257		.type	= FIO_OPT_INT,
2258		.off1	= td_var_offset(verify_batch),
2259		.help	= "Verify this number of IO blocks",
2260		.parent	= "verify",
2261		.hide	= 1,
2262		.category = FIO_OPT_C_IO,
2263		.group	= FIO_OPT_G_VERIFY,
2264	},
2265#ifdef FIO_HAVE_CPU_AFFINITY
2266	{
2267		.name	= "verify_async_cpus",
2268		.lname	= "Async verify CPUs",
2269		.type	= FIO_OPT_STR,
2270		.cb	= str_verify_cpus_allowed_cb,
2271		.help	= "Set CPUs allowed for async verify threads",
2272		.parent	= "verify_async",
2273		.hide	= 1,
2274		.category = FIO_OPT_C_IO,
2275		.group	= FIO_OPT_G_VERIFY,
2276	},
2277#endif
2278	{
2279		.name	= "experimental_verify",
2280		.off1	= td_var_offset(experimental_verify),
2281		.type	= FIO_OPT_BOOL,
2282		.help	= "Enable experimental verification",
2283		.category = FIO_OPT_C_IO,
2284		.group	= FIO_OPT_G_VERIFY,
2285	},
2286#ifdef FIO_HAVE_TRIM
2287	{
2288		.name	= "trim_percentage",
2289		.lname	= "Trim percentage",
2290		.type	= FIO_OPT_INT,
2291		.off1	= td_var_offset(trim_percentage),
2292		.minval = 0,
2293		.maxval = 100,
2294		.help	= "Number of verify blocks to discard/trim",
2295		.parent	= "verify",
2296		.def	= "0",
2297		.interval = 1,
2298		.hide	= 1,
2299		.category = FIO_OPT_C_IO,
2300		.group	= FIO_OPT_G_TRIM,
2301	},
2302	{
2303		.name	= "trim_verify_zero",
2304		.lname	= "Verify trim zero",
2305		.type	= FIO_OPT_BOOL,
2306		.help	= "Verify that trim/discarded blocks are returned as zeroes",
2307		.off1	= td_var_offset(trim_zero),
2308		.parent	= "trim_percentage",
2309		.hide	= 1,
2310		.def	= "1",
2311		.category = FIO_OPT_C_IO,
2312		.group	= FIO_OPT_G_TRIM,
2313	},
2314	{
2315		.name	= "trim_backlog",
2316		.lname	= "Trim backlog",
2317		.type	= FIO_OPT_STR_VAL,
2318		.off1	= td_var_offset(trim_backlog),
2319		.help	= "Trim after this number of blocks are written",
2320		.parent	= "trim_percentage",
2321		.hide	= 1,
2322		.interval = 1,
2323		.category = FIO_OPT_C_IO,
2324		.group	= FIO_OPT_G_TRIM,
2325	},
2326	{
2327		.name	= "trim_backlog_batch",
2328		.lname	= "Trim backlog batch",
2329		.type	= FIO_OPT_INT,
2330		.off1	= td_var_offset(trim_batch),
2331		.help	= "Trim this number of IO blocks",
2332		.parent	= "trim_percentage",
2333		.hide	= 1,
2334		.interval = 1,
2335		.category = FIO_OPT_C_IO,
2336		.group	= FIO_OPT_G_TRIM,
2337	},
2338#endif
2339	{
2340		.name	= "write_iolog",
2341		.lname	= "Write I/O log",
2342		.type	= FIO_OPT_STR_STORE,
2343		.off1	= td_var_offset(write_iolog_file),
2344		.help	= "Store IO pattern to file",
2345		.category = FIO_OPT_C_IO,
2346		.group	= FIO_OPT_G_IOLOG,
2347	},
2348	{
2349		.name	= "read_iolog",
2350		.lname	= "Read I/O log",
2351		.type	= FIO_OPT_STR_STORE,
2352		.off1	= td_var_offset(read_iolog_file),
2353		.help	= "Playback IO pattern from file",
2354		.category = FIO_OPT_C_IO,
2355		.group	= FIO_OPT_G_IOLOG,
2356	},
2357	{
2358		.name	= "replay_no_stall",
2359		.lname	= "Don't stall on replay",
2360		.type	= FIO_OPT_BOOL,
2361		.off1	= td_var_offset(no_stall),
2362		.def	= "0",
2363		.parent	= "read_iolog",
2364		.hide	= 1,
2365		.help	= "Playback IO pattern file as fast as possible without stalls",
2366		.category = FIO_OPT_C_IO,
2367		.group	= FIO_OPT_G_IOLOG,
2368	},
2369	{
2370		.name	= "replay_redirect",
2371		.lname	= "Redirect device for replay",
2372		.type	= FIO_OPT_STR_STORE,
2373		.off1	= td_var_offset(replay_redirect),
2374		.parent	= "read_iolog",
2375		.hide	= 1,
2376		.help	= "Replay all I/O onto this device, regardless of trace device",
2377		.category = FIO_OPT_C_IO,
2378		.group	= FIO_OPT_G_IOLOG,
2379	},
2380	{
2381		.name	= "exec_prerun",
2382		.lname	= "Pre-execute runnable",
2383		.type	= FIO_OPT_STR_STORE,
2384		.off1	= td_var_offset(exec_prerun),
2385		.help	= "Execute this file prior to running job",
2386		.category = FIO_OPT_C_GENERAL,
2387		.group	= FIO_OPT_G_INVALID,
2388	},
2389	{
2390		.name	= "exec_postrun",
2391		.lname	= "Post-execute runnable",
2392		.type	= FIO_OPT_STR_STORE,
2393		.off1	= td_var_offset(exec_postrun),
2394		.help	= "Execute this file after running job",
2395		.category = FIO_OPT_C_GENERAL,
2396		.group	= FIO_OPT_G_INVALID,
2397	},
2398#ifdef FIO_HAVE_IOSCHED_SWITCH
2399	{
2400		.name	= "ioscheduler",
2401		.lname	= "I/O scheduler",
2402		.type	= FIO_OPT_STR_STORE,
2403		.off1	= td_var_offset(ioscheduler),
2404		.help	= "Use this IO scheduler on the backing device",
2405		.category = FIO_OPT_C_FILE,
2406		.group	= FIO_OPT_G_INVALID,
2407	},
2408#endif
2409	{
2410		.name	= "zonesize",
2411		.lname	= "Zone size",
2412		.type	= FIO_OPT_STR_VAL,
2413		.off1	= td_var_offset(zone_size),
2414		.help	= "Amount of data to read per zone",
2415		.def	= "0",
2416		.interval = 1024 * 1024,
2417		.category = FIO_OPT_C_IO,
2418		.group	= FIO_OPT_G_ZONE,
2419	},
2420	{
2421		.name	= "zonerange",
2422		.lname	= "Zone range",
2423		.type	= FIO_OPT_STR_VAL,
2424		.off1	= td_var_offset(zone_range),
2425		.help	= "Give size of an IO zone",
2426		.def	= "0",
2427		.interval = 1024 * 1024,
2428		.category = FIO_OPT_C_IO,
2429		.group	= FIO_OPT_G_ZONE,
2430	},
2431	{
2432		.name	= "zoneskip",
2433		.lname	= "Zone skip",
2434		.type	= FIO_OPT_STR_VAL,
2435		.off1	= td_var_offset(zone_skip),
2436		.help	= "Space between IO zones",
2437		.def	= "0",
2438		.interval = 1024 * 1024,
2439		.category = FIO_OPT_C_IO,
2440		.group	= FIO_OPT_G_ZONE,
2441	},
2442	{
2443		.name	= "lockmem",
2444		.lname	= "Lock memory",
2445		.type	= FIO_OPT_STR_VAL,
2446		.off1	= td_var_offset(lockmem),
2447		.help	= "Lock down this amount of memory (per worker)",
2448		.def	= "0",
2449		.interval = 1024 * 1024,
2450		.category = FIO_OPT_C_GENERAL,
2451		.group	= FIO_OPT_G_INVALID,
2452	},
2453	{
2454		.name	= "rwmixread",
2455		.lname	= "Read/write mix read",
2456		.type	= FIO_OPT_INT,
2457		.cb	= str_rwmix_read_cb,
2458		.maxval	= 100,
2459		.help	= "Percentage of mixed workload that is reads",
2460		.def	= "50",
2461		.interval = 5,
2462		.inverse = "rwmixwrite",
2463		.category = FIO_OPT_C_IO,
2464		.group	= FIO_OPT_G_RWMIX,
2465	},
2466	{
2467		.name	= "rwmixwrite",
2468		.lname	= "Read/write mix write",
2469		.type	= FIO_OPT_INT,
2470		.cb	= str_rwmix_write_cb,
2471		.maxval	= 100,
2472		.help	= "Percentage of mixed workload that is writes",
2473		.def	= "50",
2474		.interval = 5,
2475		.inverse = "rwmixread",
2476		.category = FIO_OPT_C_IO,
2477		.group	= FIO_OPT_G_RWMIX,
2478	},
2479	{
2480		.name	= "rwmixcycle",
2481		.lname	= "Read/write mix cycle",
2482		.type	= FIO_OPT_DEPRECATED,
2483		.category = FIO_OPT_C_IO,
2484		.group	= FIO_OPT_G_RWMIX,
2485	},
2486	{
2487		.name	= "nice",
2488		.lname	= "Nice",
2489		.type	= FIO_OPT_INT,
2490		.off1	= td_var_offset(nice),
2491		.help	= "Set job CPU nice value",
2492		.minval	= -19,
2493		.maxval	= 20,
2494		.def	= "0",
2495		.interval = 1,
2496		.category = FIO_OPT_C_GENERAL,
2497		.group	= FIO_OPT_G_CRED,
2498	},
2499#ifdef FIO_HAVE_IOPRIO
2500	{
2501		.name	= "prio",
2502		.lname	= "I/O nice priority",
2503		.type	= FIO_OPT_INT,
2504		.off1	= td_var_offset(ioprio),
2505		.help	= "Set job IO priority value",
2506		.minval	= 0,
2507		.maxval	= 7,
2508		.interval = 1,
2509		.category = FIO_OPT_C_GENERAL,
2510		.group	= FIO_OPT_G_CRED,
2511	},
2512	{
2513		.name	= "prioclass",
2514		.lname	= "I/O nice priority class",
2515		.type	= FIO_OPT_INT,
2516		.off1	= td_var_offset(ioprio_class),
2517		.help	= "Set job IO priority class",
2518		.minval	= 0,
2519		.maxval	= 3,
2520		.interval = 1,
2521		.category = FIO_OPT_C_GENERAL,
2522		.group	= FIO_OPT_G_CRED,
2523	},
2524#endif
2525	{
2526		.name	= "thinktime",
2527		.lname	= "Thinktime",
2528		.type	= FIO_OPT_INT,
2529		.off1	= td_var_offset(thinktime),
2530		.help	= "Idle time between IO buffers (usec)",
2531		.def	= "0",
2532		.category = FIO_OPT_C_IO,
2533		.group	= FIO_OPT_G_THINKTIME,
2534	},
2535	{
2536		.name	= "thinktime_spin",
2537		.lname	= "Thinktime spin",
2538		.type	= FIO_OPT_INT,
2539		.off1	= td_var_offset(thinktime_spin),
2540		.help	= "Start think time by spinning this amount (usec)",
2541		.def	= "0",
2542		.parent	= "thinktime",
2543		.hide	= 1,
2544		.category = FIO_OPT_C_IO,
2545		.group	= FIO_OPT_G_THINKTIME,
2546	},
2547	{
2548		.name	= "thinktime_blocks",
2549		.lname	= "Thinktime blocks",
2550		.type	= FIO_OPT_INT,
2551		.off1	= td_var_offset(thinktime_blocks),
2552		.help	= "IO buffer period between 'thinktime'",
2553		.def	= "1",
2554		.parent	= "thinktime",
2555		.hide	= 1,
2556		.category = FIO_OPT_C_IO,
2557		.group	= FIO_OPT_G_THINKTIME,
2558	},
2559	{
2560		.name	= "rate",
2561		.lname	= "I/O rate",
2562		.type	= FIO_OPT_INT,
2563		.off1	= td_var_offset(rate[DDIR_READ]),
2564		.off2	= td_var_offset(rate[DDIR_WRITE]),
2565		.off3	= td_var_offset(rate[DDIR_TRIM]),
2566		.help	= "Set bandwidth rate",
2567		.category = FIO_OPT_C_IO,
2568		.group	= FIO_OPT_G_RATE,
2569	},
2570	{
2571		.name	= "ratemin",
2572		.lname	= "I/O min rate",
2573		.type	= FIO_OPT_INT,
2574		.off1	= td_var_offset(ratemin[DDIR_READ]),
2575		.off2	= td_var_offset(ratemin[DDIR_WRITE]),
2576		.off3	= td_var_offset(ratemin[DDIR_TRIM]),
2577		.help	= "Job must meet this rate or it will be shutdown",
2578		.parent	= "rate",
2579		.hide	= 1,
2580		.category = FIO_OPT_C_IO,
2581		.group	= FIO_OPT_G_RATE,
2582	},
2583	{
2584		.name	= "rate_iops",
2585		.lname	= "I/O rate IOPS",
2586		.type	= FIO_OPT_INT,
2587		.off1	= td_var_offset(rate_iops[DDIR_READ]),
2588		.off2	= td_var_offset(rate_iops[DDIR_WRITE]),
2589		.off3	= td_var_offset(rate_iops[DDIR_TRIM]),
2590		.help	= "Limit IO used to this number of IO operations/sec",
2591		.hide	= 1,
2592		.category = FIO_OPT_C_IO,
2593		.group	= FIO_OPT_G_RATE,
2594	},
2595	{
2596		.name	= "rate_iops_min",
2597		.lname	= "I/O min rate IOPS",
2598		.type	= FIO_OPT_INT,
2599		.off1	= td_var_offset(rate_iops_min[DDIR_READ]),
2600		.off2	= td_var_offset(rate_iops_min[DDIR_WRITE]),
2601		.off3	= td_var_offset(rate_iops_min[DDIR_TRIM]),
2602		.help	= "Job must meet this rate or it will be shut down",
2603		.parent	= "rate_iops",
2604		.hide	= 1,
2605		.category = FIO_OPT_C_IO,
2606		.group	= FIO_OPT_G_RATE,
2607	},
2608	{
2609		.name	= "ratecycle",
2610		.lname	= "I/O rate cycle",
2611		.type	= FIO_OPT_INT,
2612		.off1	= td_var_offset(ratecycle),
2613		.help	= "Window average for rate limits (msec)",
2614		.def	= "1000",
2615		.parent = "rate",
2616		.hide	= 1,
2617		.category = FIO_OPT_C_IO,
2618		.group	= FIO_OPT_G_RATE,
2619	},
2620	{
2621		.name	= "max_latency",
2622		.type	= FIO_OPT_INT,
2623		.off1	= td_var_offset(max_latency),
2624		.help	= "Maximum tolerated IO latency (usec)",
2625		.category = FIO_OPT_C_IO,
2626		.group = FIO_OPT_G_RATE,
2627	},
2628	{
2629		.name	= "invalidate",
2630		.lname	= "Cache invalidate",
2631		.type	= FIO_OPT_BOOL,
2632		.off1	= td_var_offset(invalidate_cache),
2633		.help	= "Invalidate buffer/page cache prior to running job",
2634		.def	= "1",
2635		.category = FIO_OPT_C_IO,
2636		.group	= FIO_OPT_G_IO_TYPE,
2637	},
2638	{
2639		.name	= "sync",
2640		.lname	= "Synchronous I/O",
2641		.type	= FIO_OPT_BOOL,
2642		.off1	= td_var_offset(sync_io),
2643		.help	= "Use O_SYNC for buffered writes",
2644		.def	= "0",
2645		.parent = "buffered",
2646		.hide	= 1,
2647		.category = FIO_OPT_C_IO,
2648		.group	= FIO_OPT_G_IO_TYPE,
2649	},
2650	{
2651		.name	= "create_serialize",
2652		.lname	= "Create serialize",
2653		.type	= FIO_OPT_BOOL,
2654		.off1	= td_var_offset(create_serialize),
2655		.help	= "Serialize creating of job files",
2656		.def	= "1",
2657		.category = FIO_OPT_C_FILE,
2658		.group	= FIO_OPT_G_INVALID,
2659	},
2660	{
2661		.name	= "create_fsync",
2662		.lname	= "Create fsync",
2663		.type	= FIO_OPT_BOOL,
2664		.off1	= td_var_offset(create_fsync),
2665		.help	= "fsync file after creation",
2666		.def	= "1",
2667		.category = FIO_OPT_C_FILE,
2668		.group	= FIO_OPT_G_INVALID,
2669	},
2670	{
2671		.name	= "create_on_open",
2672		.lname	= "Create on open",
2673		.type	= FIO_OPT_BOOL,
2674		.off1	= td_var_offset(create_on_open),
2675		.help	= "Create files when they are opened for IO",
2676		.def	= "0",
2677		.category = FIO_OPT_C_FILE,
2678		.group	= FIO_OPT_G_INVALID,
2679	},
2680	{
2681		.name	= "create_only",
2682		.type	= FIO_OPT_BOOL,
2683		.off1	= td_var_offset(create_only),
2684		.help	= "Only perform file creation phase",
2685		.category = FIO_OPT_C_FILE,
2686		.def	= "0",
2687	},
2688	{
2689		.name	= "pre_read",
2690		.lname	= "Pre-read files",
2691		.type	= FIO_OPT_BOOL,
2692		.off1	= td_var_offset(pre_read),
2693		.help	= "Pre-read files before starting official testing",
2694		.def	= "0",
2695		.category = FIO_OPT_C_FILE,
2696		.group	= FIO_OPT_G_INVALID,
2697	},
2698#ifdef FIO_HAVE_CPU_AFFINITY
2699	{
2700		.name	= "cpumask",
2701		.lname	= "CPU mask",
2702		.type	= FIO_OPT_INT,
2703		.cb	= str_cpumask_cb,
2704		.help	= "CPU affinity mask",
2705		.category = FIO_OPT_C_GENERAL,
2706		.group	= FIO_OPT_G_CRED,
2707	},
2708	{
2709		.name	= "cpus_allowed",
2710		.lname	= "CPUs allowed",
2711		.type	= FIO_OPT_STR,
2712		.cb	= str_cpus_allowed_cb,
2713		.help	= "Set CPUs allowed",
2714		.category = FIO_OPT_C_GENERAL,
2715		.group	= FIO_OPT_G_CRED,
2716	},
2717#endif
2718#ifdef CONFIG_LIBNUMA
2719	{
2720		.name	= "numa_cpu_nodes",
2721		.type	= FIO_OPT_STR,
2722		.cb	= str_numa_cpunodes_cb,
2723		.help	= "NUMA CPU nodes bind",
2724		.category = FIO_OPT_C_GENERAL,
2725		.group	= FIO_OPT_G_INVALID,
2726	},
2727	{
2728		.name	= "numa_mem_policy",
2729		.type	= FIO_OPT_STR,
2730		.cb	= str_numa_mpol_cb,
2731		.help	= "NUMA memory policy setup",
2732		.category = FIO_OPT_C_GENERAL,
2733		.group	= FIO_OPT_G_INVALID,
2734	},
2735#endif
2736	{
2737		.name	= "end_fsync",
2738		.lname	= "End fsync",
2739		.type	= FIO_OPT_BOOL,
2740		.off1	= td_var_offset(end_fsync),
2741		.help	= "Include fsync at the end of job",
2742		.def	= "0",
2743		.category = FIO_OPT_C_FILE,
2744		.group	= FIO_OPT_G_INVALID,
2745	},
2746	{
2747		.name	= "fsync_on_close",
2748		.lname	= "Fsync on close",
2749		.type	= FIO_OPT_BOOL,
2750		.off1	= td_var_offset(fsync_on_close),
2751		.help	= "fsync files on close",
2752		.def	= "0",
2753		.category = FIO_OPT_C_FILE,
2754		.group	= FIO_OPT_G_INVALID,
2755	},
2756	{
2757		.name	= "unlink",
2758		.lname	= "Unlink file",
2759		.type	= FIO_OPT_BOOL,
2760		.off1	= td_var_offset(unlink),
2761		.help	= "Unlink created files after job has completed",
2762		.def	= "0",
2763		.category = FIO_OPT_C_FILE,
2764		.group	= FIO_OPT_G_INVALID,
2765	},
2766	{
2767		.name	= "exitall",
2768		.lname	= "Exit-all on terminate",
2769		.type	= FIO_OPT_STR_SET,
2770		.cb	= str_exitall_cb,
2771		.help	= "Terminate all jobs when one exits",
2772		.category = FIO_OPT_C_GENERAL,
2773		.group	= FIO_OPT_G_PROCESS,
2774	},
2775	{
2776		.name	= "stonewall",
2777		.lname	= "Wait for previous",
2778		.alias	= "wait_for_previous",
2779		.type	= FIO_OPT_STR_SET,
2780		.off1	= td_var_offset(stonewall),
2781		.help	= "Insert a hard barrier between this job and previous",
2782		.category = FIO_OPT_C_GENERAL,
2783		.group	= FIO_OPT_G_PROCESS,
2784	},
2785	{
2786		.name	= "new_group",
2787		.lname	= "New group",
2788		.type	= FIO_OPT_STR_SET,
2789		.off1	= td_var_offset(new_group),
2790		.help	= "Mark the start of a new group (for reporting)",
2791		.category = FIO_OPT_C_GENERAL,
2792		.group	= FIO_OPT_G_PROCESS,
2793	},
2794	{
2795		.name	= "thread",
2796		.lname	= "Thread",
2797		.type	= FIO_OPT_STR_SET,
2798		.off1	= td_var_offset(use_thread),
2799		.help	= "Use threads instead of processes",
2800		.category = FIO_OPT_C_GENERAL,
2801		.group	= FIO_OPT_G_PROCESS,
2802	},
2803	{
2804		.name	= "write_bw_log",
2805		.lname	= "Write bandwidth log",
2806		.type	= FIO_OPT_STR_STORE,
2807		.off1	= td_var_offset(bw_log_file),
2808		.help	= "Write log of bandwidth during run",
2809		.category = FIO_OPT_C_LOG,
2810		.group	= FIO_OPT_G_INVALID,
2811	},
2812	{
2813		.name	= "write_lat_log",
2814		.lname	= "Write latency log",
2815		.type	= FIO_OPT_STR_STORE,
2816		.off1	= td_var_offset(lat_log_file),
2817		.help	= "Write log of latency during run",
2818		.category = FIO_OPT_C_LOG,
2819		.group	= FIO_OPT_G_INVALID,
2820	},
2821	{
2822		.name	= "write_iops_log",
2823		.lname	= "Write IOPS log",
2824		.type	= FIO_OPT_STR_STORE,
2825		.off1	= td_var_offset(iops_log_file),
2826		.help	= "Write log of IOPS during run",
2827		.category = FIO_OPT_C_LOG,
2828		.group	= FIO_OPT_G_INVALID,
2829	},
2830	{
2831		.name	= "log_avg_msec",
2832		.lname	= "Log averaging (msec)",
2833		.type	= FIO_OPT_INT,
2834		.off1	= td_var_offset(log_avg_msec),
2835		.help	= "Average bw/iops/lat logs over this period of time",
2836		.def	= "0",
2837		.category = FIO_OPT_C_LOG,
2838		.group	= FIO_OPT_G_INVALID,
2839	},
2840	{
2841		.name	= "bwavgtime",
2842		.lname	= "Bandwidth average time",
2843		.type	= FIO_OPT_INT,
2844		.off1	= td_var_offset(bw_avg_time),
2845		.help	= "Time window over which to calculate bandwidth"
2846			  " (msec)",
2847		.def	= "500",
2848		.parent	= "write_bw_log",
2849		.hide	= 1,
2850		.interval = 100,
2851		.category = FIO_OPT_C_LOG,
2852		.group	= FIO_OPT_G_INVALID,
2853	},
2854	{
2855		.name	= "iopsavgtime",
2856		.lname	= "IOPS average time",
2857		.type	= FIO_OPT_INT,
2858		.off1	= td_var_offset(iops_avg_time),
2859		.help	= "Time window over which to calculate IOPS (msec)",
2860		.def	= "500",
2861		.parent	= "write_iops_log",
2862		.hide	= 1,
2863		.interval = 100,
2864		.category = FIO_OPT_C_LOG,
2865		.group	= FIO_OPT_G_INVALID,
2866	},
2867	{
2868		.name	= "group_reporting",
2869		.lname	= "Group reporting",
2870		.type	= FIO_OPT_STR_SET,
2871		.off1	= td_var_offset(group_reporting),
2872		.help	= "Do reporting on a per-group basis",
2873		.category = FIO_OPT_C_STAT,
2874		.group	= FIO_OPT_G_INVALID,
2875	},
2876	{
2877		.name	= "zero_buffers",
2878		.lname	= "Zero I/O buffers",
2879		.type	= FIO_OPT_STR_SET,
2880		.off1	= td_var_offset(zero_buffers),
2881		.help	= "Init IO buffers to all zeroes",
2882		.category = FIO_OPT_C_IO,
2883		.group	= FIO_OPT_G_IO_BUF,
2884	},
2885	{
2886		.name	= "refill_buffers",
2887		.lname	= "Refill I/O buffers",
2888		.type	= FIO_OPT_STR_SET,
2889		.off1	= td_var_offset(refill_buffers),
2890		.help	= "Refill IO buffers on every IO submit",
2891		.category = FIO_OPT_C_IO,
2892		.group	= FIO_OPT_G_IO_BUF,
2893	},
2894	{
2895		.name	= "scramble_buffers",
2896		.lname	= "Scramble I/O buffers",
2897		.type	= FIO_OPT_BOOL,
2898		.off1	= td_var_offset(scramble_buffers),
2899		.help	= "Slightly scramble buffers on every IO submit",
2900		.def	= "1",
2901		.category = FIO_OPT_C_IO,
2902		.group	= FIO_OPT_G_IO_BUF,
2903	},
2904	{
2905		.name	= "buffer_compress_percentage",
2906		.lname	= "Buffer compression percentage",
2907		.type	= FIO_OPT_INT,
2908		.off1	= td_var_offset(compress_percentage),
2909		.maxval	= 100,
2910		.minval	= 1,
2911		.help	= "How compressible the buffer is (approximately)",
2912		.interval = 5,
2913		.category = FIO_OPT_C_IO,
2914		.group	= FIO_OPT_G_IO_BUF,
2915	},
2916	{
2917		.name	= "buffer_compress_chunk",
2918		.lname	= "Buffer compression chunk size",
2919		.type	= FIO_OPT_INT,
2920		.off1	= td_var_offset(compress_chunk),
2921		.parent	= "buffer_compress_percentage",
2922		.hide	= 1,
2923		.help	= "Size of compressible region in buffer",
2924		.interval = 256,
2925		.category = FIO_OPT_C_IO,
2926		.group	= FIO_OPT_G_IO_BUF,
2927	},
2928	{
2929		.name	= "clat_percentiles",
2930		.lname	= "Completion latency percentiles",
2931		.type	= FIO_OPT_BOOL,
2932		.off1	= td_var_offset(clat_percentiles),
2933		.help	= "Enable the reporting of completion latency percentiles",
2934		.def	= "1",
2935		.category = FIO_OPT_C_STAT,
2936		.group	= FIO_OPT_G_INVALID,
2937	},
2938	{
2939		.name	= "percentile_list",
2940		.lname	= "Completion latency percentile list",
2941		.type	= FIO_OPT_FLOAT_LIST,
2942		.off1	= td_var_offset(percentile_list),
2943		.off2	= td_var_offset(percentile_precision),
2944		.help	= "Specify a custom list of percentiles to report",
2945		.def    = "1:5:10:20:30:40:50:60:70:80:90:95:99:99.5:99.9:99.95:99.99",
2946		.maxlen	= FIO_IO_U_LIST_MAX_LEN,
2947		.minfp	= 0.0,
2948		.maxfp	= 100.0,
2949		.category = FIO_OPT_C_STAT,
2950		.group	= FIO_OPT_G_INVALID,
2951	},
2952
2953#ifdef FIO_HAVE_DISK_UTIL
2954	{
2955		.name	= "disk_util",
2956		.lname	= "Disk utilization",
2957		.type	= FIO_OPT_BOOL,
2958		.off1	= td_var_offset(do_disk_util),
2959		.help	= "Log disk utilization statistics",
2960		.def	= "1",
2961		.category = FIO_OPT_C_STAT,
2962		.group	= FIO_OPT_G_INVALID,
2963	},
2964#endif
2965	{
2966		.name	= "gtod_reduce",
2967		.lname	= "Reduce gettimeofday() calls",
2968		.type	= FIO_OPT_BOOL,
2969		.help	= "Greatly reduce number of gettimeofday() calls",
2970		.cb	= str_gtod_reduce_cb,
2971		.def	= "0",
2972		.hide_on_set = 1,
2973		.category = FIO_OPT_C_STAT,
2974		.group	= FIO_OPT_G_INVALID,
2975	},
2976	{
2977		.name	= "disable_lat",
2978		.lname	= "Disable all latency stats",
2979		.type	= FIO_OPT_BOOL,
2980		.off1	= td_var_offset(disable_lat),
2981		.help	= "Disable latency numbers",
2982		.parent	= "gtod_reduce",
2983		.hide	= 1,
2984		.def	= "0",
2985		.category = FIO_OPT_C_STAT,
2986		.group	= FIO_OPT_G_INVALID,
2987	},
2988	{
2989		.name	= "disable_clat",
2990		.lname	= "Disable completion latency stats",
2991		.type	= FIO_OPT_BOOL,
2992		.off1	= td_var_offset(disable_clat),
2993		.help	= "Disable completion latency numbers",
2994		.parent	= "gtod_reduce",
2995		.hide	= 1,
2996		.def	= "0",
2997		.category = FIO_OPT_C_STAT,
2998		.group	= FIO_OPT_G_INVALID,
2999	},
3000	{
3001		.name	= "disable_slat",
3002		.lname	= "Disable submission latency stats",
3003		.type	= FIO_OPT_BOOL,
3004		.off1	= td_var_offset(disable_slat),
3005		.help	= "Disable submission latency numbers",
3006		.parent	= "gtod_reduce",
3007		.hide	= 1,
3008		.def	= "0",
3009		.category = FIO_OPT_C_STAT,
3010		.group	= FIO_OPT_G_INVALID,
3011	},
3012	{
3013		.name	= "disable_bw_measurement",
3014		.lname	= "Disable bandwidth stats",
3015		.type	= FIO_OPT_BOOL,
3016		.off1	= td_var_offset(disable_bw),
3017		.help	= "Disable bandwidth logging",
3018		.parent	= "gtod_reduce",
3019		.hide	= 1,
3020		.def	= "0",
3021		.category = FIO_OPT_C_STAT,
3022		.group	= FIO_OPT_G_INVALID,
3023	},
3024	{
3025		.name	= "gtod_cpu",
3026		.lname	= "Dedicated gettimeofday() CPU",
3027		.type	= FIO_OPT_INT,
3028		.cb	= str_gtod_cpu_cb,
3029		.help	= "Set up dedicated gettimeofday() thread on this CPU",
3030		.verify	= gtod_cpu_verify,
3031		.category = FIO_OPT_C_GENERAL,
3032		.group	= FIO_OPT_G_CLOCK,
3033	},
3034	{
3035		.name	= "unified_rw_reporting",
3036		.type	= FIO_OPT_BOOL,
3037		.off1	= td_var_offset(unified_rw_rep),
3038		.help	= "Unify reporting across data direction",
3039		.def	= "0",
3040		.category = FIO_OPT_C_GENERAL,
3041		.group	= FIO_OPT_G_INVALID,
3042	},
3043	{
3044		.name	= "continue_on_error",
3045		.lname	= "Continue on error",
3046		.type	= FIO_OPT_STR,
3047		.off1	= td_var_offset(continue_on_error),
3048		.help	= "Continue on non-fatal errors during IO",
3049		.def	= "none",
3050		.category = FIO_OPT_C_GENERAL,
3051		.group	= FIO_OPT_G_ERR,
3052		.posval = {
3053			  { .ival = "none",
3054			    .oval = ERROR_TYPE_NONE,
3055			    .help = "Exit when an error is encountered",
3056			  },
3057			  { .ival = "read",
3058			    .oval = ERROR_TYPE_READ,
3059			    .help = "Continue on read errors only",
3060			  },
3061			  { .ival = "write",
3062			    .oval = ERROR_TYPE_WRITE,
3063			    .help = "Continue on write errors only",
3064			  },
3065			  { .ival = "io",
3066			    .oval = ERROR_TYPE_READ | ERROR_TYPE_WRITE,
3067			    .help = "Continue on any IO errors",
3068			  },
3069			  { .ival = "verify",
3070			    .oval = ERROR_TYPE_VERIFY,
3071			    .help = "Continue on verify errors only",
3072			  },
3073			  { .ival = "all",
3074			    .oval = ERROR_TYPE_ANY,
3075			    .help = "Continue on all io and verify errors",
3076			  },
3077			  { .ival = "0",
3078			    .oval = ERROR_TYPE_NONE,
3079			    .help = "Alias for 'none'",
3080			  },
3081			  { .ival = "1",
3082			    .oval = ERROR_TYPE_ANY,
3083			    .help = "Alias for 'all'",
3084			  },
3085		},
3086	},
3087	{
3088		.name	= "ignore_error",
3089		.type	= FIO_OPT_STR,
3090		.cb	= str_ignore_error_cb,
3091		.help	= "Set a specific list of errors to ignore",
3092		.parent	= "rw",
3093		.category = FIO_OPT_C_GENERAL,
3094		.group	= FIO_OPT_G_ERR,
3095	},
3096	{
3097		.name	= "error_dump",
3098		.type	= FIO_OPT_BOOL,
3099		.off1	= td_var_offset(error_dump),
3100		.def	= "0",
3101		.help	= "Dump info on each error",
3102		.category = FIO_OPT_C_GENERAL,
3103		.group	= FIO_OPT_G_ERR,
3104	},
3105	{
3106		.name	= "profile",
3107		.lname	= "Profile",
3108		.type	= FIO_OPT_STR_STORE,
3109		.off1	= td_var_offset(profile),
3110		.help	= "Select a specific builtin performance test",
3111		.category = FIO_OPT_C_PROFILE,
3112		.group	= FIO_OPT_G_INVALID,
3113	},
3114	{
3115		.name	= "cgroup",
3116		.lname	= "Cgroup",
3117		.type	= FIO_OPT_STR_STORE,
3118		.off1	= td_var_offset(cgroup),
3119		.help	= "Add job to cgroup of this name",
3120		.category = FIO_OPT_C_GENERAL,
3121		.group	= FIO_OPT_G_CGROUP,
3122	},
3123	{
3124		.name	= "cgroup_nodelete",
3125		.lname	= "Cgroup no-delete",
3126		.type	= FIO_OPT_BOOL,
3127		.off1	= td_var_offset(cgroup_nodelete),
3128		.help	= "Do not delete cgroups after job completion",
3129		.def	= "0",
3130		.parent	= "cgroup",
3131		.category = FIO_OPT_C_GENERAL,
3132		.group	= FIO_OPT_G_CGROUP,
3133	},
3134	{
3135		.name	= "cgroup_weight",
3136		.lname	= "Cgroup weight",
3137		.type	= FIO_OPT_INT,
3138		.off1	= td_var_offset(cgroup_weight),
3139		.help	= "Use given weight for cgroup",
3140		.minval = 100,
3141		.maxval	= 1000,
3142		.parent	= "cgroup",
3143		.category = FIO_OPT_C_GENERAL,
3144		.group	= FIO_OPT_G_CGROUP,
3145	},
3146	{
3147		.name	= "uid",
3148		.lname	= "User ID",
3149		.type	= FIO_OPT_INT,
3150		.off1	= td_var_offset(uid),
3151		.help	= "Run job with this user ID",
3152		.category = FIO_OPT_C_GENERAL,
3153		.group	= FIO_OPT_G_CRED,
3154	},
3155	{
3156		.name	= "gid",
3157		.lname	= "Group ID",
3158		.type	= FIO_OPT_INT,
3159		.off1	= td_var_offset(gid),
3160		.help	= "Run job with this group ID",
3161		.category = FIO_OPT_C_GENERAL,
3162		.group	= FIO_OPT_G_CRED,
3163	},
3164	{
3165		.name	= "kb_base",
3166		.lname	= "KB Base",
3167		.type	= FIO_OPT_INT,
3168		.off1	= td_var_offset(kb_base),
3169		.prio	= 1,
3170		.def	= "1024",
3171		.posval = {
3172			  { .ival = "1024",
3173			    .oval = 1024,
3174			    .help = "Use 1024 as the K base",
3175			  },
3176			  { .ival = "1000",
3177			    .oval = 1000,
3178			    .help = "Use 1000 as the K base",
3179			  },
3180		},
3181		.help	= "How many bytes per KB for reporting (1000 or 1024)",
3182		.category = FIO_OPT_C_GENERAL,
3183		.group	= FIO_OPT_G_INVALID,
3184	},
3185	{
3186		.name	= "unit_base",
3187		.lname	= "Base unit for reporting (Bits or Bytes)",
3188		.type	= FIO_OPT_INT,
3189		.off1	= td_var_offset(unit_base),
3190		.prio	= 1,
3191		.posval = {
3192			  { .ival = "0",
3193			    .oval = 0,
3194			    .help = "Auto-detect",
3195			  },
3196			  { .ival = "8",
3197			    .oval = 8,
3198			    .help = "Normal (byte based)",
3199			  },
3200			  { .ival = "1",
3201			    .oval = 1,
3202			    .help = "Bit based",
3203			  },
3204		},
3205		.help	= "Bit multiple of result summary data (8 for byte, 1 for bit)",
3206		.category = FIO_OPT_C_GENERAL,
3207		.group	= FIO_OPT_G_INVALID,
3208	},
3209	{
3210		.name	= "hugepage-size",
3211		.lname	= "Hugepage size",
3212		.type	= FIO_OPT_INT,
3213		.off1	= td_var_offset(hugepage_size),
3214		.help	= "When using hugepages, specify size of each page",
3215		.def	= __fio_stringify(FIO_HUGE_PAGE),
3216		.interval = 1024 * 1024,
3217		.category = FIO_OPT_C_GENERAL,
3218		.group	= FIO_OPT_G_INVALID,
3219	},
3220	{
3221		.name	= "flow_id",
3222		.lname	= "I/O flow ID",
3223		.type	= FIO_OPT_INT,
3224		.off1	= td_var_offset(flow_id),
3225		.help	= "The flow index ID to use",
3226		.def	= "0",
3227		.category = FIO_OPT_C_IO,
3228		.group	= FIO_OPT_G_IO_FLOW,
3229	},
3230	{
3231		.name	= "flow",
3232		.lname	= "I/O flow weight",
3233		.type	= FIO_OPT_INT,
3234		.off1	= td_var_offset(flow),
3235		.help	= "Weight for flow control of this job",
3236		.parent	= "flow_id",
3237		.hide	= 1,
3238		.def	= "0",
3239		.category = FIO_OPT_C_IO,
3240		.group	= FIO_OPT_G_IO_FLOW,
3241	},
3242	{
3243		.name	= "flow_watermark",
3244		.lname	= "I/O flow watermark",
3245		.type	= FIO_OPT_INT,
3246		.off1	= td_var_offset(flow_watermark),
3247		.help	= "High watermark for flow control. This option"
3248			" should be set to the same value for all threads"
3249			" with non-zero flow.",
3250		.parent	= "flow_id",
3251		.hide	= 1,
3252		.def	= "1024",
3253		.category = FIO_OPT_C_IO,
3254		.group	= FIO_OPT_G_IO_FLOW,
3255	},
3256	{
3257		.name	= "flow_sleep",
3258		.lname	= "I/O flow sleep",
3259		.type	= FIO_OPT_INT,
3260		.off1	= td_var_offset(flow_sleep),
3261		.help	= "How many microseconds to sleep after being held"
3262			" back by the flow control mechanism",
3263		.parent	= "flow_id",
3264		.hide	= 1,
3265		.def	= "0",
3266		.category = FIO_OPT_C_IO,
3267		.group	= FIO_OPT_G_IO_FLOW,
3268	},
3269	{
3270		.name = NULL,
3271	},
3272};
3273
3274static void add_to_lopt(struct option *lopt, struct fio_option *o,
3275			const char *name, int val)
3276{
3277	lopt->name = (char *) name;
3278	lopt->val = val;
3279	if (o->type == FIO_OPT_STR_SET)
3280		lopt->has_arg = optional_argument;
3281	else
3282		lopt->has_arg = required_argument;
3283}
3284
3285static void options_to_lopts(struct fio_option *opts,
3286			      struct option *long_options,
3287			      int i, int option_type)
3288{
3289	struct fio_option *o = &opts[0];
3290	while (o->name) {
3291		add_to_lopt(&long_options[i], o, o->name, option_type);
3292		if (o->alias) {
3293			i++;
3294			add_to_lopt(&long_options[i], o, o->alias, option_type);
3295		}
3296
3297		i++;
3298		o++;
3299		assert(i < FIO_NR_OPTIONS);
3300	}
3301}
3302
3303void fio_options_set_ioengine_opts(struct option *long_options,
3304				   struct thread_data *td)
3305{
3306	unsigned int i;
3307
3308	i = 0;
3309	while (long_options[i].name) {
3310		if (long_options[i].val == FIO_GETOPT_IOENGINE) {
3311			memset(&long_options[i], 0, sizeof(*long_options));
3312			break;
3313		}
3314		i++;
3315	}
3316
3317	/*
3318	 * Just clear out the prior ioengine options.
3319	 */
3320	if (!td || !td->eo)
3321		return;
3322
3323	options_to_lopts(td->io_ops->options, long_options, i,
3324			 FIO_GETOPT_IOENGINE);
3325}
3326
3327void fio_options_dup_and_init(struct option *long_options)
3328{
3329	unsigned int i;
3330
3331	options_init(fio_options);
3332
3333	i = 0;
3334	while (long_options[i].name)
3335		i++;
3336
3337	options_to_lopts(fio_options, long_options, i, FIO_GETOPT_JOB);
3338}
3339
3340struct fio_keyword {
3341	const char *word;
3342	const char *desc;
3343	char *replace;
3344};
3345
3346static struct fio_keyword fio_keywords[] = {
3347	{
3348		.word	= "$pagesize",
3349		.desc	= "Page size in the system",
3350	},
3351	{
3352		.word	= "$mb_memory",
3353		.desc	= "Megabytes of memory online",
3354	},
3355	{
3356		.word	= "$ncpus",
3357		.desc	= "Number of CPUs online in the system",
3358	},
3359	{
3360		.word	= NULL,
3361	},
3362};
3363
3364void fio_keywords_init(void)
3365{
3366	unsigned long long mb_memory;
3367	char buf[128];
3368	long l;
3369
3370	sprintf(buf, "%lu", (unsigned long) page_size);
3371	fio_keywords[0].replace = strdup(buf);
3372
3373	mb_memory = os_phys_mem() / (1024 * 1024);
3374	sprintf(buf, "%llu", mb_memory);
3375	fio_keywords[1].replace = strdup(buf);
3376
3377	l = cpus_online();
3378	sprintf(buf, "%lu", l);
3379	fio_keywords[2].replace = strdup(buf);
3380}
3381
3382#define BC_APP		"bc"
3383
3384static char *bc_calc(char *str)
3385{
3386	char buf[128], *tmp;
3387	FILE *f;
3388	int ret;
3389
3390	/*
3391	 * No math, just return string
3392	 */
3393	if ((!strchr(str, '+') && !strchr(str, '-') && !strchr(str, '*') &&
3394	     !strchr(str, '/')) || strchr(str, '\''))
3395		return str;
3396
3397	/*
3398	 * Split option from value, we only need to calculate the value
3399	 */
3400	tmp = strchr(str, '=');
3401	if (!tmp)
3402		return str;
3403
3404	tmp++;
3405
3406	/*
3407	 * Prevent buffer overflows; such a case isn't reasonable anyway
3408	 */
3409	if (strlen(str) >= 128 || strlen(tmp) > 100)
3410		return str;
3411
3412	sprintf(buf, "which %s > /dev/null", BC_APP);
3413	if (system(buf)) {
3414		log_err("fio: bc is needed for performing math\n");
3415		return NULL;
3416	}
3417
3418	sprintf(buf, "echo '%s' | %s", tmp, BC_APP);
3419	f = popen(buf, "r");
3420	if (!f)
3421		return NULL;
3422
3423	ret = fread(&buf[tmp - str], 1, 128 - (tmp - str), f);
3424	if (ret <= 0)
3425		return NULL;
3426
3427	pclose(f);
3428	buf[(tmp - str) + ret - 1] = '\0';
3429	memcpy(buf, str, tmp - str);
3430	free(str);
3431	return strdup(buf);
3432}
3433
3434/*
3435 * Return a copy of the input string with substrings of the form ${VARNAME}
3436 * substituted with the value of the environment variable VARNAME.  The
3437 * substitution always occurs, even if VARNAME is empty or the corresponding
3438 * environment variable undefined.
3439 */
3440static char *option_dup_subs(const char *opt)
3441{
3442	char out[OPT_LEN_MAX+1];
3443	char in[OPT_LEN_MAX+1];
3444	char *outptr = out;
3445	char *inptr = in;
3446	char *ch1, *ch2, *env;
3447	ssize_t nchr = OPT_LEN_MAX;
3448	size_t envlen;
3449
3450	if (strlen(opt) + 1 > OPT_LEN_MAX) {
3451		log_err("OPT_LEN_MAX (%d) is too small\n", OPT_LEN_MAX);
3452		return NULL;
3453	}
3454
3455	in[OPT_LEN_MAX] = '\0';
3456	strncpy(in, opt, OPT_LEN_MAX);
3457
3458	while (*inptr && nchr > 0) {
3459		if (inptr[0] == '$' && inptr[1] == '{') {
3460			ch2 = strchr(inptr, '}');
3461			if (ch2 && inptr+1 < ch2) {
3462				ch1 = inptr+2;
3463				inptr = ch2+1;
3464				*ch2 = '\0';
3465
3466				env = getenv(ch1);
3467				if (env) {
3468					envlen = strlen(env);
3469					if (envlen <= nchr) {
3470						memcpy(outptr, env, envlen);
3471						outptr += envlen;
3472						nchr -= envlen;
3473					}
3474				}
3475
3476				continue;
3477			}
3478		}
3479
3480		*outptr++ = *inptr++;
3481		--nchr;
3482	}
3483
3484	*outptr = '\0';
3485	return strdup(out);
3486}
3487
3488/*
3489 * Look for reserved variable names and replace them with real values
3490 */
3491static char *fio_keyword_replace(char *opt)
3492{
3493	char *s;
3494	int i;
3495	int docalc = 0;
3496
3497	for (i = 0; fio_keywords[i].word != NULL; i++) {
3498		struct fio_keyword *kw = &fio_keywords[i];
3499
3500		while ((s = strstr(opt, kw->word)) != NULL) {
3501			char *new = malloc(strlen(opt) + 1);
3502			char *o_org = opt;
3503			int olen = s - opt;
3504			int len;
3505
3506			/*
3507			 * Copy part of the string before the keyword and
3508			 * sprintf() the replacement after it.
3509			 */
3510			memcpy(new, opt, olen);
3511			len = sprintf(new + olen, "%s", kw->replace);
3512
3513			/*
3514			 * If there's more in the original string, copy that
3515			 * in too
3516			 */
3517			opt += strlen(kw->word) + olen;
3518			if (strlen(opt))
3519				memcpy(new + olen + len, opt, opt - o_org - 1);
3520
3521			/*
3522			 * replace opt and free the old opt
3523			 */
3524			opt = new;
3525			free(o_org);
3526
3527			docalc = 1;
3528		}
3529	}
3530
3531	/*
3532	 * Check for potential math and invoke bc, if possible
3533	 */
3534	if (docalc)
3535		opt = bc_calc(opt);
3536
3537	return opt;
3538}
3539
3540static char **dup_and_sub_options(char **opts, int num_opts)
3541{
3542	int i;
3543	char **opts_copy = malloc(num_opts * sizeof(*opts));
3544	for (i = 0; i < num_opts; i++) {
3545		opts_copy[i] = option_dup_subs(opts[i]);
3546		if (!opts_copy[i])
3547			continue;
3548		opts_copy[i] = fio_keyword_replace(opts_copy[i]);
3549	}
3550	return opts_copy;
3551}
3552
3553int fio_options_parse(struct thread_data *td, char **opts, int num_opts)
3554{
3555	int i, ret, unknown;
3556	char **opts_copy;
3557
3558	sort_options(opts, fio_options, num_opts);
3559	opts_copy = dup_and_sub_options(opts, num_opts);
3560
3561	for (ret = 0, i = 0, unknown = 0; i < num_opts; i++) {
3562		struct fio_option *o;
3563		int newret = parse_option(opts_copy[i], opts[i], fio_options,
3564						&o, td);
3565
3566		if (opts_copy[i]) {
3567			if (newret && !o) {
3568				unknown++;
3569				continue;
3570			}
3571			free(opts_copy[i]);
3572			opts_copy[i] = NULL;
3573		}
3574
3575		ret |= newret;
3576	}
3577
3578	if (unknown) {
3579		ret |= ioengine_load(td);
3580		if (td->eo) {
3581			sort_options(opts_copy, td->io_ops->options, num_opts);
3582			opts = opts_copy;
3583		}
3584		for (i = 0; i < num_opts; i++) {
3585			struct fio_option *o = NULL;
3586			int newret = 1;
3587			if (!opts_copy[i])
3588				continue;
3589
3590			if (td->eo)
3591				newret = parse_option(opts_copy[i], opts[i],
3592						      td->io_ops->options, &o,
3593						      td->eo);
3594
3595			ret |= newret;
3596			if (!o)
3597				log_err("Bad option <%s>\n", opts[i]);
3598
3599			free(opts_copy[i]);
3600			opts_copy[i] = NULL;
3601		}
3602	}
3603
3604	free(opts_copy);
3605	return ret;
3606}
3607
3608int fio_cmd_option_parse(struct thread_data *td, const char *opt, char *val)
3609{
3610	return parse_cmd_option(opt, val, fio_options, td);
3611}
3612
3613int fio_cmd_ioengine_option_parse(struct thread_data *td, const char *opt,
3614				char *val)
3615{
3616	return parse_cmd_option(opt, val, td->io_ops->options, td->eo);
3617}
3618
3619void fio_fill_default_options(struct thread_data *td)
3620{
3621	fill_default_options(td, fio_options);
3622}
3623
3624int fio_show_option_help(const char *opt)
3625{
3626	return show_cmd_help(fio_options, opt);
3627}
3628
3629void options_mem_dupe(void *data, struct fio_option *options)
3630{
3631	struct fio_option *o;
3632	char **ptr;
3633
3634	for (o = &options[0]; o->name; o++) {
3635		if (o->type != FIO_OPT_STR_STORE)
3636			continue;
3637
3638		ptr = td_var(data, o->off1);
3639		if (*ptr)
3640			*ptr = strdup(*ptr);
3641	}
3642}
3643
3644/*
3645 * dupe FIO_OPT_STR_STORE options
3646 */
3647void fio_options_mem_dupe(struct thread_data *td)
3648{
3649	options_mem_dupe(&td->o, fio_options);
3650
3651	if (td->eo && td->io_ops) {
3652		void *oldeo = td->eo;
3653
3654		td->eo = malloc(td->io_ops->option_struct_size);
3655		memcpy(td->eo, oldeo, td->io_ops->option_struct_size);
3656		options_mem_dupe(td->eo, td->io_ops->options);
3657	}
3658}
3659
3660unsigned int fio_get_kb_base(void *data)
3661{
3662	struct thread_options *o = data;
3663	unsigned int kb_base = 0;
3664
3665	if (o)
3666		kb_base = o->kb_base;
3667	if (!kb_base)
3668		kb_base = 1024;
3669
3670	return kb_base;
3671}
3672
3673int add_option(struct fio_option *o)
3674{
3675	struct fio_option *__o;
3676	int opt_index = 0;
3677
3678	__o = fio_options;
3679	while (__o->name) {
3680		opt_index++;
3681		__o++;
3682	}
3683
3684	memcpy(&fio_options[opt_index], o, sizeof(*o));
3685	return 0;
3686}
3687
3688void invalidate_profile_options(const char *prof_name)
3689{
3690	struct fio_option *o;
3691
3692	o = fio_options;
3693	while (o->name) {
3694		if (o->prof_name && !strcmp(o->prof_name, prof_name)) {
3695			o->type = FIO_OPT_INVALID;
3696			o->prof_name = NULL;
3697		}
3698		o++;
3699	}
3700}
3701
3702void add_opt_posval(const char *optname, const char *ival, const char *help)
3703{
3704	struct fio_option *o;
3705	unsigned int i;
3706
3707	o = find_option(fio_options, optname);
3708	if (!o)
3709		return;
3710
3711	for (i = 0; i < PARSE_MAX_VP; i++) {
3712		if (o->posval[i].ival)
3713			continue;
3714
3715		o->posval[i].ival = ival;
3716		o->posval[i].help = help;
3717		break;
3718	}
3719}
3720
3721void del_opt_posval(const char *optname, const char *ival)
3722{
3723	struct fio_option *o;
3724	unsigned int i;
3725
3726	o = find_option(fio_options, optname);
3727	if (!o)
3728		return;
3729
3730	for (i = 0; i < PARSE_MAX_VP; i++) {
3731		if (!o->posval[i].ival)
3732			continue;
3733		if (strcmp(o->posval[i].ival, ival))
3734			continue;
3735
3736		o->posval[i].ival = NULL;
3737		o->posval[i].help = NULL;
3738	}
3739}
3740
3741void fio_options_free(struct thread_data *td)
3742{
3743	options_free(fio_options, td);
3744	if (td->eo && td->io_ops && td->io_ops->options) {
3745		options_free(td->io_ops->options, td->eo);
3746		free(td->eo);
3747		td->eo = NULL;
3748	}
3749}
3750
3751struct fio_option *fio_option_find(const char *name)
3752{
3753	return find_option(fio_options, name);
3754}
3755
3756