options.c revision 79d16311c8d0c7188d73df77838fb1b4b6ff58db
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <ctype.h>
5#include <string.h>
6#include <getopt.h>
7#include <assert.h>
8#include <libgen.h>
9#include <fcntl.h>
10#include <sys/types.h>
11#include <sys/stat.h>
12
13#include "fio.h"
14#include "verify.h"
15#include "parse.h"
16#include "lib/fls.h"
17#include "options.h"
18
19static FLIST_HEAD(ext_opt_list);
20
21/*
22 * Check if mmap/mmaphuge has a :/foo/bar/file at the end. If so, return that.
23 */
24static char *get_opt_postfix(const char *str)
25{
26	char *p = strstr(str, ":");
27
28	if (!p)
29		return NULL;
30
31	p++;
32	strip_blank_front(&p);
33	strip_blank_end(p);
34	return strdup(p);
35}
36
37static int converthexchartoint(char a)
38{
39	int base;
40
41	switch(a) {
42	case '0'...'9':
43		base = '0';
44		break;
45	case 'A'...'F':
46		base = 'A' - 10;
47		break;
48	case 'a'...'f':
49		base = 'a' - 10;
50		break;
51	default:
52		base = 0;
53	}
54	return (a - base);
55}
56
57static int bs_cmp(const void *p1, const void *p2)
58{
59	const struct bssplit *bsp1 = p1;
60	const struct bssplit *bsp2 = p2;
61
62	return bsp1->perc < bsp2->perc;
63}
64
65static int bssplit_ddir(struct thread_data *td, int ddir, char *str)
66{
67	struct bssplit *bssplit;
68	unsigned int i, perc, perc_missing;
69	unsigned int max_bs, min_bs;
70	long long val;
71	char *fname;
72
73	td->o.bssplit_nr[ddir] = 4;
74	bssplit = malloc(4 * sizeof(struct bssplit));
75
76	i = 0;
77	max_bs = 0;
78	min_bs = -1;
79	while ((fname = strsep(&str, ":")) != NULL) {
80		char *perc_str;
81
82		if (!strlen(fname))
83			break;
84
85		/*
86		 * grow struct buffer, if needed
87		 */
88		if (i == td->o.bssplit_nr[ddir]) {
89			td->o.bssplit_nr[ddir] <<= 1;
90			bssplit = realloc(bssplit, td->o.bssplit_nr[ddir]
91						  * sizeof(struct bssplit));
92		}
93
94		perc_str = strstr(fname, "/");
95		if (perc_str) {
96			*perc_str = '\0';
97			perc_str++;
98			perc = atoi(perc_str);
99			if (perc > 100)
100				perc = 100;
101			else if (!perc)
102				perc = -1;
103		} else
104			perc = -1;
105
106		if (str_to_decimal(fname, &val, 1, td)) {
107			log_err("fio: bssplit conversion failed\n");
108			free(td->o.bssplit);
109			return 1;
110		}
111
112		if (val > max_bs)
113			max_bs = val;
114		if (val < min_bs)
115			min_bs = val;
116
117		bssplit[i].bs = val;
118		bssplit[i].perc = perc;
119		i++;
120	}
121
122	td->o.bssplit_nr[ddir] = i;
123
124	/*
125	 * Now check if the percentages add up, and how much is missing
126	 */
127	perc = perc_missing = 0;
128	for (i = 0; i < td->o.bssplit_nr[ddir]; i++) {
129		struct bssplit *bsp = &bssplit[i];
130
131		if (bsp->perc == (unsigned char) -1)
132			perc_missing++;
133		else
134			perc += bsp->perc;
135	}
136
137	if (perc > 100) {
138		log_err("fio: bssplit percentages add to more than 100%%\n");
139		free(bssplit);
140		return 1;
141	}
142	/*
143	 * If values didn't have a percentage set, divide the remains between
144	 * them.
145	 */
146	if (perc_missing) {
147		for (i = 0; i < td->o.bssplit_nr[ddir]; i++) {
148			struct bssplit *bsp = &bssplit[i];
149
150			if (bsp->perc == (unsigned char) -1)
151				bsp->perc = (100 - perc) / perc_missing;
152		}
153	}
154
155	td->o.min_bs[ddir] = min_bs;
156	td->o.max_bs[ddir] = max_bs;
157
158	/*
159	 * now sort based on percentages, for ease of lookup
160	 */
161	qsort(bssplit, td->o.bssplit_nr[ddir], sizeof(struct bssplit), bs_cmp);
162	td->o.bssplit[ddir] = bssplit;
163	return 0;
164
165}
166
167static int str_bssplit_cb(void *data, const char *input)
168{
169	struct thread_data *td = data;
170	char *str, *p, *odir;
171	int ret = 0;
172
173	p = str = strdup(input);
174
175	strip_blank_front(&str);
176	strip_blank_end(str);
177
178	odir = strchr(str, ',');
179	if (odir) {
180		ret = bssplit_ddir(td, DDIR_WRITE, odir + 1);
181		if (!ret) {
182			*odir = '\0';
183			ret = bssplit_ddir(td, DDIR_READ, str);
184		}
185	} else {
186		char *op;
187
188		op = strdup(str);
189
190		ret = bssplit_ddir(td, DDIR_READ, str);
191		if (!ret)
192			ret = bssplit_ddir(td, DDIR_WRITE, op);
193
194		free(op);
195	}
196
197	free(p);
198	return ret;
199}
200
201static int str_rw_cb(void *data, const char *str)
202{
203	struct thread_data *td = data;
204	char *nr = get_opt_postfix(str);
205
206	td->o.ddir_nr = 1;
207	if (nr) {
208		td->o.ddir_nr = atoi(nr);
209		free(nr);
210	}
211
212	return 0;
213}
214
215static int str_mem_cb(void *data, const char *mem)
216{
217	struct thread_data *td = data;
218
219	if (td->o.mem_type == MEM_MMAPHUGE || td->o.mem_type == MEM_MMAP) {
220		td->mmapfile = get_opt_postfix(mem);
221		if (td->o.mem_type == MEM_MMAPHUGE && !td->mmapfile) {
222			log_err("fio: mmaphuge:/path/to/file\n");
223			return 1;
224		}
225	}
226
227	return 0;
228}
229
230static int str_lockmem_cb(void fio_unused *data, unsigned long *val)
231{
232	mlock_size = *val;
233	return 0;
234}
235
236static int str_rwmix_read_cb(void *data, unsigned int *val)
237{
238	struct thread_data *td = data;
239
240	td->o.rwmix[DDIR_READ] = *val;
241	td->o.rwmix[DDIR_WRITE] = 100 - *val;
242	return 0;
243}
244
245static int str_rwmix_write_cb(void *data, unsigned int *val)
246{
247	struct thread_data *td = data;
248
249	td->o.rwmix[DDIR_WRITE] = *val;
250	td->o.rwmix[DDIR_READ] = 100 - *val;
251	return 0;
252}
253
254#ifdef FIO_HAVE_IOPRIO
255static int str_prioclass_cb(void *data, unsigned int *val)
256{
257	struct thread_data *td = data;
258	unsigned short mask;
259
260	/*
261	 * mask off old class bits, str_prio_cb() may have set a default class
262	 */
263	mask = (1 << IOPRIO_CLASS_SHIFT) - 1;
264	td->ioprio &= mask;
265
266	td->ioprio |= *val << IOPRIO_CLASS_SHIFT;
267	td->ioprio_set = 1;
268	return 0;
269}
270
271static int str_prio_cb(void *data, unsigned int *val)
272{
273	struct thread_data *td = data;
274
275	td->ioprio |= *val;
276
277	/*
278	 * If no class is set, assume BE
279	 */
280	if ((td->ioprio >> IOPRIO_CLASS_SHIFT) == 0)
281		td->ioprio |= IOPRIO_CLASS_BE << IOPRIO_CLASS_SHIFT;
282
283	td->ioprio_set = 1;
284	return 0;
285}
286#endif
287
288static int str_exitall_cb(void)
289{
290	exitall_on_terminate = 1;
291	return 0;
292}
293
294#ifdef FIO_HAVE_CPU_AFFINITY
295static int str_cpumask_cb(void *data, unsigned int *val)
296{
297	struct thread_data *td = data;
298	unsigned int i;
299	long max_cpu;
300	int ret;
301
302	ret = fio_cpuset_init(&td->o.cpumask);
303	if (ret < 0) {
304		log_err("fio: cpuset_init failed\n");
305		td_verror(td, ret, "fio_cpuset_init");
306		return 1;
307	}
308
309	max_cpu = sysconf(_SC_NPROCESSORS_ONLN);
310
311	for (i = 0; i < sizeof(int) * 8; i++) {
312		if ((1 << i) & *val) {
313			if (i > max_cpu) {
314				log_err("fio: CPU %d too large (max=%ld)\n", i,
315								max_cpu);
316				return 1;
317			}
318			dprint(FD_PARSE, "set cpu allowed %d\n", i);
319			fio_cpu_set(&td->o.cpumask, i);
320		}
321	}
322
323	td->o.cpumask_set = 1;
324	return 0;
325}
326
327static int set_cpus_allowed(struct thread_data *td, os_cpu_mask_t *mask,
328			    const char *input)
329{
330	char *cpu, *str, *p;
331	long max_cpu;
332	int ret = 0;
333
334	ret = fio_cpuset_init(mask);
335	if (ret < 0) {
336		log_err("fio: cpuset_init failed\n");
337		td_verror(td, ret, "fio_cpuset_init");
338		return 1;
339	}
340
341	p = str = strdup(input);
342
343	strip_blank_front(&str);
344	strip_blank_end(str);
345
346	max_cpu = sysconf(_SC_NPROCESSORS_ONLN);
347
348	while ((cpu = strsep(&str, ",")) != NULL) {
349		char *str2, *cpu2;
350		int icpu, icpu2;
351
352		if (!strlen(cpu))
353			break;
354
355		str2 = cpu;
356		icpu2 = -1;
357		while ((cpu2 = strsep(&str2, "-")) != NULL) {
358			if (!strlen(cpu2))
359				break;
360
361			icpu2 = atoi(cpu2);
362		}
363
364		icpu = atoi(cpu);
365		if (icpu2 == -1)
366			icpu2 = icpu;
367		while (icpu <= icpu2) {
368			if (icpu >= FIO_MAX_CPUS) {
369				log_err("fio: your OS only supports up to"
370					" %d CPUs\n", (int) FIO_MAX_CPUS);
371				ret = 1;
372				break;
373			}
374			if (icpu > max_cpu) {
375				log_err("fio: CPU %d too large (max=%ld)\n",
376							icpu, max_cpu);
377				ret = 1;
378				break;
379			}
380
381			dprint(FD_PARSE, "set cpu allowed %d\n", icpu);
382			fio_cpu_set(mask, icpu);
383			icpu++;
384		}
385		if (ret)
386			break;
387	}
388
389	free(p);
390	if (!ret)
391		td->o.cpumask_set = 1;
392	return ret;
393}
394
395static int str_cpus_allowed_cb(void *data, const char *input)
396{
397	struct thread_data *td = data;
398	int ret;
399
400	ret = set_cpus_allowed(td, &td->o.cpumask, input);
401	if (!ret)
402		td->o.cpumask_set = 1;
403
404	return ret;
405}
406
407static int str_verify_cpus_allowed_cb(void *data, const char *input)
408{
409	struct thread_data *td = data;
410	int ret;
411
412	ret = set_cpus_allowed(td, &td->o.verify_cpumask, input);
413	if (!ret)
414		td->o.verify_cpumask_set = 1;
415
416	return ret;
417}
418#endif
419
420static int str_fst_cb(void *data, const char *str)
421{
422	struct thread_data *td = data;
423	char *nr = get_opt_postfix(str);
424
425	td->file_service_nr = 1;
426	if (nr) {
427		td->file_service_nr = atoi(nr);
428		free(nr);
429	}
430
431	return 0;
432}
433
434static int check_dir(struct thread_data *td, char *fname)
435{
436	char file[PATH_MAX], *dir;
437	int elen = 0;
438
439	if (td->o.directory) {
440		strcpy(file, td->o.directory);
441		strcat(file, "/");
442		elen = strlen(file);
443	}
444
445	sprintf(file + elen, "%s", fname);
446	dir = dirname(file);
447
448#if 0
449	{
450	struct stat sb;
451	/*
452	 * We can't do this on FIO_DISKLESSIO engines. The engine isn't loaded
453	 * yet, so we can't do this check right here...
454	 */
455	if (lstat(dir, &sb) < 0) {
456		int ret = errno;
457
458		log_err("fio: %s is not a directory\n", dir);
459		td_verror(td, ret, "lstat");
460		return 1;
461	}
462
463	if (!S_ISDIR(sb.st_mode)) {
464		log_err("fio: %s is not a directory\n", dir);
465		return 1;
466	}
467	}
468#endif
469
470	return 0;
471}
472
473/*
474 * Return next file in the string. Files are separated with ':'. If the ':'
475 * is escaped with a '\', then that ':' is part of the filename and does not
476 * indicate a new file.
477 */
478static char *get_next_file_name(char **ptr)
479{
480	char *str = *ptr;
481	char *p, *start;
482
483	if (!str || !strlen(str))
484		return NULL;
485
486	start = str;
487	do {
488		/*
489		 * No colon, we are done
490		 */
491		p = strchr(str, ':');
492		if (!p) {
493			*ptr = NULL;
494			break;
495		}
496
497		/*
498		 * We got a colon, but it's the first character. Skip and
499		 * continue
500		 */
501		if (p == start) {
502			str = ++start;
503			continue;
504		}
505
506		if (*(p - 1) != '\\') {
507			*p = '\0';
508			*ptr = p + 1;
509			break;
510		}
511
512		memmove(p - 1, p, strlen(p) + 1);
513		str = p;
514	} while (1);
515
516	return start;
517}
518
519static int str_filename_cb(void *data, const char *input)
520{
521	struct thread_data *td = data;
522	char *fname, *str, *p;
523
524	p = str = strdup(input);
525
526	strip_blank_front(&str);
527	strip_blank_end(str);
528
529	if (!td->files_index)
530		td->o.nr_files = 0;
531
532	while ((fname = get_next_file_name(&str)) != NULL) {
533		if (!strlen(fname))
534			break;
535		if (check_dir(td, fname)) {
536			free(p);
537			return 1;
538		}
539		add_file(td, fname);
540		td->o.nr_files++;
541	}
542
543	free(p);
544	return 0;
545}
546
547static int str_directory_cb(void *data, const char fio_unused *str)
548{
549	struct thread_data *td = data;
550	struct stat sb;
551
552	if (lstat(td->o.directory, &sb) < 0) {
553		int ret = errno;
554
555		log_err("fio: %s is not a directory\n", td->o.directory);
556		td_verror(td, ret, "lstat");
557		return 1;
558	}
559	if (!S_ISDIR(sb.st_mode)) {
560		log_err("fio: %s is not a directory\n", td->o.directory);
561		return 1;
562	}
563
564	return 0;
565}
566
567static int str_opendir_cb(void *data, const char fio_unused *str)
568{
569	struct thread_data *td = data;
570
571	if (!td->files_index)
572		td->o.nr_files = 0;
573
574	return add_dir_files(td, td->o.opendir);
575}
576
577static int str_verify_offset_cb(void *data, unsigned int *off)
578{
579	struct thread_data *td = data;
580
581	if (*off && *off < sizeof(struct verify_header)) {
582		log_err("fio: verify_offset too small\n");
583		return 1;
584	}
585
586	td->o.verify_offset = *off;
587	return 0;
588}
589
590static int str_verify_pattern_cb(void *data, const char *input)
591{
592	struct thread_data *td = data;
593	long off;
594	int i = 0, j = 0, len, k, base = 10;
595	char* loc1, * loc2;
596
597	loc1 = strstr(input, "0x");
598	loc2 = strstr(input, "0X");
599	if (loc1 || loc2)
600		base = 16;
601	off = strtol(input, NULL, base);
602	if (off != LONG_MAX || errno != ERANGE) {
603		while (off) {
604			td->o.verify_pattern[i] = off & 0xff;
605			off >>= 8;
606			i++;
607		}
608	} else {
609		len = strlen(input);
610		k = len - 1;
611		if (base == 16) {
612			if (loc1)
613				j = loc1 - input + 2;
614			else
615				j = loc2 - input + 2;
616		} else
617			return 1;
618		if (len - j < MAX_PATTERN_SIZE * 2) {
619			while (k >= j) {
620				off = converthexchartoint(input[k--]);
621				if (k >= j)
622					off += (converthexchartoint(input[k--])
623						* 16);
624				td->o.verify_pattern[i++] = (char) off;
625			}
626		}
627	}
628	td->o.verify_pattern_bytes = i;
629	return 0;
630}
631
632static int str_lockfile_cb(void *data, const char *str)
633{
634	struct thread_data *td = data;
635	char *nr = get_opt_postfix(str);
636
637	td->o.lockfile_batch = 1;
638	if (nr) {
639		td->o.lockfile_batch = atoi(nr);
640		free(nr);
641	}
642
643	return 0;
644}
645
646static int str_write_bw_log_cb(void *data, const char *str)
647{
648	struct thread_data *td = data;
649
650	if (str)
651		td->o.bw_log_file = strdup(str);
652
653	td->o.write_bw_log = 1;
654	return 0;
655}
656
657static int str_write_lat_log_cb(void *data, const char *str)
658{
659	struct thread_data *td = data;
660
661	if (str)
662		td->o.lat_log_file = strdup(str);
663
664	td->o.write_lat_log = 1;
665	return 0;
666}
667
668static int str_gtod_reduce_cb(void *data, int *il)
669{
670	struct thread_data *td = data;
671	int val = *il;
672
673	td->o.disable_clat = !!val;
674	td->o.disable_slat = !!val;
675	td->o.disable_bw = !!val;
676	if (val)
677		td->tv_cache_mask = 63;
678
679	return 0;
680}
681
682static int str_gtod_cpu_cb(void *data, int *il)
683{
684	struct thread_data *td = data;
685	int val = *il;
686
687	td->o.gtod_cpu = val;
688	td->o.gtod_offload = 1;
689	return 0;
690}
691
692static int rw_verify(struct fio_option *o, void *data)
693{
694	struct thread_data *td = data;
695
696	if (read_only && td_write(td)) {
697		log_err("fio: job <%s> has write bit set, but fio is in"
698			" read-only mode\n", td->o.name);
699		return 1;
700	}
701
702	return 0;
703}
704
705static int gtod_cpu_verify(struct fio_option *o, void *data)
706{
707#ifndef FIO_HAVE_CPU_AFFINITY
708	struct thread_data *td = data;
709
710	if (td->o.gtod_cpu) {
711		log_err("fio: platform must support CPU affinity for"
712			"gettimeofday() offloading\n");
713		return 1;
714	}
715#endif
716
717	return 0;
718}
719
720static int kb_base_verify(struct fio_option *o, void *data)
721{
722	struct thread_data *td = data;
723
724	if (td->o.kb_base != 1024 && td->o.kb_base != 1000) {
725		log_err("fio: kb_base set to nonsensical value: %u\n",
726				td->o.kb_base);
727		return 1;
728	}
729
730	return 0;
731}
732
733#define __stringify_1(x)	#x
734#define __stringify(x)		__stringify_1(x)
735
736/*
737 * Map of job/command line options
738 */
739static struct fio_option options[] = {
740	{
741		.name	= "description",
742		.type	= FIO_OPT_STR_STORE,
743		.off1	= td_var_offset(description),
744		.help	= "Text job description",
745	},
746	{
747		.name	= "name",
748		.type	= FIO_OPT_STR_STORE,
749		.off1	= td_var_offset(name),
750		.help	= "Name of this job",
751	},
752	{
753		.name	= "directory",
754		.type	= FIO_OPT_STR_STORE,
755		.off1	= td_var_offset(directory),
756		.cb	= str_directory_cb,
757		.help	= "Directory to store files in",
758	},
759	{
760		.name	= "filename",
761		.type	= FIO_OPT_STR_STORE,
762		.off1	= td_var_offset(filename),
763		.cb	= str_filename_cb,
764		.prio	= -1, /* must come after "directory" */
765		.help	= "File(s) to use for the workload",
766	},
767	{
768		.name	= "kb_base",
769		.type	= FIO_OPT_INT,
770		.off1	= td_var_offset(kb_base),
771		.verify	= kb_base_verify,
772		.prio	= 1,
773		.def	= "1024",
774		.help	= "How many bytes per KB for reporting (1000 or 1024)",
775	},
776	{
777		.name	= "lockfile",
778		.type	= FIO_OPT_STR,
779		.cb	= str_lockfile_cb,
780		.off1	= td_var_offset(file_lock_mode),
781		.help	= "Lock file when doing IO to it",
782		.parent	= "filename",
783		.def	= "none",
784		.posval = {
785			  { .ival = "none",
786			    .oval = FILE_LOCK_NONE,
787			    .help = "No file locking",
788			  },
789			  { .ival = "exclusive",
790			    .oval = FILE_LOCK_EXCLUSIVE,
791			    .help = "Exclusive file lock",
792			  },
793			  {
794			    .ival = "readwrite",
795			    .oval = FILE_LOCK_READWRITE,
796			    .help = "Read vs write lock",
797			  },
798		},
799	},
800	{
801		.name	= "opendir",
802		.type	= FIO_OPT_STR_STORE,
803		.off1	= td_var_offset(opendir),
804		.cb	= str_opendir_cb,
805		.help	= "Recursively add files from this directory and down",
806	},
807	{
808		.name	= "rw",
809		.alias	= "readwrite",
810		.type	= FIO_OPT_STR,
811		.cb	= str_rw_cb,
812		.off1	= td_var_offset(td_ddir),
813		.help	= "IO direction",
814		.def	= "read",
815		.verify	= rw_verify,
816		.posval = {
817			  { .ival = "read",
818			    .oval = TD_DDIR_READ,
819			    .help = "Sequential read",
820			  },
821			  { .ival = "write",
822			    .oval = TD_DDIR_WRITE,
823			    .help = "Sequential write",
824			  },
825			  { .ival = "randread",
826			    .oval = TD_DDIR_RANDREAD,
827			    .help = "Random read",
828			  },
829			  { .ival = "randwrite",
830			    .oval = TD_DDIR_RANDWRITE,
831			    .help = "Random write",
832			  },
833			  { .ival = "rw",
834			    .oval = TD_DDIR_RW,
835			    .help = "Sequential read and write mix",
836			  },
837			  { .ival = "randrw",
838			    .oval = TD_DDIR_RANDRW,
839			    .help = "Random read and write mix"
840			  },
841		},
842	},
843	{
844		.name	= "ioengine",
845		.type	= FIO_OPT_STR_STORE,
846		.off1	= td_var_offset(ioengine),
847		.help	= "IO engine to use",
848		.def	= "sync",
849		.posval	= {
850			  { .ival = "sync",
851			    .help = "Use read/write",
852			  },
853			  { .ival = "psync",
854			    .help = "Use pread/pwrite",
855			  },
856			  { .ival = "vsync",
857			     .help = "Use readv/writev",
858			  },
859#ifdef FIO_HAVE_LIBAIO
860			  { .ival = "libaio",
861			    .help = "Linux native asynchronous IO",
862			  },
863#endif
864#ifdef FIO_HAVE_POSIXAIO
865			  { .ival = "posixaio",
866			    .help = "POSIX asynchronous IO",
867			  },
868#endif
869#ifdef FIO_HAVE_SOLARISAIO
870			  { .ival = "solarisaio",
871			    .help = "Solaris native asynchronous IO",
872			  },
873#endif
874			  { .ival = "mmap",
875			    .help = "Memory mapped IO",
876			  },
877#ifdef FIO_HAVE_SPLICE
878			  { .ival = "splice",
879			    .help = "splice/vmsplice based IO",
880			  },
881			  { .ival = "netsplice",
882			    .help = "splice/vmsplice to/from the network",
883			  },
884#endif
885#ifdef FIO_HAVE_SGIO
886			  { .ival = "sg",
887			    .help = "SCSI generic v3 IO",
888			  },
889#endif
890			  { .ival = "null",
891			    .help = "Testing engine (no data transfer)",
892			  },
893			  { .ival = "net",
894			    .help = "Network IO",
895			  },
896#ifdef FIO_HAVE_SYSLET
897			  { .ival = "syslet-rw",
898			    .help = "syslet enabled async pread/pwrite IO",
899			  },
900#endif
901			  { .ival = "cpuio",
902			    .help = "CPU cycler burner engine",
903			  },
904#ifdef FIO_HAVE_GUASI
905			  { .ival = "guasi",
906			    .help = "GUASI IO engine",
907			  },
908#endif
909			  { .ival = "external",
910			    .help = "Load external engine (append name)",
911			  },
912		},
913	},
914	{
915		.name	= "iodepth",
916		.type	= FIO_OPT_INT,
917		.off1	= td_var_offset(iodepth),
918		.help	= "Amount of IO buffers to keep in flight",
919		.minval = 1,
920		.def	= "1",
921	},
922	{
923		.name	= "iodepth_batch",
924		.alias	= "iodepth_batch_submit",
925		.type	= FIO_OPT_INT,
926		.off1	= td_var_offset(iodepth_batch),
927		.help	= "Number of IO buffers to submit in one go",
928		.parent	= "iodepth",
929		.minval	= 1,
930		.def	= "1",
931	},
932	{
933		.name	= "iodepth_batch_complete",
934		.type	= FIO_OPT_INT,
935		.off1	= td_var_offset(iodepth_batch_complete),
936		.help	= "Number of IO buffers to retrieve in one go",
937		.parent	= "iodepth",
938		.minval	= 0,
939		.def	= "1",
940	},
941	{
942		.name	= "iodepth_low",
943		.type	= FIO_OPT_INT,
944		.off1	= td_var_offset(iodepth_low),
945		.help	= "Low water mark for queuing depth",
946		.parent	= "iodepth",
947	},
948	{
949		.name	= "size",
950		.type	= FIO_OPT_STR_VAL,
951		.off1	= td_var_offset(size),
952		.minval = 1,
953		.help	= "Total size of device or files",
954	},
955	{
956		.name	= "fill_device",
957		.type	= FIO_OPT_BOOL,
958		.off1	= td_var_offset(fill_device),
959		.help	= "Write until an ENOSPC error occurs",
960		.def	= "0",
961	},
962	{
963		.name	= "filesize",
964		.type	= FIO_OPT_STR_VAL,
965		.off1	= td_var_offset(file_size_low),
966		.off2	= td_var_offset(file_size_high),
967		.minval = 1,
968		.help	= "Size of individual files",
969	},
970	{
971		.name	= "offset",
972		.alias	= "fileoffset",
973		.type	= FIO_OPT_STR_VAL,
974		.off1	= td_var_offset(start_offset),
975		.help	= "Start IO from this offset",
976		.def	= "0",
977	},
978	{
979		.name	= "bs",
980		.alias	= "blocksize",
981		.type	= FIO_OPT_INT,
982		.off1	= td_var_offset(bs[DDIR_READ]),
983		.off2	= td_var_offset(bs[DDIR_WRITE]),
984		.minval = 1,
985		.help	= "Block size unit",
986		.def	= "4k",
987		.parent = "rw",
988	},
989	{
990		.name	= "ba",
991		.alias	= "blockalign",
992		.type	= FIO_OPT_INT,
993		.off1	= td_var_offset(ba[DDIR_READ]),
994		.off2	= td_var_offset(ba[DDIR_WRITE]),
995		.minval	= 1,
996		.help	= "IO block offset alignment",
997		.parent	= "rw",
998	},
999	{
1000		.name	= "bsrange",
1001		.alias	= "blocksize_range",
1002		.type	= FIO_OPT_RANGE,
1003		.off1	= td_var_offset(min_bs[DDIR_READ]),
1004		.off2	= td_var_offset(max_bs[DDIR_READ]),
1005		.off3	= td_var_offset(min_bs[DDIR_WRITE]),
1006		.off4	= td_var_offset(max_bs[DDIR_WRITE]),
1007		.minval = 1,
1008		.help	= "Set block size range (in more detail than bs)",
1009		.parent = "rw",
1010	},
1011	{
1012		.name	= "bssplit",
1013		.type	= FIO_OPT_STR,
1014		.cb	= str_bssplit_cb,
1015		.help	= "Set a specific mix of block sizes",
1016		.parent	= "rw",
1017	},
1018	{
1019		.name	= "bs_unaligned",
1020		.alias	= "blocksize_unaligned",
1021		.type	= FIO_OPT_STR_SET,
1022		.off1	= td_var_offset(bs_unaligned),
1023		.help	= "Don't sector align IO buffer sizes",
1024		.parent = "rw",
1025	},
1026	{
1027		.name	= "randrepeat",
1028		.type	= FIO_OPT_BOOL,
1029		.off1	= td_var_offset(rand_repeatable),
1030		.help	= "Use repeatable random IO pattern",
1031		.def	= "1",
1032		.parent = "rw",
1033	},
1034	{
1035		.name	= "norandommap",
1036		.type	= FIO_OPT_STR_SET,
1037		.off1	= td_var_offset(norandommap),
1038		.help	= "Accept potential duplicate random blocks",
1039		.parent = "rw",
1040	},
1041	{
1042		.name	= "softrandommap",
1043		.type	= FIO_OPT_BOOL,
1044		.off1	= td_var_offset(softrandommap),
1045		.help	= "Set norandommap if randommap allocation fails",
1046		.parent	= "norandommap",
1047		.def	= "0",
1048	},
1049	{
1050		.name	= "nrfiles",
1051		.type	= FIO_OPT_INT,
1052		.off1	= td_var_offset(nr_files),
1053		.help	= "Split job workload between this number of files",
1054		.def	= "1",
1055	},
1056	{
1057		.name	= "openfiles",
1058		.type	= FIO_OPT_INT,
1059		.off1	= td_var_offset(open_files),
1060		.help	= "Number of files to keep open at the same time",
1061	},
1062	{
1063		.name	= "file_service_type",
1064		.type	= FIO_OPT_STR,
1065		.cb	= str_fst_cb,
1066		.off1	= td_var_offset(file_service_type),
1067		.help	= "How to select which file to service next",
1068		.def	= "roundrobin",
1069		.posval	= {
1070			  { .ival = "random",
1071			    .oval = FIO_FSERVICE_RANDOM,
1072			    .help = "Choose a file at random",
1073			  },
1074			  { .ival = "roundrobin",
1075			    .oval = FIO_FSERVICE_RR,
1076			    .help = "Round robin select files",
1077			  },
1078			  { .ival = "sequential",
1079			    .oval = FIO_FSERVICE_SEQ,
1080			    .help = "Finish one file before moving to the next",
1081			  },
1082		},
1083		.parent = "nrfiles",
1084	},
1085#ifdef FIO_HAVE_FALLOCATE
1086	{
1087		.name	= "fallocate",
1088		.type	= FIO_OPT_BOOL,
1089		.off1	= td_var_offset(fallocate),
1090		.help	= "Use fallocate() when laying out files",
1091		.def	= "1",
1092	},
1093#endif
1094	{
1095		.name	= "fadvise_hint",
1096		.type	= FIO_OPT_BOOL,
1097		.off1	= td_var_offset(fadvise_hint),
1098		.help	= "Use fadvise() to advise the kernel on IO pattern",
1099		.def	= "1",
1100	},
1101	{
1102		.name	= "fsync",
1103		.type	= FIO_OPT_INT,
1104		.off1	= td_var_offset(fsync_blocks),
1105		.help	= "Issue fsync for writes every given number of blocks",
1106		.def	= "0",
1107	},
1108	{
1109		.name	= "fdatasync",
1110		.type	= FIO_OPT_INT,
1111		.off1	= td_var_offset(fdatasync_blocks),
1112		.help	= "Issue fdatasync for writes every given number of blocks",
1113		.def	= "0",
1114	},
1115	{
1116		.name	= "direct",
1117		.type	= FIO_OPT_BOOL,
1118		.off1	= td_var_offset(odirect),
1119		.help	= "Use O_DIRECT IO (negates buffered)",
1120		.def	= "0",
1121	},
1122	{
1123		.name	= "buffered",
1124		.type	= FIO_OPT_BOOL,
1125		.off1	= td_var_offset(odirect),
1126		.neg	= 1,
1127		.help	= "Use buffered IO (negates direct)",
1128		.def	= "1",
1129	},
1130	{
1131		.name	= "overwrite",
1132		.type	= FIO_OPT_BOOL,
1133		.off1	= td_var_offset(overwrite),
1134		.help	= "When writing, set whether to overwrite current data",
1135		.def	= "0",
1136	},
1137	{
1138		.name	= "loops",
1139		.type	= FIO_OPT_INT,
1140		.off1	= td_var_offset(loops),
1141		.help	= "Number of times to run the job",
1142		.def	= "1",
1143	},
1144	{
1145		.name	= "numjobs",
1146		.type	= FIO_OPT_INT,
1147		.off1	= td_var_offset(numjobs),
1148		.help	= "Duplicate this job this many times",
1149		.def	= "1",
1150	},
1151	{
1152		.name	= "startdelay",
1153		.type	= FIO_OPT_INT,
1154		.off1	= td_var_offset(start_delay),
1155		.help	= "Only start job when this period has passed",
1156		.def	= "0",
1157	},
1158	{
1159		.name	= "runtime",
1160		.alias	= "timeout",
1161		.type	= FIO_OPT_STR_VAL_TIME,
1162		.off1	= td_var_offset(timeout),
1163		.help	= "Stop workload when this amount of time has passed",
1164		.def	= "0",
1165	},
1166	{
1167		.name	= "time_based",
1168		.type	= FIO_OPT_STR_SET,
1169		.off1	= td_var_offset(time_based),
1170		.help	= "Keep running until runtime/timeout is met",
1171	},
1172	{
1173		.name	= "ramp_time",
1174		.type	= FIO_OPT_STR_VAL_TIME,
1175		.off1	= td_var_offset(ramp_time),
1176		.help	= "Ramp up time before measuring performance",
1177	},
1178	{
1179		.name	= "mem",
1180		.alias	= "iomem",
1181		.type	= FIO_OPT_STR,
1182		.cb	= str_mem_cb,
1183		.off1	= td_var_offset(mem_type),
1184		.help	= "Backing type for IO buffers",
1185		.def	= "malloc",
1186		.posval	= {
1187			  { .ival = "malloc",
1188			    .oval = MEM_MALLOC,
1189			    .help = "Use malloc(3) for IO buffers",
1190			  },
1191			  { .ival = "shm",
1192			    .oval = MEM_SHM,
1193			    .help = "Use shared memory segments for IO buffers",
1194			  },
1195#ifdef FIO_HAVE_HUGETLB
1196			  { .ival = "shmhuge",
1197			    .oval = MEM_SHMHUGE,
1198			    .help = "Like shm, but use huge pages",
1199			  },
1200#endif
1201			  { .ival = "mmap",
1202			    .oval = MEM_MMAP,
1203			    .help = "Use mmap(2) (file or anon) for IO buffers",
1204			  },
1205#ifdef FIO_HAVE_HUGETLB
1206			  { .ival = "mmaphuge",
1207			    .oval = MEM_MMAPHUGE,
1208			    .help = "Like mmap, but use huge pages",
1209			  },
1210#endif
1211		  },
1212	},
1213	{
1214		.name	= "iomem_align",
1215		.alias	= "mem_align",
1216		.type	= FIO_OPT_INT,
1217		.off1	= td_var_offset(mem_align),
1218		.minval	= 0,
1219		.help	= "IO memory buffer offset alignment",
1220		.def	= "0",
1221		.parent	= "iomem",
1222	},
1223	{
1224		.name	= "verify",
1225		.type	= FIO_OPT_STR,
1226		.off1	= td_var_offset(verify),
1227		.help	= "Verify data written",
1228		.def	= "0",
1229		.posval = {
1230			  { .ival = "0",
1231			    .oval = VERIFY_NONE,
1232			    .help = "Don't do IO verification",
1233			  },
1234			  { .ival = "md5",
1235			    .oval = VERIFY_MD5,
1236			    .help = "Use md5 checksums for verification",
1237			  },
1238			  { .ival = "crc64",
1239			    .oval = VERIFY_CRC64,
1240			    .help = "Use crc64 checksums for verification",
1241			  },
1242			  { .ival = "crc32",
1243			    .oval = VERIFY_CRC32,
1244			    .help = "Use crc32 checksums for verification",
1245			  },
1246			  { .ival = "crc32c-intel",
1247			    .oval = VERIFY_CRC32C_INTEL,
1248			    .help = "Use hw crc32c checksums for verification",
1249			  },
1250			  { .ival = "crc32c",
1251			    .oval = VERIFY_CRC32C,
1252			    .help = "Use crc32c checksums for verification",
1253			  },
1254			  { .ival = "crc16",
1255			    .oval = VERIFY_CRC16,
1256			    .help = "Use crc16 checksums for verification",
1257			  },
1258			  { .ival = "crc7",
1259			    .oval = VERIFY_CRC7,
1260			    .help = "Use crc7 checksums for verification",
1261			  },
1262			  { .ival = "sha1",
1263			    .oval = VERIFY_SHA1,
1264			    .help = "Use sha1 checksums for verification",
1265			  },
1266			  { .ival = "sha256",
1267			    .oval = VERIFY_SHA256,
1268			    .help = "Use sha256 checksums for verification",
1269			  },
1270			  { .ival = "sha512",
1271			    .oval = VERIFY_SHA512,
1272			    .help = "Use sha512 checksums for verification",
1273			  },
1274			  { .ival = "meta",
1275			    .oval = VERIFY_META,
1276			    .help = "Use io information",
1277			  },
1278			  {
1279			    .ival = "null",
1280			    .oval = VERIFY_NULL,
1281			    .help = "Pretend to verify",
1282			  },
1283		},
1284	},
1285	{
1286		.name	= "do_verify",
1287		.type	= FIO_OPT_BOOL,
1288		.off1	= td_var_offset(do_verify),
1289		.help	= "Run verification stage after write",
1290		.def	= "1",
1291		.parent = "verify",
1292	},
1293	{
1294		.name	= "verifysort",
1295		.type	= FIO_OPT_BOOL,
1296		.off1	= td_var_offset(verifysort),
1297		.help	= "Sort written verify blocks for read back",
1298		.def	= "1",
1299		.parent = "verify",
1300	},
1301	{
1302		.name   = "verify_interval",
1303		.type   = FIO_OPT_INT,
1304		.off1   = td_var_offset(verify_interval),
1305		.minval	= 2 * sizeof(struct verify_header),
1306		.help   = "Store verify buffer header every N bytes",
1307		.parent	= "verify",
1308	},
1309	{
1310		.name	= "verify_offset",
1311		.type	= FIO_OPT_INT,
1312		.help	= "Offset verify header location by N bytes",
1313		.def	= "0",
1314		.cb	= str_verify_offset_cb,
1315		.parent	= "verify",
1316	},
1317	{
1318		.name	= "verify_pattern",
1319		.type	= FIO_OPT_STR,
1320		.cb	= str_verify_pattern_cb,
1321		.help	= "Fill pattern for IO buffers",
1322		.parent	= "verify",
1323	},
1324	{
1325		.name	= "verify_fatal",
1326		.type	= FIO_OPT_BOOL,
1327		.off1	= td_var_offset(verify_fatal),
1328		.def	= "0",
1329		.help	= "Exit on a single verify failure, don't continue",
1330		.parent = "verify",
1331	},
1332	{
1333		.name	= "verify_async",
1334		.type	= FIO_OPT_INT,
1335		.off1	= td_var_offset(verify_async),
1336		.def	= "0",
1337		.help	= "Number of async verifier threads to use",
1338		.parent	= "verify",
1339	},
1340#ifdef FIO_HAVE_CPU_AFFINITY
1341	{
1342		.name	= "verify_async_cpus",
1343		.type	= FIO_OPT_STR,
1344		.cb	= str_verify_cpus_allowed_cb,
1345		.help	= "Set CPUs allowed for async verify threads",
1346		.parent	= "verify_async",
1347	},
1348#endif
1349	{
1350		.name	= "write_iolog",
1351		.type	= FIO_OPT_STR_STORE,
1352		.off1	= td_var_offset(write_iolog_file),
1353		.help	= "Store IO pattern to file",
1354	},
1355	{
1356		.name	= "read_iolog",
1357		.type	= FIO_OPT_STR_STORE,
1358		.off1	= td_var_offset(read_iolog_file),
1359		.help	= "Playback IO pattern from file",
1360	},
1361	{
1362		.name	= "exec_prerun",
1363		.type	= FIO_OPT_STR_STORE,
1364		.off1	= td_var_offset(exec_prerun),
1365		.help	= "Execute this file prior to running job",
1366	},
1367	{
1368		.name	= "exec_postrun",
1369		.type	= FIO_OPT_STR_STORE,
1370		.off1	= td_var_offset(exec_postrun),
1371		.help	= "Execute this file after running job",
1372	},
1373#ifdef FIO_HAVE_IOSCHED_SWITCH
1374	{
1375		.name	= "ioscheduler",
1376		.type	= FIO_OPT_STR_STORE,
1377		.off1	= td_var_offset(ioscheduler),
1378		.help	= "Use this IO scheduler on the backing device",
1379	},
1380#endif
1381	{
1382		.name	= "zonesize",
1383		.type	= FIO_OPT_STR_VAL,
1384		.off1	= td_var_offset(zone_size),
1385		.help	= "Give size of an IO zone",
1386		.def	= "0",
1387	},
1388	{
1389		.name	= "zoneskip",
1390		.type	= FIO_OPT_STR_VAL,
1391		.off1	= td_var_offset(zone_skip),
1392		.help	= "Space between IO zones",
1393		.def	= "0",
1394	},
1395	{
1396		.name	= "lockmem",
1397		.type	= FIO_OPT_STR_VAL,
1398		.cb	= str_lockmem_cb,
1399		.help	= "Lock down this amount of memory",
1400		.def	= "0",
1401	},
1402	{
1403		.name	= "rwmixread",
1404		.type	= FIO_OPT_INT,
1405		.cb	= str_rwmix_read_cb,
1406		.maxval	= 100,
1407		.help	= "Percentage of mixed workload that is reads",
1408		.def	= "50",
1409	},
1410	{
1411		.name	= "rwmixwrite",
1412		.type	= FIO_OPT_INT,
1413		.cb	= str_rwmix_write_cb,
1414		.maxval	= 100,
1415		.help	= "Percentage of mixed workload that is writes",
1416		.def	= "50",
1417	},
1418	{
1419		.name	= "rwmixcycle",
1420		.type	= FIO_OPT_DEPRECATED,
1421	},
1422	{
1423		.name	= "nice",
1424		.type	= FIO_OPT_INT,
1425		.off1	= td_var_offset(nice),
1426		.help	= "Set job CPU nice value",
1427		.minval	= -19,
1428		.maxval	= 20,
1429		.def	= "0",
1430	},
1431#ifdef FIO_HAVE_IOPRIO
1432	{
1433		.name	= "prio",
1434		.type	= FIO_OPT_INT,
1435		.cb	= str_prio_cb,
1436		.help	= "Set job IO priority value",
1437		.minval	= 0,
1438		.maxval	= 7,
1439	},
1440	{
1441		.name	= "prioclass",
1442		.type	= FIO_OPT_INT,
1443		.cb	= str_prioclass_cb,
1444		.help	= "Set job IO priority class",
1445		.minval	= 0,
1446		.maxval	= 3,
1447	},
1448#endif
1449	{
1450		.name	= "thinktime",
1451		.type	= FIO_OPT_INT,
1452		.off1	= td_var_offset(thinktime),
1453		.help	= "Idle time between IO buffers (usec)",
1454		.def	= "0",
1455	},
1456	{
1457		.name	= "thinktime_spin",
1458		.type	= FIO_OPT_INT,
1459		.off1	= td_var_offset(thinktime_spin),
1460		.help	= "Start think time by spinning this amount (usec)",
1461		.def	= "0",
1462		.parent	= "thinktime",
1463	},
1464	{
1465		.name	= "thinktime_blocks",
1466		.type	= FIO_OPT_INT,
1467		.off1	= td_var_offset(thinktime_blocks),
1468		.help	= "IO buffer period between 'thinktime'",
1469		.def	= "1",
1470		.parent	= "thinktime",
1471	},
1472	{
1473		.name	= "rate",
1474		.type	= FIO_OPT_INT,
1475		.off1	= td_var_offset(rate[0]),
1476		.off2	= td_var_offset(rate[1]),
1477		.help	= "Set bandwidth rate",
1478	},
1479	{
1480		.name	= "ratemin",
1481		.type	= FIO_OPT_INT,
1482		.off1	= td_var_offset(ratemin[0]),
1483		.off2	= td_var_offset(ratemin[1]),
1484		.help	= "Job must meet this rate or it will be shutdown",
1485		.parent	= "rate",
1486	},
1487	{
1488		.name	= "rate_iops",
1489		.type	= FIO_OPT_INT,
1490		.off1	= td_var_offset(rate_iops[0]),
1491		.off2	= td_var_offset(rate_iops[1]),
1492		.help	= "Limit IO used to this number of IO operations/sec",
1493	},
1494	{
1495		.name	= "rate_iops_min",
1496		.type	= FIO_OPT_INT,
1497		.off1	= td_var_offset(rate_iops_min[0]),
1498		.off2	= td_var_offset(rate_iops_min[1]),
1499		.help	= "Job must meet this rate or it will be shutdown",
1500		.parent	= "rate_iops",
1501	},
1502	{
1503		.name	= "ratecycle",
1504		.type	= FIO_OPT_INT,
1505		.off1	= td_var_offset(ratecycle),
1506		.help	= "Window average for rate limits (msec)",
1507		.def	= "1000",
1508		.parent = "rate",
1509	},
1510	{
1511		.name	= "invalidate",
1512		.type	= FIO_OPT_BOOL,
1513		.off1	= td_var_offset(invalidate_cache),
1514		.help	= "Invalidate buffer/page cache prior to running job",
1515		.def	= "1",
1516	},
1517	{
1518		.name	= "sync",
1519		.type	= FIO_OPT_BOOL,
1520		.off1	= td_var_offset(sync_io),
1521		.help	= "Use O_SYNC for buffered writes",
1522		.def	= "0",
1523		.parent = "buffered",
1524	},
1525	{
1526		.name	= "bwavgtime",
1527		.type	= FIO_OPT_INT,
1528		.off1	= td_var_offset(bw_avg_time),
1529		.help	= "Time window over which to calculate bandwidth"
1530			  " (msec)",
1531		.def	= "500",
1532	},
1533	{
1534		.name	= "create_serialize",
1535		.type	= FIO_OPT_BOOL,
1536		.off1	= td_var_offset(create_serialize),
1537		.help	= "Serialize creating of job files",
1538		.def	= "1",
1539	},
1540	{
1541		.name	= "create_fsync",
1542		.type	= FIO_OPT_BOOL,
1543		.off1	= td_var_offset(create_fsync),
1544		.help	= "Fsync file after creation",
1545		.def	= "1",
1546	},
1547	{
1548		.name	= "create_on_open",
1549		.type	= FIO_OPT_BOOL,
1550		.off1	= td_var_offset(create_on_open),
1551		.help	= "Create files when they are opened for IO",
1552		.def	= "0",
1553	},
1554	{
1555		.name	= "pre_read",
1556		.type	= FIO_OPT_BOOL,
1557		.off1	= td_var_offset(pre_read),
1558		.help	= "Preread files before starting official testing",
1559		.def	= "0",
1560	},
1561	{
1562		.name	= "cpuload",
1563		.type	= FIO_OPT_INT,
1564		.off1	= td_var_offset(cpuload),
1565		.help	= "Use this percentage of CPU",
1566	},
1567	{
1568		.name	= "cpuchunks",
1569		.type	= FIO_OPT_INT,
1570		.off1	= td_var_offset(cpucycle),
1571		.help	= "Length of the CPU burn cycles (usecs)",
1572		.def	= "50000",
1573		.parent = "cpuload",
1574	},
1575#ifdef FIO_HAVE_CPU_AFFINITY
1576	{
1577		.name	= "cpumask",
1578		.type	= FIO_OPT_INT,
1579		.cb	= str_cpumask_cb,
1580		.help	= "CPU affinity mask",
1581	},
1582	{
1583		.name	= "cpus_allowed",
1584		.type	= FIO_OPT_STR,
1585		.cb	= str_cpus_allowed_cb,
1586		.help	= "Set CPUs allowed",
1587	},
1588#endif
1589	{
1590		.name	= "end_fsync",
1591		.type	= FIO_OPT_BOOL,
1592		.off1	= td_var_offset(end_fsync),
1593		.help	= "Include fsync at the end of job",
1594		.def	= "0",
1595	},
1596	{
1597		.name	= "fsync_on_close",
1598		.type	= FIO_OPT_BOOL,
1599		.off1	= td_var_offset(fsync_on_close),
1600		.help	= "fsync files on close",
1601		.def	= "0",
1602	},
1603	{
1604		.name	= "unlink",
1605		.type	= FIO_OPT_BOOL,
1606		.off1	= td_var_offset(unlink),
1607		.help	= "Unlink created files after job has completed",
1608		.def	= "0",
1609	},
1610	{
1611		.name	= "exitall",
1612		.type	= FIO_OPT_STR_SET,
1613		.cb	= str_exitall_cb,
1614		.help	= "Terminate all jobs when one exits",
1615	},
1616	{
1617		.name	= "stonewall",
1618		.type	= FIO_OPT_STR_SET,
1619		.off1	= td_var_offset(stonewall),
1620		.help	= "Insert a hard barrier between this job and previous",
1621	},
1622	{
1623		.name	= "new_group",
1624		.type	= FIO_OPT_STR_SET,
1625		.off1	= td_var_offset(new_group),
1626		.help	= "Mark the start of a new group (for reporting)",
1627	},
1628	{
1629		.name	= "thread",
1630		.type	= FIO_OPT_STR_SET,
1631		.off1	= td_var_offset(use_thread),
1632		.help	= "Use threads instead of forks",
1633	},
1634	{
1635		.name	= "write_bw_log",
1636		.type	= FIO_OPT_STR,
1637		.off1	= td_var_offset(write_bw_log),
1638		.cb	= str_write_bw_log_cb,
1639		.help	= "Write log of bandwidth during run",
1640	},
1641	{
1642		.name	= "write_lat_log",
1643		.type	= FIO_OPT_STR,
1644		.off1	= td_var_offset(write_lat_log),
1645		.cb	= str_write_lat_log_cb,
1646		.help	= "Write log of latency during run",
1647	},
1648	{
1649		.name	= "hugepage-size",
1650		.type	= FIO_OPT_INT,
1651		.off1	= td_var_offset(hugepage_size),
1652		.help	= "When using hugepages, specify size of each page",
1653		.def	= __stringify(FIO_HUGE_PAGE),
1654	},
1655	{
1656		.name	= "group_reporting",
1657		.type	= FIO_OPT_STR_SET,
1658		.off1	= td_var_offset(group_reporting),
1659		.help	= "Do reporting on a per-group basis",
1660	},
1661	{
1662		.name	= "zero_buffers",
1663		.type	= FIO_OPT_STR_SET,
1664		.off1	= td_var_offset(zero_buffers),
1665		.help	= "Init IO buffers to all zeroes",
1666	},
1667	{
1668		.name	= "refill_buffers",
1669		.type	= FIO_OPT_STR_SET,
1670		.off1	= td_var_offset(refill_buffers),
1671		.help	= "Refill IO buffers on every IO submit",
1672	},
1673#ifdef FIO_HAVE_DISK_UTIL
1674	{
1675		.name	= "disk_util",
1676		.type	= FIO_OPT_BOOL,
1677		.off1	= td_var_offset(do_disk_util),
1678		.help	= "Log disk utilization statistics",
1679		.def	= "1",
1680	},
1681#endif
1682	{
1683		.name	= "gtod_reduce",
1684		.type	= FIO_OPT_BOOL,
1685		.help	= "Greatly reduce number of gettimeofday() calls",
1686		.cb	= str_gtod_reduce_cb,
1687		.def	= "0",
1688	},
1689	{
1690		.name	= "disable_clat",
1691		.type	= FIO_OPT_BOOL,
1692		.off1	= td_var_offset(disable_clat),
1693		.help	= "Disable completion latency numbers",
1694		.parent	= "gtod_reduce",
1695		.def	= "0",
1696	},
1697	{
1698		.name	= "disable_slat",
1699		.type	= FIO_OPT_BOOL,
1700		.off1	= td_var_offset(disable_slat),
1701		.help	= "Disable submissionn latency numbers",
1702		.parent	= "gtod_reduce",
1703		.def	= "0",
1704	},
1705	{
1706		.name	= "disable_bw_measurement",
1707		.type	= FIO_OPT_BOOL,
1708		.off1	= td_var_offset(disable_bw),
1709		.help	= "Disable bandwidth logging",
1710		.parent	= "gtod_reduce",
1711		.def	= "0",
1712	},
1713	{
1714		.name	= "gtod_cpu",
1715		.type	= FIO_OPT_INT,
1716		.cb	= str_gtod_cpu_cb,
1717		.help	= "Setup dedicated gettimeofday() thread on this CPU",
1718		.verify	= gtod_cpu_verify,
1719	},
1720	{
1721		.name	= "continue_on_error",
1722		.type	= FIO_OPT_BOOL,
1723		.off1	= td_var_offset(continue_on_error),
1724		.help	= "Continue on non-fatal errors during I/O",
1725		.def	= "0",
1726	},
1727	{
1728		.name	= "profile",
1729		.type	= FIO_OPT_STR_STORE,
1730		.off1	= td_var_offset(profile),
1731		.help	= "Select a specific builtin performance test",
1732	},
1733	{
1734		.name	= "cgroup",
1735		.type	= FIO_OPT_STR_STORE,
1736		.off1	= td_var_offset(cgroup),
1737		.help	= "Add job to cgroup of this name",
1738	},
1739	{
1740		.name	= "cgroup_weight",
1741		.type	= FIO_OPT_INT,
1742		.off1	= td_var_offset(cgroup_weight),
1743		.help	= "Use given weight for cgroup",
1744		.minval = 100,
1745		.maxval	= 1000,
1746	},
1747	{
1748		.name	= "uid",
1749		.type	= FIO_OPT_INT,
1750		.off1	= td_var_offset(uid),
1751		.help	= "Run job with this user ID",
1752	},
1753	{
1754		.name	= "gid",
1755		.type	= FIO_OPT_INT,
1756		.off1	= td_var_offset(gid),
1757		.help	= "Run job with this group ID",
1758	},
1759	{
1760		.name = NULL,
1761	},
1762};
1763
1764void fio_options_dup_and_init(struct option *long_options)
1765{
1766	struct fio_option *o;
1767	unsigned int i;
1768
1769	options_init(options);
1770
1771	i = 0;
1772	while (long_options[i].name)
1773		i++;
1774
1775	o = &options[0];
1776	while (o->name) {
1777		long_options[i].name = (char *) o->name;
1778		long_options[i].val = FIO_GETOPT_JOB;
1779		if (o->type == FIO_OPT_STR_SET)
1780			long_options[i].has_arg = no_argument;
1781		else
1782			long_options[i].has_arg = required_argument;
1783
1784		i++;
1785		o++;
1786		assert(i < FIO_NR_OPTIONS);
1787	}
1788}
1789
1790struct fio_keyword {
1791	const char *word;
1792	const char *desc;
1793	char *replace;
1794};
1795
1796static struct fio_keyword fio_keywords[] = {
1797	{
1798		.word	= "$pagesize",
1799		.desc	= "Page size in the system",
1800	},
1801	{
1802		.word	= "$mb_memory",
1803		.desc	= "Megabytes of memory online",
1804	},
1805	{
1806		.word	= "$ncpus",
1807		.desc	= "Number of CPUs online in the system",
1808	},
1809	{
1810		.word	= NULL,
1811	},
1812};
1813
1814void fio_keywords_init(void)
1815{
1816	unsigned long long mb_memory;
1817	char buf[128];
1818	long l;
1819
1820	sprintf(buf, "%lu", page_size);
1821	fio_keywords[0].replace = strdup(buf);
1822
1823	mb_memory = os_phys_mem() / page_size;
1824	sprintf(buf, "%llu", mb_memory);
1825	fio_keywords[1].replace = strdup(buf);
1826
1827	l = sysconf(_SC_NPROCESSORS_ONLN);
1828	sprintf(buf, "%lu", l);
1829	fio_keywords[2].replace = strdup(buf);
1830}
1831
1832#define BC_APP		"bc"
1833
1834static char *bc_calc(char *str)
1835{
1836	char *buf, *tmp, opt[80];
1837	FILE *f;
1838	int ret;
1839
1840	/*
1841	 * No math, just return string
1842	 */
1843	if (!strchr(str, '+') && !strchr(str, '-') && !strchr(str, '*') &&
1844	    !strchr(str, '/'))
1845		return str;
1846
1847	/*
1848	 * Split option from value, we only need to calculate the value
1849	 */
1850	tmp = strchr(str, '=');
1851	if (!tmp)
1852		return str;
1853
1854	tmp++;
1855	memset(opt, 0, sizeof(opt));
1856	strncpy(opt, str, tmp - str);
1857
1858	buf = malloc(128);
1859
1860	sprintf(buf, "which %s > /dev/null", BC_APP);
1861	if (system(buf)) {
1862		log_err("fio: bc is needed for performing math\n");
1863		free(buf);
1864		return NULL;
1865	}
1866
1867	sprintf(buf, "echo %s | %s", tmp, BC_APP);
1868	f = popen(buf, "r");
1869	if (!f) {
1870		free(buf);
1871		return NULL;
1872	}
1873
1874	ret = fread(buf, 1, 128, f);
1875	if (ret <= 0) {
1876		free(buf);
1877		return NULL;
1878	}
1879
1880	buf[ret - 1] = '\0';
1881	strcat(opt, buf);
1882	strcpy(buf, opt);
1883	pclose(f);
1884	free(str);
1885	return buf;
1886}
1887
1888/*
1889 * Look for reserved variable names and replace them with real values
1890 */
1891static char *fio_keyword_replace(char *opt)
1892{
1893	char *s;
1894	int i;
1895
1896	for (i = 0; fio_keywords[i].word != NULL; i++) {
1897		struct fio_keyword *kw = &fio_keywords[i];
1898
1899		while ((s = strstr(opt, kw->word)) != NULL) {
1900			char *new = malloc(strlen(opt) + 1);
1901			char *o_org = opt;
1902			int olen = s - opt;
1903			int len;
1904
1905			/*
1906			 * Copy part of the string before the keyword and
1907			 * sprintf() the replacement after it.
1908			 */
1909			memcpy(new, opt, olen);
1910			len = sprintf(new + olen, "%s", kw->replace);
1911
1912			/*
1913			 * If there's more in the original string, copy that
1914			 * in too
1915			 */
1916			opt += strlen(kw->word) + olen;
1917			if (strlen(opt))
1918				memcpy(new + olen + len, opt, opt - o_org - 1);
1919
1920			/*
1921			 * replace opt and free the old opt
1922			 */
1923			opt = new;
1924			//free(o_org);
1925
1926			/*
1927			 * Check for potential math and invoke bc, if possible
1928			 */
1929			opt = bc_calc(opt);
1930		}
1931	}
1932
1933	return opt;
1934}
1935
1936int fio_options_parse(struct thread_data *td, char **opts, int num_opts)
1937{
1938	int i, ret;
1939
1940	sort_options(opts, options, num_opts);
1941
1942	for (ret = 0, i = 0; i < num_opts; i++) {
1943		opts[i] = fio_keyword_replace(opts[i]);
1944		ret |= parse_option(opts[i], options, &ext_opt_list, td);
1945	}
1946
1947	return ret;
1948}
1949
1950int fio_cmd_option_parse(struct thread_data *td, const char *opt, char *val)
1951{
1952	return parse_cmd_option(opt, val, options, &ext_opt_list, td);
1953}
1954
1955void fio_fill_default_options(struct thread_data *td)
1956{
1957	fill_default_options(td, options);
1958}
1959
1960int fio_show_option_help(const char *opt)
1961{
1962	return show_cmd_help(options, opt);
1963}
1964
1965static void __options_mem(struct thread_data *td, int alloc)
1966{
1967	struct thread_options *o = &td->o;
1968	struct fio_option *opt;
1969	char **ptr;
1970	int i;
1971
1972	for (i = 0, opt = &options[0]; opt->name; i++, opt = &options[i]) {
1973		if (opt->type != FIO_OPT_STR_STORE)
1974			continue;
1975
1976		ptr = (void *) o + opt->off1;
1977		if (*ptr) {
1978			if (alloc)
1979				*ptr = strdup(*ptr);
1980			else {
1981				free(*ptr);
1982				*ptr = NULL;
1983			}
1984		}
1985	}
1986}
1987
1988/*
1989 * dupe FIO_OPT_STR_STORE options
1990 */
1991void options_mem_dupe(struct thread_data *td)
1992{
1993	__options_mem(td, 1);
1994}
1995
1996void options_mem_free(struct thread_data fio_unused *td)
1997{
1998#if 0
1999	__options_mem(td, 0);
2000#endif
2001}
2002
2003unsigned int fio_get_kb_base(void *data)
2004{
2005	struct thread_data *td = data;
2006	unsigned int kb_base = 0;
2007
2008	if (td)
2009		kb_base = td->o.kb_base;
2010	if (!kb_base)
2011		kb_base = 1024;
2012
2013	return kb_base;
2014}
2015
2016void register_ext_option(struct ext_option *eopt)
2017{
2018	dprint(FD_PARSE, "register option '%s'\n", eopt->o.name);
2019	option_init(&eopt->o);
2020	flist_add_tail(&eopt->list, &ext_opt_list);
2021}
2022