options.c revision 767a0a6603d5611a2319b4d1d84ecd78100c734f
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_data *td, 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	td->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 == td->o.bssplit_nr[ddir]) {
88			td->o.bssplit_nr[ddir] <<= 1;
89			bssplit = realloc(bssplit, td->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, td)) {
106			log_err("fio: bssplit conversion failed\n");
107			free(td->o.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	td->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 < td->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 < td->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	td->o.min_bs[ddir] = min_bs;
155	td->o.max_bs[ddir] = max_bs;
156
157	/*
158	 * now sort based on percentages, for ease of lookup
159	 */
160	qsort(bssplit, td->o.bssplit_nr[ddir], sizeof(struct bssplit), bs_cmp);
161	td->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;
169	int ret = 0;
170
171	p = str = strdup(input);
172
173	strip_blank_front(&str);
174	strip_blank_end(str);
175
176	odir = strchr(str, ',');
177	if (odir) {
178		ret = bssplit_ddir(td, DDIR_WRITE, odir + 1);
179		if (!ret) {
180			*odir = '\0';
181			ret = bssplit_ddir(td, DDIR_READ, str);
182		}
183	} else {
184		char *op;
185
186		op = strdup(str);
187
188		ret = bssplit_ddir(td, DDIR_READ, str);
189		if (!ret)
190			ret = bssplit_ddir(td, DDIR_WRITE, op);
191
192		free(op);
193	}
194
195	free(p);
196	return ret;
197}
198
199static int str_rw_cb(void *data, const char *str)
200{
201	struct thread_data *td = data;
202	char *nr = get_opt_postfix(str);
203
204	td->o.ddir_seq_nr = 1;
205	td->o.ddir_seq_add = 0;
206
207	if (!nr)
208		return 0;
209
210	if (td_random(td))
211		td->o.ddir_seq_nr = atoi(nr);
212	else {
213		long long val;
214
215		if (str_to_decimal(nr, &val, 1, td)) {
216			log_err("fio: rw postfix parsing failed\n");
217			free(nr);
218			return 1;
219		}
220
221		td->o.ddir_seq_add = val;
222	}
223
224	free(nr);
225	return 0;
226}
227
228static int str_mem_cb(void *data, const char *mem)
229{
230	struct thread_data *td = data;
231
232	if (td->o.mem_type == MEM_MMAPHUGE || td->o.mem_type == MEM_MMAP) {
233		td->mmapfile = get_opt_postfix(mem);
234		if (td->o.mem_type == MEM_MMAPHUGE && !td->mmapfile) {
235			log_err("fio: mmaphuge:/path/to/file\n");
236			return 1;
237		}
238	}
239
240	return 0;
241}
242
243static int str_verify_cb(void *data, const char *mem)
244{
245	struct thread_data *td = data;
246
247	if (td->o.verify == VERIFY_CRC32C_INTEL ||
248	    td->o.verify == VERIFY_CRC32C) {
249		crc32c_intel_probe();
250	}
251
252	return 0;
253}
254
255static int fio_clock_source_cb(void *data, const char *str)
256{
257	struct thread_data *td = data;
258
259	fio_clock_source = td->o.clocksource;
260	fio_time_init();
261	return 0;
262}
263
264static int str_lockmem_cb(void fio_unused *data, unsigned long long *val)
265{
266	mlock_size = *val;
267	return 0;
268}
269
270static int str_rwmix_read_cb(void *data, unsigned long long *val)
271{
272	struct thread_data *td = data;
273
274	td->o.rwmix[DDIR_READ] = *val;
275	td->o.rwmix[DDIR_WRITE] = 100 - *val;
276	return 0;
277}
278
279static int str_rwmix_write_cb(void *data, unsigned long long *val)
280{
281	struct thread_data *td = data;
282
283	td->o.rwmix[DDIR_WRITE] = *val;
284	td->o.rwmix[DDIR_READ] = 100 - *val;
285	return 0;
286}
287
288#ifdef FIO_HAVE_IOPRIO
289static int str_prioclass_cb(void *data, unsigned long long *val)
290{
291	struct thread_data *td = data;
292	unsigned short mask;
293
294	/*
295	 * mask off old class bits, str_prio_cb() may have set a default class
296	 */
297	mask = (1 << IOPRIO_CLASS_SHIFT) - 1;
298	td->ioprio &= mask;
299
300	td->ioprio |= *val << IOPRIO_CLASS_SHIFT;
301	td->ioprio_set = 1;
302	return 0;
303}
304
305static int str_prio_cb(void *data, unsigned long long *val)
306{
307	struct thread_data *td = data;
308
309	td->ioprio |= *val;
310
311	/*
312	 * If no class is set, assume BE
313	 */
314	if ((td->ioprio >> IOPRIO_CLASS_SHIFT) == 0)
315		td->ioprio |= IOPRIO_CLASS_BE << IOPRIO_CLASS_SHIFT;
316
317	td->ioprio_set = 1;
318	return 0;
319}
320#endif
321
322static int str_exitall_cb(void)
323{
324	exitall_on_terminate = 1;
325	return 0;
326}
327
328#ifdef FIO_HAVE_CPU_AFFINITY
329static int str_cpumask_cb(void *data, unsigned long long *val)
330{
331	struct thread_data *td = data;
332	unsigned int i;
333	long max_cpu;
334	int ret;
335
336	ret = fio_cpuset_init(&td->o.cpumask);
337	if (ret < 0) {
338		log_err("fio: cpuset_init failed\n");
339		td_verror(td, ret, "fio_cpuset_init");
340		return 1;
341	}
342
343	max_cpu = cpus_online();
344
345	for (i = 0; i < sizeof(int) * 8; i++) {
346		if ((1 << i) & *val) {
347			if (i > max_cpu) {
348				log_err("fio: CPU %d too large (max=%ld)\n", i,
349								max_cpu);
350				return 1;
351			}
352			dprint(FD_PARSE, "set cpu allowed %d\n", i);
353			fio_cpu_set(&td->o.cpumask, i);
354		}
355	}
356
357	td->o.cpumask_set = 1;
358	return 0;
359}
360
361static int set_cpus_allowed(struct thread_data *td, os_cpu_mask_t *mask,
362			    const char *input)
363{
364	char *cpu, *str, *p;
365	long max_cpu;
366	int ret = 0;
367
368	ret = fio_cpuset_init(mask);
369	if (ret < 0) {
370		log_err("fio: cpuset_init failed\n");
371		td_verror(td, ret, "fio_cpuset_init");
372		return 1;
373	}
374
375	p = str = strdup(input);
376
377	strip_blank_front(&str);
378	strip_blank_end(str);
379
380	max_cpu = cpus_online();
381
382	while ((cpu = strsep(&str, ",")) != NULL) {
383		char *str2, *cpu2;
384		int icpu, icpu2;
385
386		if (!strlen(cpu))
387			break;
388
389		str2 = cpu;
390		icpu2 = -1;
391		while ((cpu2 = strsep(&str2, "-")) != NULL) {
392			if (!strlen(cpu2))
393				break;
394
395			icpu2 = atoi(cpu2);
396		}
397
398		icpu = atoi(cpu);
399		if (icpu2 == -1)
400			icpu2 = icpu;
401		while (icpu <= icpu2) {
402			if (icpu >= FIO_MAX_CPUS) {
403				log_err("fio: your OS only supports up to"
404					" %d CPUs\n", (int) FIO_MAX_CPUS);
405				ret = 1;
406				break;
407			}
408			if (icpu > max_cpu) {
409				log_err("fio: CPU %d too large (max=%ld)\n",
410							icpu, max_cpu);
411				ret = 1;
412				break;
413			}
414
415			dprint(FD_PARSE, "set cpu allowed %d\n", icpu);
416			fio_cpu_set(mask, icpu);
417			icpu++;
418		}
419		if (ret)
420			break;
421	}
422
423	free(p);
424	if (!ret)
425		td->o.cpumask_set = 1;
426	return ret;
427}
428
429static int str_cpus_allowed_cb(void *data, const char *input)
430{
431	struct thread_data *td = data;
432	int ret;
433
434	ret = set_cpus_allowed(td, &td->o.cpumask, input);
435	if (!ret)
436		td->o.cpumask_set = 1;
437
438	return ret;
439}
440
441static int str_verify_cpus_allowed_cb(void *data, const char *input)
442{
443	struct thread_data *td = data;
444	int ret;
445
446	ret = set_cpus_allowed(td, &td->o.verify_cpumask, input);
447	if (!ret)
448		td->o.verify_cpumask_set = 1;
449
450	return ret;
451}
452#endif
453
454#ifdef FIO_HAVE_TRIM
455static int str_verify_trim_cb(void *data, unsigned long long *val)
456{
457	struct thread_data *td = data;
458
459	td->o.trim_percentage = *val;
460	return 0;
461}
462#endif
463
464static int str_fst_cb(void *data, const char *str)
465{
466	struct thread_data *td = data;
467	char *nr = get_opt_postfix(str);
468
469	td->file_service_nr = 1;
470	if (nr) {
471		td->file_service_nr = atoi(nr);
472		free(nr);
473	}
474
475	return 0;
476}
477
478#ifdef FIO_HAVE_SYNC_FILE_RANGE
479static int str_sfr_cb(void *data, const char *str)
480{
481	struct thread_data *td = data;
482	char *nr = get_opt_postfix(str);
483
484	td->sync_file_range_nr = 1;
485	if (nr) {
486		td->sync_file_range_nr = atoi(nr);
487		free(nr);
488	}
489
490	return 0;
491}
492#endif
493
494static int check_dir(struct thread_data *td, char *fname)
495{
496#if 0
497	char file[PATH_MAX], *dir;
498	int elen = 0;
499
500	if (td->o.directory) {
501		strcpy(file, td->o.directory);
502		strcat(file, "/");
503		elen = strlen(file);
504	}
505
506	sprintf(file + elen, "%s", fname);
507	dir = dirname(file);
508
509	{
510	struct stat sb;
511	/*
512	 * We can't do this on FIO_DISKLESSIO engines. The engine isn't loaded
513	 * yet, so we can't do this check right here...
514	 */
515	if (lstat(dir, &sb) < 0) {
516		int ret = errno;
517
518		log_err("fio: %s is not a directory\n", dir);
519		td_verror(td, ret, "lstat");
520		return 1;
521	}
522
523	if (!S_ISDIR(sb.st_mode)) {
524		log_err("fio: %s is not a directory\n", dir);
525		return 1;
526	}
527	}
528#endif
529
530	return 0;
531}
532
533/*
534 * Return next file in the string. Files are separated with ':'. If the ':'
535 * is escaped with a '\', then that ':' is part of the filename and does not
536 * indicate a new file.
537 */
538static char *get_next_file_name(char **ptr)
539{
540	char *str = *ptr;
541	char *p, *start;
542
543	if (!str || !strlen(str))
544		return NULL;
545
546	start = str;
547	do {
548		/*
549		 * No colon, we are done
550		 */
551		p = strchr(str, ':');
552		if (!p) {
553			*ptr = NULL;
554			break;
555		}
556
557		/*
558		 * We got a colon, but it's the first character. Skip and
559		 * continue
560		 */
561		if (p == start) {
562			str = ++start;
563			continue;
564		}
565
566		if (*(p - 1) != '\\') {
567			*p = '\0';
568			*ptr = p + 1;
569			break;
570		}
571
572		memmove(p - 1, p, strlen(p) + 1);
573		str = p;
574	} while (1);
575
576	return start;
577}
578
579static int str_filename_cb(void *data, const char *input)
580{
581	struct thread_data *td = data;
582	char *fname, *str, *p;
583
584	p = str = strdup(input);
585
586	strip_blank_front(&str);
587	strip_blank_end(str);
588
589	if (!td->files_index)
590		td->o.nr_files = 0;
591
592	while ((fname = get_next_file_name(&str)) != NULL) {
593		if (!strlen(fname))
594			break;
595		if (check_dir(td, fname)) {
596			free(p);
597			return 1;
598		}
599		add_file(td, fname);
600		td->o.nr_files++;
601	}
602
603	free(p);
604	return 0;
605}
606
607static int str_directory_cb(void *data, const char fio_unused *str)
608{
609	struct thread_data *td = data;
610	struct stat sb;
611
612	if (lstat(td->o.directory, &sb) < 0) {
613		int ret = errno;
614
615		log_err("fio: %s is not a directory\n", td->o.directory);
616		td_verror(td, ret, "lstat");
617		return 1;
618	}
619	if (!S_ISDIR(sb.st_mode)) {
620		log_err("fio: %s is not a directory\n", td->o.directory);
621		return 1;
622	}
623
624	return 0;
625}
626
627static int str_opendir_cb(void *data, const char fio_unused *str)
628{
629	struct thread_data *td = data;
630
631	if (!td->files_index)
632		td->o.nr_files = 0;
633
634	return add_dir_files(td, td->o.opendir);
635}
636
637static int str_verify_offset_cb(void *data, unsigned long long *off)
638{
639	struct thread_data *td = data;
640
641	if (*off && *off < sizeof(struct verify_header)) {
642		log_err("fio: verify_offset too small\n");
643		return 1;
644	}
645
646	td->o.verify_offset = *off;
647	return 0;
648}
649
650static int str_verify_pattern_cb(void *data, const char *input)
651{
652	struct thread_data *td = data;
653	long off;
654	int i = 0, j = 0, len, k, base = 10;
655	char* loc1, * loc2;
656
657	loc1 = strstr(input, "0x");
658	loc2 = strstr(input, "0X");
659	if (loc1 || loc2)
660		base = 16;
661	off = strtol(input, NULL, base);
662	if (off != LONG_MAX || errno != ERANGE) {
663		while (off) {
664			td->o.verify_pattern[i] = off & 0xff;
665			off >>= 8;
666			i++;
667		}
668	} else {
669		len = strlen(input);
670		k = len - 1;
671		if (base == 16) {
672			if (loc1)
673				j = loc1 - input + 2;
674			else
675				j = loc2 - input + 2;
676		} else
677			return 1;
678		if (len - j < MAX_PATTERN_SIZE * 2) {
679			while (k >= j) {
680				off = converthexchartoint(input[k--]);
681				if (k >= j)
682					off += (converthexchartoint(input[k--])
683						* 16);
684				td->o.verify_pattern[i++] = (char) off;
685			}
686		}
687	}
688
689	/*
690	 * Fill the pattern all the way to the end. This greatly reduces
691	 * the number of memcpy's we have to do when verifying the IO.
692	 */
693	while (i > 1 && i * 2 <= MAX_PATTERN_SIZE) {
694		memcpy(&td->o.verify_pattern[i], &td->o.verify_pattern[0], i);
695		i *= 2;
696	}
697	if (i == 1) {
698		/*
699		 * The code in verify_io_u_pattern assumes a single byte pattern
700		 * fills the whole verify pattern buffer.
701		 */
702		memset(td->o.verify_pattern, td->o.verify_pattern[0],
703		       MAX_PATTERN_SIZE);
704	}
705
706	td->o.verify_pattern_bytes = i;
707
708	/*
709	 * VERIFY_META could already be set
710	 */
711	if (td->o.verify == VERIFY_NONE)
712		td->o.verify = VERIFY_PATTERN;
713
714	return 0;
715}
716
717static int str_lockfile_cb(void *data, const char *str)
718{
719	struct thread_data *td = data;
720	char *nr = get_opt_postfix(str);
721
722	td->o.lockfile_batch = 1;
723	if (nr) {
724		td->o.lockfile_batch = atoi(nr);
725		free(nr);
726	}
727
728	return 0;
729}
730
731static int str_write_bw_log_cb(void *data, const char *str)
732{
733	struct thread_data *td = data;
734
735	if (str)
736		td->o.bw_log_file = strdup(str);
737
738	td->o.write_bw_log = 1;
739	return 0;
740}
741
742static int str_write_lat_log_cb(void *data, const char *str)
743{
744	struct thread_data *td = data;
745
746	if (str)
747		td->o.lat_log_file = strdup(str);
748
749	td->o.write_lat_log = 1;
750	return 0;
751}
752
753static int str_write_iops_log_cb(void *data, const char *str)
754{
755	struct thread_data *td = data;
756
757	if (str)
758		td->o.iops_log_file = strdup(str);
759
760	td->o.write_iops_log = 1;
761	return 0;
762}
763
764static int str_gtod_reduce_cb(void *data, int *il)
765{
766	struct thread_data *td = data;
767	int val = *il;
768
769	td->o.disable_lat = !!val;
770	td->o.disable_clat = !!val;
771	td->o.disable_slat = !!val;
772	td->o.disable_bw = !!val;
773	td->o.clat_percentiles = !val;
774	if (val)
775		td->tv_cache_mask = 63;
776
777	return 0;
778}
779
780static int str_gtod_cpu_cb(void *data, long long *il)
781{
782	struct thread_data *td = data;
783	int val = *il;
784
785	td->o.gtod_cpu = val;
786	td->o.gtod_offload = 1;
787	return 0;
788}
789
790static int str_size_cb(void *data, unsigned long long *__val)
791{
792	struct thread_data *td = data;
793	unsigned long long v = *__val;
794
795	if (parse_is_percent(v)) {
796		td->o.size = 0;
797		td->o.size_percent = -1ULL - v;
798	} else
799		td->o.size = v;
800
801	return 0;
802}
803
804static int rw_verify(struct fio_option *o, void *data)
805{
806	struct thread_data *td = data;
807
808	if (read_only && td_write(td)) {
809		log_err("fio: job <%s> has write bit set, but fio is in"
810			" read-only mode\n", td->o.name);
811		return 1;
812	}
813
814	return 0;
815}
816
817static int gtod_cpu_verify(struct fio_option *o, void *data)
818{
819#ifndef FIO_HAVE_CPU_AFFINITY
820	struct thread_data *td = data;
821
822	if (td->o.gtod_cpu) {
823		log_err("fio: platform must support CPU affinity for"
824			"gettimeofday() offloading\n");
825		return 1;
826	}
827#endif
828
829	return 0;
830}
831
832static int kb_base_verify(struct fio_option *o, void *data)
833{
834	struct thread_data *td = data;
835
836	if (td->o.kb_base != 1024 && td->o.kb_base != 1000) {
837		log_err("fio: kb_base set to nonsensical value: %u\n",
838				td->o.kb_base);
839		return 1;
840	}
841
842	return 0;
843}
844
845/*
846 * Option grouping
847 */
848static struct opt_group fio_opt_groups[] = {
849	{
850		.name	= "Description",
851		.mask	= FIO_OPT_G_DESC,
852	},
853	{
854		.name	= "File",
855		.mask	= FIO_OPT_G_FILE,
856	},
857	{
858		.name	= "Misc",
859		.mask	= FIO_OPT_G_MISC,
860	},
861	{
862		.name	= "IO (main)",
863		.mask	= FIO_OPT_G_IO,
864	},
865	{
866		.name	= "IO direction",
867		.mask	= FIO_OPT_G_IO_DDIR,
868	},
869	{
870		.name	= "IO buffer",
871		.mask	= FIO_OPT_G_IO_BUF,
872	},
873	{
874		.name	= "Random",
875		.mask	= FIO_OPT_G_RAND,
876	},
877	{
878		.name	= "OS",
879		.mask	= FIO_OPT_G_OS,
880	},
881	{
882		.name	= "Memory",
883		.mask	= FIO_OPT_G_MEM,
884	},
885	{
886		.name	= "Verify",
887		.mask	= FIO_OPT_G_VERIFY,
888	},
889	{
890		.name	= "CPU",
891		.mask	= FIO_OPT_G_CPU,
892	},
893	{
894		.name	= "Log",
895		.mask	= FIO_OPT_G_LOG,
896	},
897	{
898		.name	= "Zone",
899		.mask	= FIO_OPT_G_ZONE,
900	},
901	{
902		.name	= "Cache",
903		.mask	= FIO_OPT_G_CACHE,
904	},
905	{
906		.name	= "Stat",
907		.mask	= FIO_OPT_G_STAT,
908	},
909	{
910		.name	= "Error",
911		.mask	= FIO_OPT_G_ERR,
912	},
913	{
914		.name	= "Job",
915		.mask	= FIO_OPT_G_JOB,
916	},
917	{
918		.name	= NULL,
919	},
920};
921
922struct opt_group *opt_group_from_mask(unsigned int *mask)
923{
924	struct opt_group *og;
925	int i;
926
927	if (*mask == FIO_OPT_G_INVALID)
928		return NULL;
929
930	for (i = 0; fio_opt_groups[i].name; i++) {
931		og = &fio_opt_groups[i];
932
933		if (*mask & og->mask) {
934			*mask &= ~(og->mask);
935			return og;
936		}
937	}
938
939	return NULL;
940}
941
942/*
943 * Map of job/command line options
944 */
945struct fio_option fio_options[FIO_MAX_OPTS] = {
946	{
947		.name	= "description",
948		.type	= FIO_OPT_STR_STORE,
949		.off1	= td_var_offset(description),
950		.help	= "Text job description",
951		.category = FIO_OPT_G_DESC,
952	},
953	{
954		.name	= "name",
955		.type	= FIO_OPT_STR_STORE,
956		.off1	= td_var_offset(name),
957		.help	= "Name of this job",
958		.category = FIO_OPT_G_DESC,
959	},
960	{
961		.name	= "directory",
962		.type	= FIO_OPT_STR_STORE,
963		.off1	= td_var_offset(directory),
964		.cb	= str_directory_cb,
965		.help	= "Directory to store files in",
966		.category = FIO_OPT_G_FILE,
967	},
968	{
969		.name	= "filename",
970		.type	= FIO_OPT_STR_STORE,
971		.off1	= td_var_offset(filename),
972		.cb	= str_filename_cb,
973		.prio	= -1, /* must come after "directory" */
974		.help	= "File(s) to use for the workload",
975		.category = FIO_OPT_G_FILE,
976	},
977	{
978		.name	= "kb_base",
979		.type	= FIO_OPT_INT,
980		.off1	= td_var_offset(kb_base),
981		.verify	= kb_base_verify,
982		.prio	= 1,
983		.def	= "1024",
984		.help	= "How many bytes per KB for reporting (1000 or 1024)",
985		.category = FIO_OPT_G_MISC,
986	},
987	{
988		.name	= "lockfile",
989		.type	= FIO_OPT_STR,
990		.cb	= str_lockfile_cb,
991		.off1	= td_var_offset(file_lock_mode),
992		.help	= "Lock file when doing IO to it",
993		.parent	= "filename",
994		.def	= "none",
995		.category = FIO_OPT_G_FILE,
996		.posval = {
997			  { .ival = "none",
998			    .oval = FILE_LOCK_NONE,
999			    .help = "No file locking",
1000			  },
1001			  { .ival = "exclusive",
1002			    .oval = FILE_LOCK_EXCLUSIVE,
1003			    .help = "Exclusive file lock",
1004			  },
1005			  {
1006			    .ival = "readwrite",
1007			    .oval = FILE_LOCK_READWRITE,
1008			    .help = "Read vs write lock",
1009			  },
1010		},
1011	},
1012	{
1013		.name	= "opendir",
1014		.type	= FIO_OPT_STR_STORE,
1015		.off1	= td_var_offset(opendir),
1016		.cb	= str_opendir_cb,
1017		.help	= "Recursively add files from this directory and down",
1018		.category = FIO_OPT_G_FILE,
1019	},
1020	{
1021		.name	= "rw",
1022		.alias	= "readwrite",
1023		.type	= FIO_OPT_STR,
1024		.cb	= str_rw_cb,
1025		.off1	= td_var_offset(td_ddir),
1026		.help	= "IO direction",
1027		.def	= "read",
1028		.verify	= rw_verify,
1029		.category = FIO_OPT_G_IO_DDIR,
1030		.posval = {
1031			  { .ival = "read",
1032			    .oval = TD_DDIR_READ,
1033			    .help = "Sequential read",
1034			  },
1035			  { .ival = "write",
1036			    .oval = TD_DDIR_WRITE,
1037			    .help = "Sequential write",
1038			  },
1039			  { .ival = "randread",
1040			    .oval = TD_DDIR_RANDREAD,
1041			    .help = "Random read",
1042			  },
1043			  { .ival = "randwrite",
1044			    .oval = TD_DDIR_RANDWRITE,
1045			    .help = "Random write",
1046			  },
1047			  { .ival = "rw",
1048			    .oval = TD_DDIR_RW,
1049			    .help = "Sequential read and write mix",
1050			  },
1051			  { .ival = "randrw",
1052			    .oval = TD_DDIR_RANDRW,
1053			    .help = "Random read and write mix"
1054			  },
1055		},
1056	},
1057	{
1058		.name	= "rw_sequencer",
1059		.type	= FIO_OPT_STR,
1060		.off1	= td_var_offset(rw_seq),
1061		.help	= "IO offset generator modifier",
1062		.def	= "sequential",
1063		.category = FIO_OPT_G_IO_DDIR,
1064		.posval = {
1065			  { .ival = "sequential",
1066			    .oval = RW_SEQ_SEQ,
1067			    .help = "Generate sequential offsets",
1068			  },
1069			  { .ival = "identical",
1070			    .oval = RW_SEQ_IDENT,
1071			    .help = "Generate identical offsets",
1072			  },
1073		},
1074	},
1075
1076	{
1077		.name	= "ioengine",
1078		.type	= FIO_OPT_STR_STORE,
1079		.off1	= td_var_offset(ioengine),
1080		.help	= "IO engine to use",
1081		.def	= FIO_PREFERRED_ENGINE,
1082		.category = FIO_OPT_G_IO,
1083		.posval	= {
1084			  { .ival = "sync",
1085			    .help = "Use read/write",
1086			  },
1087			  { .ival = "psync",
1088			    .help = "Use pread/pwrite",
1089			  },
1090			  { .ival = "vsync",
1091			    .help = "Use readv/writev",
1092			  },
1093#ifdef FIO_HAVE_LIBAIO
1094			  { .ival = "libaio",
1095			    .help = "Linux native asynchronous IO",
1096			  },
1097#endif
1098#ifdef FIO_HAVE_POSIXAIO
1099			  { .ival = "posixaio",
1100			    .help = "POSIX asynchronous IO",
1101			  },
1102#endif
1103#ifdef FIO_HAVE_SOLARISAIO
1104			  { .ival = "solarisaio",
1105			    .help = "Solaris native asynchronous IO",
1106			  },
1107#endif
1108#ifdef FIO_HAVE_WINDOWSAIO
1109			  { .ival = "windowsaio",
1110			    .help = "Windows native asynchronous IO"
1111			  },
1112#endif
1113			  { .ival = "mmap",
1114			    .help = "Memory mapped IO"
1115			  },
1116#ifdef FIO_HAVE_SPLICE
1117			  { .ival = "splice",
1118			    .help = "splice/vmsplice based IO",
1119			  },
1120			  { .ival = "netsplice",
1121			    .help = "splice/vmsplice to/from the network",
1122			  },
1123#endif
1124#ifdef FIO_HAVE_SGIO
1125			  { .ival = "sg",
1126			    .help = "SCSI generic v3 IO",
1127			  },
1128#endif
1129			  { .ival = "null",
1130			    .help = "Testing engine (no data transfer)",
1131			  },
1132			  { .ival = "net",
1133			    .help = "Network IO",
1134			  },
1135#ifdef FIO_HAVE_SYSLET
1136			  { .ival = "syslet-rw",
1137			    .help = "syslet enabled async pread/pwrite IO",
1138			  },
1139#endif
1140			  { .ival = "cpuio",
1141			    .help = "CPU cycle burner engine",
1142			  },
1143#ifdef FIO_HAVE_GUASI
1144			  { .ival = "guasi",
1145			    .help = "GUASI IO engine",
1146			  },
1147#endif
1148#ifdef FIO_HAVE_BINJECT
1149			  { .ival = "binject",
1150			    .help = "binject direct inject block engine",
1151			  },
1152#endif
1153#ifdef FIO_HAVE_RDMA
1154			  { .ival = "rdma",
1155			    .help = "RDMA IO engine",
1156			  },
1157#endif
1158			  { .ival = "external",
1159			    .help = "Load external engine (append name)",
1160			  },
1161		},
1162	},
1163	{
1164		.name	= "iodepth",
1165		.type	= FIO_OPT_INT,
1166		.off1	= td_var_offset(iodepth),
1167		.help	= "Number of IO buffers to keep in flight",
1168		.minval = 1,
1169		.def	= "1",
1170		.category = FIO_OPT_G_IO,
1171	},
1172	{
1173		.name	= "iodepth_batch",
1174		.alias	= "iodepth_batch_submit",
1175		.type	= FIO_OPT_INT,
1176		.off1	= td_var_offset(iodepth_batch),
1177		.help	= "Number of IO buffers to submit in one go",
1178		.parent	= "iodepth",
1179		.minval	= 1,
1180		.def	= "1",
1181		.category = FIO_OPT_G_IO,
1182	},
1183	{
1184		.name	= "iodepth_batch_complete",
1185		.type	= FIO_OPT_INT,
1186		.off1	= td_var_offset(iodepth_batch_complete),
1187		.help	= "Number of IO buffers to retrieve in one go",
1188		.parent	= "iodepth",
1189		.minval	= 0,
1190		.def	= "1",
1191		.category = FIO_OPT_G_IO,
1192	},
1193	{
1194		.name	= "iodepth_low",
1195		.type	= FIO_OPT_INT,
1196		.off1	= td_var_offset(iodepth_low),
1197		.help	= "Low water mark for queuing depth",
1198		.parent	= "iodepth",
1199		.category = FIO_OPT_G_IO,
1200	},
1201	{
1202		.name	= "size",
1203		.type	= FIO_OPT_STR_VAL,
1204		.cb	= str_size_cb,
1205		.help	= "Total size of device or files",
1206		.category = FIO_OPT_G_IO,
1207	},
1208	{
1209		.name	= "fill_device",
1210		.alias	= "fill_fs",
1211		.type	= FIO_OPT_BOOL,
1212		.off1	= td_var_offset(fill_device),
1213		.help	= "Write until an ENOSPC error occurs",
1214		.def	= "0",
1215		.category = FIO_OPT_G_IO,
1216	},
1217	{
1218		.name	= "filesize",
1219		.type	= FIO_OPT_STR_VAL,
1220		.off1	= td_var_offset(file_size_low),
1221		.off2	= td_var_offset(file_size_high),
1222		.minval = 1,
1223		.help	= "Size of individual files",
1224		.category = FIO_OPT_G_IO | FIO_OPT_G_FILE,
1225	},
1226	{
1227		.name	= "offset",
1228		.alias	= "fileoffset",
1229		.type	= FIO_OPT_STR_VAL,
1230		.off1	= td_var_offset(start_offset),
1231		.help	= "Start IO from this offset",
1232		.def	= "0",
1233		.category = FIO_OPT_G_IO,
1234	},
1235	{
1236		.name	= "offset_increment",
1237		.type	= FIO_OPT_STR_VAL,
1238		.off1	= td_var_offset(offset_increment),
1239		.help	= "What is the increment from one offset to the next",
1240		.parent = "offset",
1241		.def	= "0",
1242		.category = FIO_OPT_G_IO,
1243	},
1244	{
1245		.name	= "bs",
1246		.alias	= "blocksize",
1247		.type	= FIO_OPT_INT,
1248		.off1	= td_var_offset(bs[DDIR_READ]),
1249		.off2	= td_var_offset(bs[DDIR_WRITE]),
1250		.minval = 1,
1251		.help	= "Block size unit",
1252		.def	= "4k",
1253		.parent = "rw",
1254		.category = FIO_OPT_G_IO,
1255	},
1256	{
1257		.name	= "ba",
1258		.alias	= "blockalign",
1259		.type	= FIO_OPT_INT,
1260		.off1	= td_var_offset(ba[DDIR_READ]),
1261		.off2	= td_var_offset(ba[DDIR_WRITE]),
1262		.minval	= 1,
1263		.help	= "IO block offset alignment",
1264		.parent	= "rw",
1265		.category = FIO_OPT_G_IO | FIO_OPT_G_IO_BUF,
1266	},
1267	{
1268		.name	= "bsrange",
1269		.alias	= "blocksize_range",
1270		.type	= FIO_OPT_RANGE,
1271		.off1	= td_var_offset(min_bs[DDIR_READ]),
1272		.off2	= td_var_offset(max_bs[DDIR_READ]),
1273		.off3	= td_var_offset(min_bs[DDIR_WRITE]),
1274		.off4	= td_var_offset(max_bs[DDIR_WRITE]),
1275		.minval = 1,
1276		.help	= "Set block size range (in more detail than bs)",
1277		.parent = "rw",
1278		.category = FIO_OPT_G_IO,
1279	},
1280	{
1281		.name	= "bssplit",
1282		.type	= FIO_OPT_STR,
1283		.cb	= str_bssplit_cb,
1284		.help	= "Set a specific mix of block sizes",
1285		.parent	= "rw",
1286		.category = FIO_OPT_G_IO,
1287	},
1288	{
1289		.name	= "bs_unaligned",
1290		.alias	= "blocksize_unaligned",
1291		.type	= FIO_OPT_STR_SET,
1292		.off1	= td_var_offset(bs_unaligned),
1293		.help	= "Don't sector align IO buffer sizes",
1294		.parent = "rw",
1295		.category = FIO_OPT_G_IO,
1296	},
1297	{
1298		.name	= "randrepeat",
1299		.type	= FIO_OPT_BOOL,
1300		.off1	= td_var_offset(rand_repeatable),
1301		.help	= "Use repeatable random IO pattern",
1302		.def	= "1",
1303		.parent = "rw",
1304		.category = FIO_OPT_G_IO | FIO_OPT_G_RAND,
1305	},
1306	{
1307		.name	= "use_os_rand",
1308		.type	= FIO_OPT_BOOL,
1309		.off1	= td_var_offset(use_os_rand),
1310		.help	= "Set to use OS random generator",
1311		.def	= "0",
1312		.parent = "rw",
1313		.category = FIO_OPT_G_RAND,
1314	},
1315	{
1316		.name	= "norandommap",
1317		.type	= FIO_OPT_STR_SET,
1318		.off1	= td_var_offset(norandommap),
1319		.help	= "Accept potential duplicate random blocks",
1320		.parent = "rw",
1321		.category = FIO_OPT_G_RAND,
1322	},
1323	{
1324		.name	= "softrandommap",
1325		.type	= FIO_OPT_BOOL,
1326		.off1	= td_var_offset(softrandommap),
1327		.help	= "Set norandommap if randommap allocation fails",
1328		.parent	= "norandommap",
1329		.def	= "0",
1330		.category = FIO_OPT_G_RAND,
1331	},
1332	{
1333		.name	= "nrfiles",
1334		.alias	= "nr_files",
1335		.type	= FIO_OPT_INT,
1336		.off1	= td_var_offset(nr_files),
1337		.help	= "Split job workload between this number of files",
1338		.def	= "1",
1339		.category = FIO_OPT_G_FILE,
1340	},
1341	{
1342		.name	= "openfiles",
1343		.type	= FIO_OPT_INT,
1344		.off1	= td_var_offset(open_files),
1345		.help	= "Number of files to keep open at the same time",
1346		.category = FIO_OPT_G_FILE,
1347	},
1348	{
1349		.name	= "file_service_type",
1350		.type	= FIO_OPT_STR,
1351		.cb	= str_fst_cb,
1352		.off1	= td_var_offset(file_service_type),
1353		.help	= "How to select which file to service next",
1354		.def	= "roundrobin",
1355		.category = FIO_OPT_G_FILE,
1356		.posval	= {
1357			  { .ival = "random",
1358			    .oval = FIO_FSERVICE_RANDOM,
1359			    .help = "Choose a file at random",
1360			  },
1361			  { .ival = "roundrobin",
1362			    .oval = FIO_FSERVICE_RR,
1363			    .help = "Round robin select files",
1364			  },
1365			  { .ival = "sequential",
1366			    .oval = FIO_FSERVICE_SEQ,
1367			    .help = "Finish one file before moving to the next",
1368			  },
1369		},
1370		.parent = "nrfiles",
1371	},
1372#ifdef FIO_HAVE_FALLOCATE
1373	{
1374		.name	= "fallocate",
1375		.type	= FIO_OPT_STR,
1376		.off1	= td_var_offset(fallocate_mode),
1377		.help	= "Whether pre-allocation is performed when laying out files",
1378		.def	= "posix",
1379		.category = FIO_OPT_G_FILE,
1380		.posval	= {
1381			  { .ival = "none",
1382			    .oval = FIO_FALLOCATE_NONE,
1383			    .help = "Do not pre-allocate space",
1384			  },
1385			  { .ival = "posix",
1386			    .oval = FIO_FALLOCATE_POSIX,
1387			    .help = "Use posix_fallocate()",
1388			  },
1389#ifdef FIO_HAVE_LINUX_FALLOCATE
1390			  { .ival = "keep",
1391			    .oval = FIO_FALLOCATE_KEEP_SIZE,
1392			    .help = "Use fallocate(..., FALLOC_FL_KEEP_SIZE, ...)",
1393			  },
1394#endif
1395			  /* Compatibility with former boolean values */
1396			  { .ival = "0",
1397			    .oval = FIO_FALLOCATE_NONE,
1398			    .help = "Alias for 'none'",
1399			  },
1400			  { .ival = "1",
1401			    .oval = FIO_FALLOCATE_POSIX,
1402			    .help = "Alias for 'posix'",
1403			  },
1404		},
1405	},
1406#endif	/* FIO_HAVE_FALLOCATE */
1407	{
1408		.name	= "fadvise_hint",
1409		.type	= FIO_OPT_BOOL,
1410		.off1	= td_var_offset(fadvise_hint),
1411		.help	= "Use fadvise() to advise the kernel on IO pattern",
1412		.def	= "1",
1413		.category = FIO_OPT_G_FILE,
1414	},
1415	{
1416		.name	= "fsync",
1417		.type	= FIO_OPT_INT,
1418		.off1	= td_var_offset(fsync_blocks),
1419		.help	= "Issue fsync for writes every given number of blocks",
1420		.def	= "0",
1421		.category = FIO_OPT_G_FILE,
1422	},
1423	{
1424		.name	= "fdatasync",
1425		.type	= FIO_OPT_INT,
1426		.off1	= td_var_offset(fdatasync_blocks),
1427		.help	= "Issue fdatasync for writes every given number of blocks",
1428		.def	= "0",
1429		.category = FIO_OPT_G_FILE,
1430	},
1431	{
1432		.name	= "write_barrier",
1433		.type	= FIO_OPT_INT,
1434		.off1	= td_var_offset(barrier_blocks),
1435		.help	= "Make every Nth write a barrier write",
1436		.def	= "0",
1437		.category = FIO_OPT_G_IO,
1438	},
1439#ifdef FIO_HAVE_SYNC_FILE_RANGE
1440	{
1441		.name	= "sync_file_range",
1442		.posval	= {
1443			  { .ival = "wait_before",
1444			    .oval = SYNC_FILE_RANGE_WAIT_BEFORE,
1445			    .help = "SYNC_FILE_RANGE_WAIT_BEFORE",
1446			    .or	  = 1,
1447			  },
1448			  { .ival = "write",
1449			    .oval = SYNC_FILE_RANGE_WRITE,
1450			    .help = "SYNC_FILE_RANGE_WRITE",
1451			    .or	  = 1,
1452			  },
1453			  {
1454			    .ival = "wait_after",
1455			    .oval = SYNC_FILE_RANGE_WAIT_AFTER,
1456			    .help = "SYNC_FILE_RANGE_WAIT_AFTER",
1457			    .or	  = 1,
1458			  },
1459		},
1460		.type	= FIO_OPT_STR_MULTI,
1461		.cb	= str_sfr_cb,
1462		.off1	= td_var_offset(sync_file_range),
1463		.help	= "Use sync_file_range()",
1464		.category = FIO_OPT_G_FILE,
1465	},
1466#endif
1467	{
1468		.name	= "direct",
1469		.type	= FIO_OPT_BOOL,
1470		.off1	= td_var_offset(odirect),
1471		.help	= "Use O_DIRECT IO (negates buffered)",
1472		.def	= "0",
1473		.category = FIO_OPT_G_IO,
1474	},
1475	{
1476		.name	= "buffered",
1477		.type	= FIO_OPT_BOOL,
1478		.off1	= td_var_offset(odirect),
1479		.neg	= 1,
1480		.help	= "Use buffered IO (negates direct)",
1481		.def	= "1",
1482		.category = FIO_OPT_G_IO,
1483	},
1484	{
1485		.name	= "overwrite",
1486		.type	= FIO_OPT_BOOL,
1487		.off1	= td_var_offset(overwrite),
1488		.help	= "When writing, set whether to overwrite current data",
1489		.def	= "0",
1490		.category = FIO_OPT_G_IO | FIO_OPT_G_FILE,
1491	},
1492	{
1493		.name	= "loops",
1494		.type	= FIO_OPT_INT,
1495		.off1	= td_var_offset(loops),
1496		.help	= "Number of times to run the job",
1497		.def	= "1",
1498		.category = FIO_OPT_G_MISC,
1499	},
1500	{
1501		.name	= "numjobs",
1502		.type	= FIO_OPT_INT,
1503		.off1	= td_var_offset(numjobs),
1504		.help	= "Duplicate this job this many times",
1505		.def	= "1",
1506		.category = FIO_OPT_G_MISC,
1507	},
1508	{
1509		.name	= "startdelay",
1510		.type	= FIO_OPT_STR_VAL_TIME,
1511		.off1	= td_var_offset(start_delay),
1512		.help	= "Only start job when this period has passed",
1513		.def	= "0",
1514		.category = FIO_OPT_G_MISC,
1515	},
1516	{
1517		.name	= "runtime",
1518		.alias	= "timeout",
1519		.type	= FIO_OPT_STR_VAL_TIME,
1520		.off1	= td_var_offset(timeout),
1521		.help	= "Stop workload when this amount of time has passed",
1522		.def	= "0",
1523		.category = FIO_OPT_G_MISC,
1524	},
1525	{
1526		.name	= "time_based",
1527		.type	= FIO_OPT_STR_SET,
1528		.off1	= td_var_offset(time_based),
1529		.help	= "Keep running until runtime/timeout is met",
1530		.category = FIO_OPT_G_MISC,
1531	},
1532	{
1533		.name	= "ramp_time",
1534		.type	= FIO_OPT_STR_VAL_TIME,
1535		.off1	= td_var_offset(ramp_time),
1536		.help	= "Ramp up time before measuring performance",
1537		.category = FIO_OPT_G_MISC,
1538	},
1539	{
1540		.name	= "clocksource",
1541		.type	= FIO_OPT_STR,
1542		.cb	= fio_clock_source_cb,
1543		.off1	= td_var_offset(clocksource),
1544		.help	= "What type of timing source to use",
1545		.category = FIO_OPT_G_OS,
1546		.posval	= {
1547			  { .ival = "gettimeofday",
1548			    .oval = CS_GTOD,
1549			    .help = "Use gettimeofday(2) for timing",
1550			  },
1551			  { .ival = "clock_gettime",
1552			    .oval = CS_CGETTIME,
1553			    .help = "Use clock_gettime(2) for timing",
1554			  },
1555#ifdef ARCH_HAVE_CPU_CLOCK
1556			  { .ival = "cpu",
1557			    .oval = CS_CPUCLOCK,
1558			    .help = "Use CPU private clock",
1559			  },
1560#endif
1561		},
1562	},
1563	{
1564		.name	= "mem",
1565		.alias	= "iomem",
1566		.type	= FIO_OPT_STR,
1567		.cb	= str_mem_cb,
1568		.off1	= td_var_offset(mem_type),
1569		.help	= "Backing type for IO buffers",
1570		.def	= "malloc",
1571		.category = FIO_OPT_G_IO_BUF | FIO_OPT_G_MEM,
1572		.posval	= {
1573			  { .ival = "malloc",
1574			    .oval = MEM_MALLOC,
1575			    .help = "Use malloc(3) for IO buffers",
1576			  },
1577			  { .ival = "shm",
1578			    .oval = MEM_SHM,
1579			    .help = "Use shared memory segments for IO buffers",
1580			  },
1581#ifdef FIO_HAVE_HUGETLB
1582			  { .ival = "shmhuge",
1583			    .oval = MEM_SHMHUGE,
1584			    .help = "Like shm, but use huge pages",
1585			  },
1586#endif
1587			  { .ival = "mmap",
1588			    .oval = MEM_MMAP,
1589			    .help = "Use mmap(2) (file or anon) for IO buffers",
1590			  },
1591#ifdef FIO_HAVE_HUGETLB
1592			  { .ival = "mmaphuge",
1593			    .oval = MEM_MMAPHUGE,
1594			    .help = "Like mmap, but use huge pages",
1595			  },
1596#endif
1597		  },
1598	},
1599	{
1600		.name	= "iomem_align",
1601		.alias	= "mem_align",
1602		.type	= FIO_OPT_INT,
1603		.off1	= td_var_offset(mem_align),
1604		.minval	= 0,
1605		.help	= "IO memory buffer offset alignment",
1606		.def	= "0",
1607		.parent	= "iomem",
1608		.category = FIO_OPT_G_IO_BUF | FIO_OPT_G_MEM,
1609	},
1610	{
1611		.name	= "verify",
1612		.type	= FIO_OPT_STR,
1613		.off1	= td_var_offset(verify),
1614		.help	= "Verify data written",
1615		.cb	= str_verify_cb,
1616		.def	= "0",
1617		.category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY,
1618		.posval = {
1619			  { .ival = "0",
1620			    .oval = VERIFY_NONE,
1621			    .help = "Don't do IO verification",
1622			  },
1623			  { .ival = "md5",
1624			    .oval = VERIFY_MD5,
1625			    .help = "Use md5 checksums for verification",
1626			  },
1627			  { .ival = "crc64",
1628			    .oval = VERIFY_CRC64,
1629			    .help = "Use crc64 checksums for verification",
1630			  },
1631			  { .ival = "crc32",
1632			    .oval = VERIFY_CRC32,
1633			    .help = "Use crc32 checksums for verification",
1634			  },
1635			  { .ival = "crc32c-intel",
1636			    .oval = VERIFY_CRC32C,
1637			    .help = "Use crc32c checksums for verification (hw assisted, if available)",
1638			  },
1639			  { .ival = "crc32c",
1640			    .oval = VERIFY_CRC32C,
1641			    .help = "Use crc32c checksums for verification (hw assisted, if available)",
1642			  },
1643			  { .ival = "crc16",
1644			    .oval = VERIFY_CRC16,
1645			    .help = "Use crc16 checksums for verification",
1646			  },
1647			  { .ival = "crc7",
1648			    .oval = VERIFY_CRC7,
1649			    .help = "Use crc7 checksums for verification",
1650			  },
1651			  { .ival = "sha1",
1652			    .oval = VERIFY_SHA1,
1653			    .help = "Use sha1 checksums for verification",
1654			  },
1655			  { .ival = "sha256",
1656			    .oval = VERIFY_SHA256,
1657			    .help = "Use sha256 checksums for verification",
1658			  },
1659			  { .ival = "sha512",
1660			    .oval = VERIFY_SHA512,
1661			    .help = "Use sha512 checksums for verification",
1662			  },
1663			  { .ival = "meta",
1664			    .oval = VERIFY_META,
1665			    .help = "Use io information",
1666			  },
1667			  {
1668			    .ival = "null",
1669			    .oval = VERIFY_NULL,
1670			    .help = "Pretend to verify",
1671			  },
1672		},
1673	},
1674	{
1675		.name	= "do_verify",
1676		.type	= FIO_OPT_BOOL,
1677		.off1	= td_var_offset(do_verify),
1678		.help	= "Run verification stage after write",
1679		.def	= "1",
1680		.parent = "verify",
1681		.category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY,
1682	},
1683	{
1684		.name	= "verifysort",
1685		.type	= FIO_OPT_BOOL,
1686		.off1	= td_var_offset(verifysort),
1687		.help	= "Sort written verify blocks for read back",
1688		.def	= "1",
1689		.parent = "verify",
1690		.category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY,
1691	},
1692	{
1693		.name   = "verify_interval",
1694		.type   = FIO_OPT_INT,
1695		.off1   = td_var_offset(verify_interval),
1696		.minval	= 2 * sizeof(struct verify_header),
1697		.help   = "Store verify buffer header every N bytes",
1698		.parent	= "verify",
1699		.category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY,
1700	},
1701	{
1702		.name	= "verify_offset",
1703		.type	= FIO_OPT_INT,
1704		.help	= "Offset verify header location by N bytes",
1705		.def	= "0",
1706		.cb	= str_verify_offset_cb,
1707		.parent	= "verify",
1708		.category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY,
1709	},
1710	{
1711		.name	= "verify_pattern",
1712		.type	= FIO_OPT_STR,
1713		.cb	= str_verify_pattern_cb,
1714		.help	= "Fill pattern for IO buffers",
1715		.parent	= "verify",
1716		.category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY,
1717	},
1718	{
1719		.name	= "verify_fatal",
1720		.type	= FIO_OPT_BOOL,
1721		.off1	= td_var_offset(verify_fatal),
1722		.def	= "0",
1723		.help	= "Exit on a single verify failure, don't continue",
1724		.parent = "verify",
1725		.category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY | FIO_OPT_G_ERR,
1726	},
1727	{
1728		.name	= "verify_dump",
1729		.type	= FIO_OPT_BOOL,
1730		.off1	= td_var_offset(verify_dump),
1731		.def	= "0",
1732		.help	= "Dump contents of good and bad blocks on failure",
1733		.parent = "verify",
1734		.category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY | FIO_OPT_G_ERR,
1735	},
1736	{
1737		.name	= "verify_async",
1738		.type	= FIO_OPT_INT,
1739		.off1	= td_var_offset(verify_async),
1740		.def	= "0",
1741		.help	= "Number of async verifier threads to use",
1742		.parent	= "verify",
1743		.category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY,
1744	},
1745	{
1746		.name	= "verify_backlog",
1747		.type	= FIO_OPT_STR_VAL,
1748		.off1	= td_var_offset(verify_backlog),
1749		.help	= "Verify after this number of blocks are written",
1750		.parent	= "verify",
1751		.category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY,
1752	},
1753	{
1754		.name	= "verify_backlog_batch",
1755		.type	= FIO_OPT_INT,
1756		.off1	= td_var_offset(verify_batch),
1757		.help	= "Verify this number of IO blocks",
1758		.parent	= "verify",
1759		.category = FIO_OPT_G_IO | FIO_OPT_G_VERIFY,
1760	},
1761#ifdef FIO_HAVE_CPU_AFFINITY
1762	{
1763		.name	= "verify_async_cpus",
1764		.type	= FIO_OPT_STR,
1765		.cb	= str_verify_cpus_allowed_cb,
1766		.help	= "Set CPUs allowed for async verify threads",
1767		.parent	= "verify_async",
1768		.category = FIO_OPT_G_OS | FIO_OPT_G_CPU | FIO_OPT_G_VERIFY,
1769	},
1770#endif
1771#ifdef FIO_HAVE_TRIM
1772	{
1773		.name	= "trim_percentage",
1774		.type	= FIO_OPT_INT,
1775		.cb	= str_verify_trim_cb,
1776		.maxval = 100,
1777		.help	= "Number of verify blocks to discard/trim",
1778		.parent	= "verify",
1779		.def	= "0",
1780		.category = FIO_OPT_G_IO,
1781	},
1782	{
1783		.name	= "trim_verify_zero",
1784		.type	= FIO_OPT_INT,
1785		.help	= "Verify that trim/discarded blocks are returned as zeroes",
1786		.off1	= td_var_offset(trim_zero),
1787		.parent	= "trim_percentage",
1788		.def	= "1",
1789		.category = FIO_OPT_G_IO,
1790	},
1791	{
1792		.name	= "trim_backlog",
1793		.type	= FIO_OPT_STR_VAL,
1794		.off1	= td_var_offset(trim_backlog),
1795		.help	= "Trim after this number of blocks are written",
1796		.parent	= "trim_percentage",
1797		.category = FIO_OPT_G_IO,
1798	},
1799	{
1800		.name	= "trim_backlog_batch",
1801		.type	= FIO_OPT_INT,
1802		.off1	= td_var_offset(trim_batch),
1803		.help	= "Trim this number of IO blocks",
1804		.parent	= "trim_percentage",
1805		.category = FIO_OPT_G_IO,
1806	},
1807#endif
1808	{
1809		.name	= "write_iolog",
1810		.type	= FIO_OPT_STR_STORE,
1811		.off1	= td_var_offset(write_iolog_file),
1812		.help	= "Store IO pattern to file",
1813		.category = FIO_OPT_G_IO | FIO_OPT_G_LOG,
1814	},
1815	{
1816		.name	= "read_iolog",
1817		.type	= FIO_OPT_STR_STORE,
1818		.off1	= td_var_offset(read_iolog_file),
1819		.help	= "Playback IO pattern from file",
1820		.category = FIO_OPT_G_IO | FIO_OPT_G_LOG,
1821	},
1822	{
1823		.name	= "replay_no_stall",
1824		.type	= FIO_OPT_INT,
1825		.off1	= td_var_offset(no_stall),
1826		.def	= "0",
1827		.parent	= "read_iolog",
1828		.help	= "Playback IO pattern file as fast as possible without stalls",
1829		.category = FIO_OPT_G_IO | FIO_OPT_G_LOG,
1830	},
1831	{
1832		.name	= "replay_redirect",
1833		.type	= FIO_OPT_STR_STORE,
1834		.off1	= td_var_offset(replay_redirect),
1835		.parent	= "read_iolog",
1836		.help	= "Replay all I/O onto this device, regardless of trace device",
1837		.category = FIO_OPT_G_IO | FIO_OPT_G_LOG,
1838	},
1839	{
1840		.name	= "exec_prerun",
1841		.type	= FIO_OPT_STR_STORE,
1842		.off1	= td_var_offset(exec_prerun),
1843		.help	= "Execute this file prior to running job",
1844		.category = FIO_OPT_G_MISC | FIO_OPT_G_OS,
1845	},
1846	{
1847		.name	= "exec_postrun",
1848		.type	= FIO_OPT_STR_STORE,
1849		.off1	= td_var_offset(exec_postrun),
1850		.help	= "Execute this file after running job",
1851		.category = FIO_OPT_G_MISC | FIO_OPT_G_OS,
1852	},
1853#ifdef FIO_HAVE_IOSCHED_SWITCH
1854	{
1855		.name	= "ioscheduler",
1856		.type	= FIO_OPT_STR_STORE,
1857		.off1	= td_var_offset(ioscheduler),
1858		.help	= "Use this IO scheduler on the backing device",
1859		.category = FIO_OPT_G_OS | FIO_OPT_G_IO,
1860	},
1861#endif
1862	{
1863		.name	= "zonesize",
1864		.type	= FIO_OPT_STR_VAL,
1865		.off1	= td_var_offset(zone_size),
1866		.help	= "Amount of data to read per zone",
1867		.def	= "0",
1868		.category = FIO_OPT_G_IO | FIO_OPT_G_ZONE,
1869	},
1870	{
1871		.name	= "zonerange",
1872		.type	= FIO_OPT_STR_VAL,
1873		.off1	= td_var_offset(zone_range),
1874		.help	= "Give size of an IO zone",
1875		.def	= "0",
1876		.category = FIO_OPT_G_IO | FIO_OPT_G_ZONE,
1877	},
1878	{
1879		.name	= "zoneskip",
1880		.type	= FIO_OPT_STR_VAL,
1881		.off1	= td_var_offset(zone_skip),
1882		.help	= "Space between IO zones",
1883		.def	= "0",
1884		.category = FIO_OPT_G_IO | FIO_OPT_G_ZONE,
1885	},
1886	{
1887		.name	= "lockmem",
1888		.type	= FIO_OPT_STR_VAL,
1889		.cb	= str_lockmem_cb,
1890		.help	= "Lock down this amount of memory",
1891		.def	= "0",
1892		.category = FIO_OPT_G_OS | FIO_OPT_G_MEM,
1893	},
1894	{
1895		.name	= "rwmixread",
1896		.type	= FIO_OPT_INT,
1897		.cb	= str_rwmix_read_cb,
1898		.maxval	= 100,
1899		.help	= "Percentage of mixed workload that is reads",
1900		.def	= "50",
1901		.category = FIO_OPT_G_IO,
1902	},
1903	{
1904		.name	= "rwmixwrite",
1905		.type	= FIO_OPT_INT,
1906		.cb	= str_rwmix_write_cb,
1907		.maxval	= 100,
1908		.help	= "Percentage of mixed workload that is writes",
1909		.def	= "50",
1910		.category = FIO_OPT_G_IO,
1911	},
1912	{
1913		.name	= "rwmixcycle",
1914		.type	= FIO_OPT_DEPRECATED,
1915		.category = FIO_OPT_G_IO,
1916	},
1917	{
1918		.name	= "nice",
1919		.type	= FIO_OPT_INT,
1920		.off1	= td_var_offset(nice),
1921		.help	= "Set job CPU nice value",
1922		.minval	= -19,
1923		.maxval	= 20,
1924		.def	= "0",
1925		.category = FIO_OPT_G_OS | FIO_OPT_G_CPU,
1926	},
1927#ifdef FIO_HAVE_IOPRIO
1928	{
1929		.name	= "prio",
1930		.type	= FIO_OPT_INT,
1931		.cb	= str_prio_cb,
1932		.help	= "Set job IO priority value",
1933		.minval	= 0,
1934		.maxval	= 7,
1935		.category = FIO_OPT_G_OS | FIO_OPT_G_CPU,
1936	},
1937	{
1938		.name	= "prioclass",
1939		.type	= FIO_OPT_INT,
1940		.cb	= str_prioclass_cb,
1941		.help	= "Set job IO priority class",
1942		.minval	= 0,
1943		.maxval	= 3,
1944		.category = FIO_OPT_G_OS | FIO_OPT_G_CPU,
1945	},
1946#endif
1947	{
1948		.name	= "thinktime",
1949		.type	= FIO_OPT_INT,
1950		.off1	= td_var_offset(thinktime),
1951		.help	= "Idle time between IO buffers (usec)",
1952		.def	= "0",
1953		.category = FIO_OPT_G_MISC,
1954	},
1955	{
1956		.name	= "thinktime_spin",
1957		.type	= FIO_OPT_INT,
1958		.off1	= td_var_offset(thinktime_spin),
1959		.help	= "Start think time by spinning this amount (usec)",
1960		.def	= "0",
1961		.parent	= "thinktime",
1962		.category = FIO_OPT_G_MISC,
1963	},
1964	{
1965		.name	= "thinktime_blocks",
1966		.type	= FIO_OPT_INT,
1967		.off1	= td_var_offset(thinktime_blocks),
1968		.help	= "IO buffer period between 'thinktime'",
1969		.def	= "1",
1970		.parent	= "thinktime",
1971		.category = FIO_OPT_G_MISC,
1972	},
1973	{
1974		.name	= "rate",
1975		.type	= FIO_OPT_INT,
1976		.off1	= td_var_offset(rate[0]),
1977		.off2	= td_var_offset(rate[1]),
1978		.help	= "Set bandwidth rate",
1979		.category = FIO_OPT_G_IO,
1980	},
1981	{
1982		.name	= "ratemin",
1983		.type	= FIO_OPT_INT,
1984		.off1	= td_var_offset(ratemin[0]),
1985		.off2	= td_var_offset(ratemin[1]),
1986		.help	= "Job must meet this rate or it will be shutdown",
1987		.parent	= "rate",
1988		.category = FIO_OPT_G_IO,
1989	},
1990	{
1991		.name	= "rate_iops",
1992		.type	= FIO_OPT_INT,
1993		.off1	= td_var_offset(rate_iops[0]),
1994		.off2	= td_var_offset(rate_iops[1]),
1995		.help	= "Limit IO used to this number of IO operations/sec",
1996		.category = FIO_OPT_G_IO,
1997	},
1998	{
1999		.name	= "rate_iops_min",
2000		.type	= FIO_OPT_INT,
2001		.off1	= td_var_offset(rate_iops_min[0]),
2002		.off2	= td_var_offset(rate_iops_min[1]),
2003		.help	= "Job must meet this rate or it will be shut down",
2004		.parent	= "rate_iops",
2005		.category = FIO_OPT_G_IO,
2006	},
2007	{
2008		.name	= "ratecycle",
2009		.type	= FIO_OPT_INT,
2010		.off1	= td_var_offset(ratecycle),
2011		.help	= "Window average for rate limits (msec)",
2012		.def	= "1000",
2013		.parent = "rate",
2014		.category = FIO_OPT_G_IO,
2015	},
2016	{
2017		.name	= "invalidate",
2018		.type	= FIO_OPT_BOOL,
2019		.off1	= td_var_offset(invalidate_cache),
2020		.help	= "Invalidate buffer/page cache prior to running job",
2021		.def	= "1",
2022		.category = FIO_OPT_G_IO | FIO_OPT_G_CACHE,
2023	},
2024	{
2025		.name	= "sync",
2026		.type	= FIO_OPT_BOOL,
2027		.off1	= td_var_offset(sync_io),
2028		.help	= "Use O_SYNC for buffered writes",
2029		.def	= "0",
2030		.parent = "buffered",
2031		.category = FIO_OPT_G_IO | FIO_OPT_G_FILE,
2032	},
2033	{
2034		.name	= "bwavgtime",
2035		.type	= FIO_OPT_INT,
2036		.off1	= td_var_offset(bw_avg_time),
2037		.help	= "Time window over which to calculate bandwidth"
2038			  " (msec)",
2039		.def	= "500",
2040		.parent	= "write_bw_log",
2041		.category = FIO_OPT_G_LOG | FIO_OPT_G_STAT,
2042	},
2043	{
2044		.name	= "iopsavgtime",
2045		.type	= FIO_OPT_INT,
2046		.off1	= td_var_offset(iops_avg_time),
2047		.help	= "Time window over which to calculate IOPS (msec)",
2048		.def	= "500",
2049		.parent	= "write_iops_log",
2050		.category = FIO_OPT_G_LOG | FIO_OPT_G_STAT,
2051	},
2052	{
2053		.name	= "create_serialize",
2054		.type	= FIO_OPT_BOOL,
2055		.off1	= td_var_offset(create_serialize),
2056		.help	= "Serialize creating of job files",
2057		.def	= "1",
2058		.category = FIO_OPT_G_FILE,
2059	},
2060	{
2061		.name	= "create_fsync",
2062		.type	= FIO_OPT_BOOL,
2063		.off1	= td_var_offset(create_fsync),
2064		.help	= "fsync file after creation",
2065		.def	= "1",
2066		.category = FIO_OPT_G_FILE,
2067	},
2068	{
2069		.name	= "create_on_open",
2070		.type	= FIO_OPT_BOOL,
2071		.off1	= td_var_offset(create_on_open),
2072		.help	= "Create files when they are opened for IO",
2073		.def	= "0",
2074		.category = FIO_OPT_G_FILE,
2075	},
2076	{
2077		.name	= "pre_read",
2078		.type	= FIO_OPT_BOOL,
2079		.off1	= td_var_offset(pre_read),
2080		.help	= "Pre-read files before starting official testing",
2081		.def	= "0",
2082		.category = FIO_OPT_G_FILE | FIO_OPT_G_CACHE,
2083	},
2084	{
2085		.name	= "cpuload",
2086		.type	= FIO_OPT_INT,
2087		.off1	= td_var_offset(cpuload),
2088		.help	= "Use this percentage of CPU",
2089		.category = FIO_OPT_G_CPU,
2090	},
2091	{
2092		.name	= "cpuchunks",
2093		.type	= FIO_OPT_INT,
2094		.off1	= td_var_offset(cpucycle),
2095		.help	= "Length of the CPU burn cycles (usecs)",
2096		.def	= "50000",
2097		.parent = "cpuload",
2098		.category = FIO_OPT_G_CPU,
2099	},
2100#ifdef FIO_HAVE_CPU_AFFINITY
2101	{
2102		.name	= "cpumask",
2103		.type	= FIO_OPT_INT,
2104		.cb	= str_cpumask_cb,
2105		.help	= "CPU affinity mask",
2106		.category = FIO_OPT_G_CPU | FIO_OPT_G_OS,
2107	},
2108	{
2109		.name	= "cpus_allowed",
2110		.type	= FIO_OPT_STR,
2111		.cb	= str_cpus_allowed_cb,
2112		.help	= "Set CPUs allowed",
2113		.category = FIO_OPT_G_CPU | FIO_OPT_G_OS,
2114	},
2115#endif
2116	{
2117		.name	= "end_fsync",
2118		.type	= FIO_OPT_BOOL,
2119		.off1	= td_var_offset(end_fsync),
2120		.help	= "Include fsync at the end of job",
2121		.def	= "0",
2122		.category = FIO_OPT_G_FILE,
2123	},
2124	{
2125		.name	= "fsync_on_close",
2126		.type	= FIO_OPT_BOOL,
2127		.off1	= td_var_offset(fsync_on_close),
2128		.help	= "fsync files on close",
2129		.def	= "0",
2130		.category = FIO_OPT_G_FILE,
2131	},
2132	{
2133		.name	= "unlink",
2134		.type	= FIO_OPT_BOOL,
2135		.off1	= td_var_offset(unlink),
2136		.help	= "Unlink created files after job has completed",
2137		.def	= "0",
2138		.category = FIO_OPT_G_FILE,
2139	},
2140	{
2141		.name	= "exitall",
2142		.type	= FIO_OPT_STR_SET,
2143		.cb	= str_exitall_cb,
2144		.help	= "Terminate all jobs when one exits",
2145		.category = FIO_OPT_G_MISC | FIO_OPT_G_JOB,
2146	},
2147	{
2148		.name	= "stonewall",
2149		.alias	= "wait_for_previous",
2150		.type	= FIO_OPT_STR_SET,
2151		.off1	= td_var_offset(stonewall),
2152		.help	= "Insert a hard barrier between this job and previous",
2153		.category = FIO_OPT_G_MISC | FIO_OPT_G_JOB,
2154	},
2155	{
2156		.name	= "new_group",
2157		.type	= FIO_OPT_STR_SET,
2158		.off1	= td_var_offset(new_group),
2159		.help	= "Mark the start of a new group (for reporting)",
2160		.category = FIO_OPT_G_MISC | FIO_OPT_G_JOB,
2161	},
2162	{
2163		.name	= "thread",
2164		.type	= FIO_OPT_STR_SET,
2165		.off1	= td_var_offset(use_thread),
2166		.help	= "Use threads instead of forks",
2167		.category = FIO_OPT_G_MISC | FIO_OPT_G_OS | FIO_OPT_G_JOB,
2168	},
2169	{
2170		.name	= "write_bw_log",
2171		.type	= FIO_OPT_STR,
2172		.off1	= td_var_offset(write_bw_log),
2173		.cb	= str_write_bw_log_cb,
2174		.help	= "Write log of bandwidth during run",
2175		.category = FIO_OPT_G_LOG,
2176	},
2177	{
2178		.name	= "write_lat_log",
2179		.type	= FIO_OPT_STR,
2180		.off1	= td_var_offset(write_lat_log),
2181		.cb	= str_write_lat_log_cb,
2182		.help	= "Write log of latency during run",
2183		.category = FIO_OPT_G_LOG,
2184	},
2185	{
2186		.name	= "write_iops_log",
2187		.type	= FIO_OPT_STR,
2188		.off1	= td_var_offset(write_iops_log),
2189		.cb	= str_write_iops_log_cb,
2190		.help	= "Write log of IOPS during run",
2191		.category = FIO_OPT_G_LOG,
2192	},
2193	{
2194		.name	= "log_avg_msec",
2195		.type	= FIO_OPT_INT,
2196		.off1	= td_var_offset(log_avg_msec),
2197		.help	= "Average bw/iops/lat logs over this period of time",
2198		.def	= "0",
2199		.category = FIO_OPT_G_LOG,
2200	},
2201	{
2202		.name	= "hugepage-size",
2203		.type	= FIO_OPT_INT,
2204		.off1	= td_var_offset(hugepage_size),
2205		.help	= "When using hugepages, specify size of each page",
2206		.def	= __fio_stringify(FIO_HUGE_PAGE),
2207		.category = FIO_OPT_G_OS | FIO_OPT_G_MEM,
2208	},
2209	{
2210		.name	= "group_reporting",
2211		.type	= FIO_OPT_STR_SET,
2212		.off1	= td_var_offset(group_reporting),
2213		.help	= "Do reporting on a per-group basis",
2214		.category = FIO_OPT_G_MISC,
2215	},
2216	{
2217		.name	= "zero_buffers",
2218		.type	= FIO_OPT_STR_SET,
2219		.off1	= td_var_offset(zero_buffers),
2220		.help	= "Init IO buffers to all zeroes",
2221		.category = FIO_OPT_G_IO_BUF,
2222	},
2223	{
2224		.name	= "refill_buffers",
2225		.type	= FIO_OPT_STR_SET,
2226		.off1	= td_var_offset(refill_buffers),
2227		.help	= "Refill IO buffers on every IO submit",
2228		.category = FIO_OPT_G_IO_BUF,
2229	},
2230	{
2231		.name	= "scramble_buffers",
2232		.type	= FIO_OPT_BOOL,
2233		.off1	= td_var_offset(scramble_buffers),
2234		.help	= "Slightly scramble buffers on every IO submit",
2235		.def	= "1",
2236		.category = FIO_OPT_G_IO_BUF,
2237	},
2238	{
2239		.name	= "buffer_compress_percentage",
2240		.type	= FIO_OPT_INT,
2241		.off1	= td_var_offset(compress_percentage),
2242		.maxval	= 100,
2243		.minval	= 1,
2244		.help	= "How compressible the buffer is (approximately)",
2245		.category = FIO_OPT_G_IO_BUF,
2246	},
2247	{
2248		.name	= "buffer_compress_chunk",
2249		.type	= FIO_OPT_INT,
2250		.off1	= td_var_offset(compress_chunk),
2251		.parent	= "buffer_compress_percentage",
2252		.help	= "Size of compressible region in buffer",
2253		.category = FIO_OPT_G_IO_BUF,
2254	},
2255	{
2256		.name	= "clat_percentiles",
2257		.type	= FIO_OPT_BOOL,
2258		.off1	= td_var_offset(clat_percentiles),
2259		.help	= "Enable the reporting of completion latency percentiles",
2260		.def	= "1",
2261		.category = FIO_OPT_G_STAT,
2262	},
2263	{
2264		.name	= "percentile_list",
2265		.type	= FIO_OPT_FLOAT_LIST,
2266		.off1	= td_var_offset(percentile_list),
2267		.off2   = td_var_offset(overwrite_plist),
2268		.help	= "Specify a custom list of percentiles to report",
2269		.maxlen	= FIO_IO_U_LIST_MAX_LEN,
2270		.minfp	= 0.0,
2271		.maxfp	= 100.0,
2272		.category = FIO_OPT_G_STAT,
2273	},
2274
2275#ifdef FIO_HAVE_DISK_UTIL
2276	{
2277		.name	= "disk_util",
2278		.type	= FIO_OPT_BOOL,
2279		.off1	= td_var_offset(do_disk_util),
2280		.help	= "Log disk utilization statistics",
2281		.def	= "1",
2282		.category = FIO_OPT_G_OS | FIO_OPT_G_STAT,
2283	},
2284#endif
2285	{
2286		.name	= "gtod_reduce",
2287		.type	= FIO_OPT_BOOL,
2288		.help	= "Greatly reduce number of gettimeofday() calls",
2289		.cb	= str_gtod_reduce_cb,
2290		.def	= "0",
2291		.category = FIO_OPT_G_OS | FIO_OPT_G_MISC | FIO_OPT_G_STAT,
2292	},
2293	{
2294		.name	= "disable_lat",
2295		.type	= FIO_OPT_BOOL,
2296		.off1	= td_var_offset(disable_lat),
2297		.help	= "Disable latency numbers",
2298		.parent	= "gtod_reduce",
2299		.def	= "0",
2300		.category = FIO_OPT_G_OS | FIO_OPT_G_MISC | FIO_OPT_G_STAT,
2301	},
2302	{
2303		.name	= "disable_clat",
2304		.type	= FIO_OPT_BOOL,
2305		.off1	= td_var_offset(disable_clat),
2306		.help	= "Disable completion latency numbers",
2307		.parent	= "gtod_reduce",
2308		.def	= "0",
2309		.category = FIO_OPT_G_OS | FIO_OPT_G_MISC | FIO_OPT_G_STAT,
2310	},
2311	{
2312		.name	= "disable_slat",
2313		.type	= FIO_OPT_BOOL,
2314		.off1	= td_var_offset(disable_slat),
2315		.help	= "Disable submission latency numbers",
2316		.parent	= "gtod_reduce",
2317		.def	= "0",
2318		.category = FIO_OPT_G_OS | FIO_OPT_G_MISC | FIO_OPT_G_STAT,
2319	},
2320	{
2321		.name	= "disable_bw_measurement",
2322		.type	= FIO_OPT_BOOL,
2323		.off1	= td_var_offset(disable_bw),
2324		.help	= "Disable bandwidth logging",
2325		.parent	= "gtod_reduce",
2326		.def	= "0",
2327		.category = FIO_OPT_G_OS | FIO_OPT_G_MISC | FIO_OPT_G_STAT,
2328	},
2329	{
2330		.name	= "gtod_cpu",
2331		.type	= FIO_OPT_INT,
2332		.cb	= str_gtod_cpu_cb,
2333		.help	= "Set up dedicated gettimeofday() thread on this CPU",
2334		.verify	= gtod_cpu_verify,
2335		.category = FIO_OPT_G_OS | FIO_OPT_G_MISC | FIO_OPT_G_STAT,
2336	},
2337	{
2338		.name	= "continue_on_error",
2339		.type	= FIO_OPT_STR,
2340		.off1	= td_var_offset(continue_on_error),
2341		.help	= "Continue on non-fatal errors during IO",
2342		.def	= "none",
2343		.category = FIO_OPT_G_MISC | FIO_OPT_G_ERR,
2344		.posval = {
2345			  { .ival = "none",
2346			    .oval = ERROR_TYPE_NONE,
2347			    .help = "Exit when an error is encountered",
2348			  },
2349			  { .ival = "read",
2350			    .oval = ERROR_TYPE_READ,
2351			    .help = "Continue on read errors only",
2352			  },
2353			  { .ival = "write",
2354			    .oval = ERROR_TYPE_WRITE,
2355			    .help = "Continue on write errors only",
2356			  },
2357			  { .ival = "io",
2358			    .oval = ERROR_TYPE_READ | ERROR_TYPE_WRITE,
2359			    .help = "Continue on any IO errors",
2360			  },
2361			  { .ival = "verify",
2362			    .oval = ERROR_TYPE_VERIFY,
2363			    .help = "Continue on verify errors only",
2364			  },
2365			  { .ival = "all",
2366			    .oval = ERROR_TYPE_ANY,
2367			    .help = "Continue on all io and verify errors",
2368			  },
2369			  { .ival = "0",
2370			    .oval = ERROR_TYPE_NONE,
2371			    .help = "Alias for 'none'",
2372			  },
2373			  { .ival = "1",
2374			    .oval = ERROR_TYPE_ANY,
2375			    .help = "Alias for 'all'",
2376			  },
2377		},
2378	},
2379	{
2380		.name	= "profile",
2381		.type	= FIO_OPT_STR_STORE,
2382		.off1	= td_var_offset(profile),
2383		.help	= "Select a specific builtin performance test",
2384		.category = FIO_OPT_G_MISC | FIO_OPT_G_JOB,
2385	},
2386	{
2387		.name	= "cgroup",
2388		.type	= FIO_OPT_STR_STORE,
2389		.off1	= td_var_offset(cgroup),
2390		.help	= "Add job to cgroup of this name",
2391		.category = FIO_OPT_G_MISC | FIO_OPT_G_OS,
2392	},
2393	{
2394		.name	= "cgroup_weight",
2395		.type	= FIO_OPT_INT,
2396		.off1	= td_var_offset(cgroup_weight),
2397		.help	= "Use given weight for cgroup",
2398		.minval = 100,
2399		.maxval	= 1000,
2400		.category = FIO_OPT_G_MISC | FIO_OPT_G_OS,
2401	},
2402	{
2403		.name	= "cgroup_nodelete",
2404		.type	= FIO_OPT_BOOL,
2405		.off1	= td_var_offset(cgroup_nodelete),
2406		.help	= "Do not delete cgroups after job completion",
2407		.def	= "0",
2408		.category = FIO_OPT_G_MISC | FIO_OPT_G_OS,
2409	},
2410	{
2411		.name	= "uid",
2412		.type	= FIO_OPT_INT,
2413		.off1	= td_var_offset(uid),
2414		.help	= "Run job with this user ID",
2415		.category = FIO_OPT_G_OS | FIO_OPT_G_JOB,
2416	},
2417	{
2418		.name	= "gid",
2419		.type	= FIO_OPT_INT,
2420		.off1	= td_var_offset(gid),
2421		.help	= "Run job with this group ID",
2422		.category = FIO_OPT_G_OS | FIO_OPT_G_JOB,
2423	},
2424	{
2425		.name	= "flow_id",
2426		.type	= FIO_OPT_INT,
2427		.off1	= td_var_offset(flow_id),
2428		.help	= "The flow index ID to use",
2429		.def	= "0",
2430		.category = FIO_OPT_G_IO,
2431	},
2432	{
2433		.name	= "flow",
2434		.type	= FIO_OPT_INT,
2435		.off1	= td_var_offset(flow),
2436		.help	= "Weight for flow control of this job",
2437		.parent	= "flow_id",
2438		.def	= "0",
2439		.category = FIO_OPT_G_IO,
2440	},
2441	{
2442		.name	= "flow_watermark",
2443		.type	= FIO_OPT_INT,
2444		.off1	= td_var_offset(flow_watermark),
2445		.help	= "High watermark for flow control. This option"
2446			" should be set to the same value for all threads"
2447			" with non-zero flow.",
2448		.parent	= "flow_id",
2449		.def	= "1024",
2450		.category = FIO_OPT_G_IO,
2451	},
2452	{
2453		.name	= "flow_sleep",
2454		.type	= FIO_OPT_INT,
2455		.off1	= td_var_offset(flow_sleep),
2456		.help	= "How many microseconds to sleep after being held"
2457			" back by the flow control mechanism",
2458		.parent	= "flow_id",
2459		.def	= "0",
2460		.category = FIO_OPT_G_IO,
2461	},
2462	{
2463		.name = NULL,
2464	},
2465};
2466
2467static void add_to_lopt(struct option *lopt, struct fio_option *o,
2468			const char *name, int val)
2469{
2470	lopt->name = (char *) name;
2471	lopt->val = val;
2472	if (o->type == FIO_OPT_STR_SET)
2473		lopt->has_arg = no_argument;
2474	else
2475		lopt->has_arg = required_argument;
2476}
2477
2478static void options_to_lopts(struct fio_option *opts,
2479			      struct option *long_options,
2480			      int i, int option_type)
2481{
2482	struct fio_option *o = &opts[0];
2483	while (o->name) {
2484		add_to_lopt(&long_options[i], o, o->name, option_type);
2485		if (o->alias) {
2486			i++;
2487			add_to_lopt(&long_options[i], o, o->alias, option_type);
2488		}
2489
2490		i++;
2491		o++;
2492		assert(i < FIO_NR_OPTIONS);
2493	}
2494}
2495
2496void fio_options_set_ioengine_opts(struct option *long_options,
2497				   struct thread_data *td)
2498{
2499	unsigned int i;
2500
2501	i = 0;
2502	while (long_options[i].name) {
2503		if (long_options[i].val == FIO_GETOPT_IOENGINE) {
2504			memset(&long_options[i], 0, sizeof(*long_options));
2505			break;
2506		}
2507		i++;
2508	}
2509
2510	/*
2511	 * Just clear out the prior ioengine options.
2512	 */
2513	if (!td || !td->eo)
2514		return;
2515
2516	options_to_lopts(td->io_ops->options, long_options, i,
2517			 FIO_GETOPT_IOENGINE);
2518}
2519
2520void fio_options_dup_and_init(struct option *long_options)
2521{
2522	unsigned int i;
2523
2524	options_init(fio_options);
2525
2526	i = 0;
2527	while (long_options[i].name)
2528		i++;
2529
2530	options_to_lopts(fio_options, long_options, i, FIO_GETOPT_JOB);
2531}
2532
2533struct fio_keyword {
2534	const char *word;
2535	const char *desc;
2536	char *replace;
2537};
2538
2539static struct fio_keyword fio_keywords[] = {
2540	{
2541		.word	= "$pagesize",
2542		.desc	= "Page size in the system",
2543	},
2544	{
2545		.word	= "$mb_memory",
2546		.desc	= "Megabytes of memory online",
2547	},
2548	{
2549		.word	= "$ncpus",
2550		.desc	= "Number of CPUs online in the system",
2551	},
2552	{
2553		.word	= NULL,
2554	},
2555};
2556
2557void fio_keywords_init(void)
2558{
2559	unsigned long long mb_memory;
2560	char buf[128];
2561	long l;
2562
2563	sprintf(buf, "%lu", page_size);
2564	fio_keywords[0].replace = strdup(buf);
2565
2566	mb_memory = os_phys_mem() / (1024 * 1024);
2567	sprintf(buf, "%llu", mb_memory);
2568	fio_keywords[1].replace = strdup(buf);
2569
2570	l = cpus_online();
2571	sprintf(buf, "%lu", l);
2572	fio_keywords[2].replace = strdup(buf);
2573}
2574
2575#define BC_APP		"bc"
2576
2577static char *bc_calc(char *str)
2578{
2579	char buf[128], *tmp;
2580	FILE *f;
2581	int ret;
2582
2583	/*
2584	 * No math, just return string
2585	 */
2586	if ((!strchr(str, '+') && !strchr(str, '-') && !strchr(str, '*') &&
2587	     !strchr(str, '/')) || strchr(str, '\''))
2588		return str;
2589
2590	/*
2591	 * Split option from value, we only need to calculate the value
2592	 */
2593	tmp = strchr(str, '=');
2594	if (!tmp)
2595		return str;
2596
2597	tmp++;
2598
2599	/*
2600	 * Prevent buffer overflows; such a case isn't reasonable anyway
2601	 */
2602	if (strlen(str) >= 128 || strlen(tmp) > 100)
2603		return str;
2604
2605	sprintf(buf, "which %s > /dev/null", BC_APP);
2606	if (system(buf)) {
2607		log_err("fio: bc is needed for performing math\n");
2608		return NULL;
2609	}
2610
2611	sprintf(buf, "echo '%s' | %s", tmp, BC_APP);
2612	f = popen(buf, "r");
2613	if (!f) {
2614		return NULL;
2615	}
2616
2617	ret = fread(&buf[tmp - str], 1, 128 - (tmp - str), f);
2618	if (ret <= 0) {
2619		return NULL;
2620	}
2621
2622	pclose(f);
2623	buf[(tmp - str) + ret - 1] = '\0';
2624	memcpy(buf, str, tmp - str);
2625	free(str);
2626	return strdup(buf);
2627}
2628
2629/*
2630 * Return a copy of the input string with substrings of the form ${VARNAME}
2631 * substituted with the value of the environment variable VARNAME.  The
2632 * substitution always occurs, even if VARNAME is empty or the corresponding
2633 * environment variable undefined.
2634 */
2635static char *option_dup_subs(const char *opt)
2636{
2637	char out[OPT_LEN_MAX+1];
2638	char in[OPT_LEN_MAX+1];
2639	char *outptr = out;
2640	char *inptr = in;
2641	char *ch1, *ch2, *env;
2642	ssize_t nchr = OPT_LEN_MAX;
2643	size_t envlen;
2644
2645	if (strlen(opt) + 1 > OPT_LEN_MAX) {
2646		log_err("OPT_LEN_MAX (%d) is too small\n", OPT_LEN_MAX);
2647		return NULL;
2648	}
2649
2650	in[OPT_LEN_MAX] = '\0';
2651	strncpy(in, opt, OPT_LEN_MAX);
2652
2653	while (*inptr && nchr > 0) {
2654		if (inptr[0] == '$' && inptr[1] == '{') {
2655			ch2 = strchr(inptr, '}');
2656			if (ch2 && inptr+1 < ch2) {
2657				ch1 = inptr+2;
2658				inptr = ch2+1;
2659				*ch2 = '\0';
2660
2661				env = getenv(ch1);
2662				if (env) {
2663					envlen = strlen(env);
2664					if (envlen <= nchr) {
2665						memcpy(outptr, env, envlen);
2666						outptr += envlen;
2667						nchr -= envlen;
2668					}
2669				}
2670
2671				continue;
2672			}
2673		}
2674
2675		*outptr++ = *inptr++;
2676		--nchr;
2677	}
2678
2679	*outptr = '\0';
2680	return strdup(out);
2681}
2682
2683/*
2684 * Look for reserved variable names and replace them with real values
2685 */
2686static char *fio_keyword_replace(char *opt)
2687{
2688	char *s;
2689	int i;
2690	int docalc = 0;
2691
2692	for (i = 0; fio_keywords[i].word != NULL; i++) {
2693		struct fio_keyword *kw = &fio_keywords[i];
2694
2695		while ((s = strstr(opt, kw->word)) != NULL) {
2696			char *new = malloc(strlen(opt) + 1);
2697			char *o_org = opt;
2698			int olen = s - opt;
2699			int len;
2700
2701			/*
2702			 * Copy part of the string before the keyword and
2703			 * sprintf() the replacement after it.
2704			 */
2705			memcpy(new, opt, olen);
2706			len = sprintf(new + olen, "%s", kw->replace);
2707
2708			/*
2709			 * If there's more in the original string, copy that
2710			 * in too
2711			 */
2712			opt += strlen(kw->word) + olen;
2713			if (strlen(opt))
2714				memcpy(new + olen + len, opt, opt - o_org - 1);
2715
2716			/*
2717			 * replace opt and free the old opt
2718			 */
2719			opt = new;
2720			free(o_org);
2721
2722			docalc = 1;
2723		}
2724	}
2725
2726	/*
2727	 * Check for potential math and invoke bc, if possible
2728	 */
2729	if (docalc)
2730		opt = bc_calc(opt);
2731
2732	return opt;
2733}
2734
2735static char **dup_and_sub_options(char **opts, int num_opts)
2736{
2737	int i;
2738	char **opts_copy = malloc(num_opts * sizeof(*opts));
2739	for (i = 0; i < num_opts; i++) {
2740		opts_copy[i] = option_dup_subs(opts[i]);
2741		if (!opts_copy[i])
2742			continue;
2743		opts_copy[i] = fio_keyword_replace(opts_copy[i]);
2744	}
2745	return opts_copy;
2746}
2747
2748int fio_options_parse(struct thread_data *td, char **opts, int num_opts)
2749{
2750	int i, ret, unknown;
2751	char **opts_copy;
2752
2753	sort_options(opts, fio_options, num_opts);
2754	opts_copy = dup_and_sub_options(opts, num_opts);
2755
2756	for (ret = 0, i = 0, unknown = 0; i < num_opts; i++) {
2757		struct fio_option *o;
2758		int newret = parse_option(opts_copy[i], opts[i], fio_options,
2759						&o, td);
2760
2761		if (opts_copy[i]) {
2762			if (newret && !o) {
2763				unknown++;
2764				continue;
2765			}
2766			free(opts_copy[i]);
2767			opts_copy[i] = NULL;
2768		}
2769
2770		ret |= newret;
2771	}
2772
2773	if (unknown) {
2774		ret |= ioengine_load(td);
2775		if (td->eo) {
2776			sort_options(opts_copy, td->io_ops->options, num_opts);
2777			opts = opts_copy;
2778		}
2779		for (i = 0; i < num_opts; i++) {
2780			struct fio_option *o = NULL;
2781			int newret = 1;
2782			if (!opts_copy[i])
2783				continue;
2784
2785			if (td->eo)
2786				newret = parse_option(opts_copy[i], opts[i],
2787						      td->io_ops->options, &o,
2788						      td->eo);
2789
2790			ret |= newret;
2791			if (!o)
2792				log_err("Bad option <%s>\n", opts[i]);
2793
2794			free(opts_copy[i]);
2795			opts_copy[i] = NULL;
2796		}
2797	}
2798
2799	free(opts_copy);
2800	return ret;
2801}
2802
2803int fio_cmd_option_parse(struct thread_data *td, const char *opt, char *val)
2804{
2805	return parse_cmd_option(opt, val, fio_options, td);
2806}
2807
2808int fio_cmd_ioengine_option_parse(struct thread_data *td, const char *opt,
2809				char *val)
2810{
2811	return parse_cmd_option(opt, val, td->io_ops->options, td);
2812}
2813
2814void fio_fill_default_options(struct thread_data *td)
2815{
2816	fill_default_options(td, fio_options);
2817}
2818
2819int fio_show_option_help(const char *opt)
2820{
2821	return show_cmd_help(fio_options, opt);
2822}
2823
2824void options_mem_dupe(void *data, struct fio_option *options)
2825{
2826	struct fio_option *o;
2827	char **ptr;
2828
2829	for (o = &options[0]; o->name; o++) {
2830		if (o->type != FIO_OPT_STR_STORE)
2831			continue;
2832
2833		ptr = td_var(data, o->off1);
2834		if (*ptr)
2835			*ptr = strdup(*ptr);
2836	}
2837}
2838
2839/*
2840 * dupe FIO_OPT_STR_STORE options
2841 */
2842void fio_options_mem_dupe(struct thread_data *td)
2843{
2844	options_mem_dupe(&td->o, fio_options);
2845
2846	if (td->eo && td->io_ops) {
2847		void *oldeo = td->eo;
2848
2849		td->eo = malloc(td->io_ops->option_struct_size);
2850		memcpy(td->eo, oldeo, td->io_ops->option_struct_size);
2851		options_mem_dupe(td->eo, td->io_ops->options);
2852	}
2853}
2854
2855unsigned int fio_get_kb_base(void *data)
2856{
2857	struct thread_data *td = data;
2858	unsigned int kb_base = 0;
2859
2860	if (td)
2861		kb_base = td->o.kb_base;
2862	if (!kb_base)
2863		kb_base = 1024;
2864
2865	return kb_base;
2866}
2867
2868int add_option(struct fio_option *o)
2869{
2870	struct fio_option *__o;
2871	int opt_index = 0;
2872
2873	__o = fio_options;
2874	while (__o->name) {
2875		opt_index++;
2876		__o++;
2877	}
2878
2879	memcpy(&fio_options[opt_index], o, sizeof(*o));
2880	return 0;
2881}
2882
2883void invalidate_profile_options(const char *prof_name)
2884{
2885	struct fio_option *o;
2886
2887	o = fio_options;
2888	while (o->name) {
2889		if (o->prof_name && !strcmp(o->prof_name, prof_name)) {
2890			o->type = FIO_OPT_INVALID;
2891			o->prof_name = NULL;
2892		}
2893		o++;
2894	}
2895}
2896
2897void add_opt_posval(const char *optname, const char *ival, const char *help)
2898{
2899	struct fio_option *o;
2900	unsigned int i;
2901
2902	o = find_option(fio_options, optname);
2903	if (!o)
2904		return;
2905
2906	for (i = 0; i < PARSE_MAX_VP; i++) {
2907		if (o->posval[i].ival)
2908			continue;
2909
2910		o->posval[i].ival = ival;
2911		o->posval[i].help = help;
2912		break;
2913	}
2914}
2915
2916void del_opt_posval(const char *optname, const char *ival)
2917{
2918	struct fio_option *o;
2919	unsigned int i;
2920
2921	o = find_option(fio_options, optname);
2922	if (!o)
2923		return;
2924
2925	for (i = 0; i < PARSE_MAX_VP; i++) {
2926		if (!o->posval[i].ival)
2927			continue;
2928		if (strcmp(o->posval[i].ival, ival))
2929			continue;
2930
2931		o->posval[i].ival = NULL;
2932		o->posval[i].help = NULL;
2933	}
2934}
2935
2936void fio_options_free(struct thread_data *td)
2937{
2938	options_free(fio_options, td);
2939	if (td->eo && td->io_ops && td->io_ops->options) {
2940		options_free(td->io_ops->options, td->eo);
2941		free(td->eo);
2942		td->eo = NULL;
2943	}
2944}
2945