init.c revision bb8895e07c6d6417410545f45d34b1b7916cd90a
1/*
2 * This file contains job initialization and setup functions.
3 */
4#include <stdio.h>
5#include <stdlib.h>
6#include <unistd.h>
7#include <fcntl.h>
8#include <ctype.h>
9#include <string.h>
10#include <errno.h>
11#include <getopt.h>
12#include <assert.h>
13#include <sys/ipc.h>
14#include <sys/shm.h>
15#include <sys/types.h>
16#include <sys/stat.h>
17
18#include "fio.h"
19#include "parse.h"
20
21/*
22 * The default options
23 */
24#define DEF_BS			(4096)
25#define DEF_TIMEOUT		(0)
26#define DEF_RATE_CYCLE		(1000)
27#define DEF_ODIRECT		(1)
28#define DEF_IO_ENGINE		(FIO_SYNCIO)
29#define DEF_IO_ENGINE_NAME	"sync"
30#define DEF_SEQUENTIAL		(1)
31#define DEF_RAND_REPEAT		(1)
32#define DEF_OVERWRITE		(1)
33#define DEF_INVALIDATE		(1)
34#define DEF_SYNCIO		(0)
35#define DEF_RANDSEED		(0xb1899bedUL)
36#define DEF_BWAVGTIME		(500)
37#define DEF_CREATE_SER		(1)
38#define DEF_CREATE_FSYNC	(1)
39#define DEF_LOOPS		(1)
40#define DEF_VERIFY		(0)
41#define DEF_STONEWALL		(0)
42#define DEF_NUMJOBS		(1)
43#define DEF_USE_THREAD		(0)
44#define DEF_FILE_SIZE		(1024 * 1024 * 1024UL)
45#define DEF_ZONE_SIZE		(0)
46#define DEF_ZONE_SKIP		(0)
47#define DEF_RWMIX_CYCLE		(500)
48#define DEF_RWMIX_READ		(50)
49#define DEF_NICE		(0)
50#define DEF_NR_FILES		(1)
51#define DEF_UNLINK		(0)
52#define DEF_WRITE_BW_LOG	(0)
53#define DEF_WRITE_LAT_LOG	(0)
54#define DEF_NO_RAND_MAP		(0)
55
56#define td_var_offset(var)	((size_t) &((struct thread_data *)0)->var)
57
58static int str_rw_cb(void *, const char *);
59static int str_ioengine_cb(void *, const char *);
60static int str_mem_cb(void *, const char *);
61static int str_verify_cb(void *, const char *);
62static int str_lockmem_cb(void *, unsigned long *);
63static int str_prio_cb(void *, unsigned int *);
64static int str_prioclass_cb(void *, unsigned int *);
65static int str_exitall_cb(void);
66static int str_cpumask_cb(void *, unsigned int *);
67
68/*
69 * Map of job/command line options
70 */
71static struct fio_option options[] = {
72	{
73		.name	= "name",
74		.type	= FIO_OPT_STR_STORE,
75		.off1	= td_var_offset(name),
76	},
77	{
78		.name	= "directory",
79		.type	= FIO_OPT_STR_STORE,
80		.off1	= td_var_offset(directory),
81	},
82	{
83		.name	= "filename",
84		.type	= FIO_OPT_STR_STORE,
85		.off1	= td_var_offset(filename),
86	},
87	{
88		.name	= "rw",
89		.type	= FIO_OPT_STR,
90		.cb	= str_rw_cb,
91	},
92	{
93		.name	= "ioengine",
94		.type	= FIO_OPT_STR,
95		.cb	= str_ioengine_cb,
96	},
97	{
98		.name	= "mem",
99		.type	= FIO_OPT_STR,
100		.cb	= str_mem_cb,
101	},
102	{
103		.name	= "verify",
104		.type	= FIO_OPT_STR,
105		.cb	= str_verify_cb,
106	},
107	{
108		.name	= "write_iolog",
109		.type	= FIO_OPT_STR_STORE,
110		.off1	= td_var_offset(write_iolog_file),
111	},
112	{
113		.name	= "read_iolog",
114		.type	= FIO_OPT_STR_STORE,
115		.off1	= td_var_offset(read_iolog_file),
116	},
117	{
118		.name	= "exec_prerun",
119		.type	= FIO_OPT_STR_STORE,
120		.off1	= td_var_offset(exec_prerun),
121	},
122	{
123		.name	= "exec_postrun",
124		.type	= FIO_OPT_STR_STORE,
125		.off1	= td_var_offset(exec_postrun),
126	},
127#ifdef FIO_HAVE_IOSCHED_SWITCH
128	{
129		.name	= "ioscheduler",
130		.type	= FIO_OPT_STR_STORE,
131		.off1	= td_var_offset(ioscheduler),
132	},
133#endif
134	{
135		.name	= "size",
136		.type	= FIO_OPT_STR_VAL,
137		.off1	= td_var_offset(total_file_size),
138	},
139	{
140		.name	= "bs",
141		.type	= FIO_OPT_STR_VAL,
142		.off1	= td_var_offset(bs),
143	},
144	{
145		.name	= "offset",
146		.type	= FIO_OPT_STR_VAL,
147		.off1	= td_var_offset(start_offset),
148	},
149	{
150		.name	= "zonesize",
151		.type	= FIO_OPT_STR_VAL,
152		.off1	= td_var_offset(zone_size),
153	},
154	{
155		.name	= "zoneskip",
156		.type	= FIO_OPT_STR_VAL,
157		.off1	= td_var_offset(zone_skip),
158	},
159	{
160		.name	= "lockmem",
161		.type	= FIO_OPT_STR_VAL,
162		.cb	= str_lockmem_cb,
163	},
164	{
165		.name	= "bsrange",
166		.type	= FIO_OPT_RANGE,
167		.off1	= td_var_offset(min_bs),
168		.off2	= td_var_offset(max_bs),
169	},
170	{
171		.name	= "nrfiles",
172		.type	= FIO_OPT_INT,
173		.off1	= td_var_offset(nr_files),
174	},
175	{
176		.name	= "iodepth",
177		.type	= FIO_OPT_INT,
178		.off1	= td_var_offset(iodepth),
179	},
180	{
181		.name	= "fsync",
182		.type	= FIO_OPT_INT,
183		.off1	= td_var_offset(fsync_blocks),
184	},
185	{
186		.name	= "rwmixcycle",
187		.type	= FIO_OPT_INT,
188		.off1	= td_var_offset(rwmixcycle),
189	},
190	{
191		.name	= "rwmixread",
192		.type	= FIO_OPT_INT,
193		.off1	= td_var_offset(rwmixread),
194		.max_val= 100,
195	},
196	{
197		.name	= "rwmixwrite",
198		.type	= FIO_OPT_INT,
199		.off1	= td_var_offset(rwmixwrite),
200		.max_val= 100,
201	},
202	{
203		.name	= "nice",
204		.type	= FIO_OPT_INT,
205		.off1	= td_var_offset(nice),
206	},
207#ifdef FIO_HAVE_IOPRIO
208	{
209		.name	= "prio",
210		.type	= FIO_OPT_INT,
211		.cb	= str_prio_cb,
212	},
213	{
214		.name	= "prioclass",
215		.type	= FIO_OPT_INT,
216		.cb	= str_prioclass_cb,
217	},
218#endif
219	{
220		.name	= "thinktime",
221		.type	= FIO_OPT_INT,
222		.off1	= td_var_offset(thinktime)
223	},
224	{
225		.name	= "rate",
226		.type	= FIO_OPT_INT,
227		.off1	= td_var_offset(rate)
228	},
229	{
230		.name	= "ratemin",
231		.type	= FIO_OPT_INT,
232		.off1	= td_var_offset(ratemin)
233	},
234	{
235		.name	= "ratecycle",
236		.type	= FIO_OPT_INT,
237		.off1	= td_var_offset(ratecycle)
238	},
239	{
240		.name	= "startdelay",
241		.type	= FIO_OPT_INT,
242		.off1	= td_var_offset(start_delay)
243	},
244	{
245		.name	= "timeout",
246		.type	= FIO_OPT_STR_VAL_TIME,
247		.off1	= td_var_offset(timeout)
248	},
249	{
250		.name	= "invalidate",
251		.type	= FIO_OPT_INT,
252		.off1	= td_var_offset(invalidate_cache)
253	},
254	{
255		.name	= "sync",
256		.type	= FIO_OPT_INT,
257		.off1	= td_var_offset(sync_io)
258	},
259	{
260		.name	= "bwavgtime",
261		.type	= FIO_OPT_INT,
262		.off1	= td_var_offset(bw_avg_time)
263	},
264	{
265		.name	= "create_serialize",
266		.type	= FIO_OPT_INT,
267		.off1	= td_var_offset(create_serialize)
268	},
269	{
270		.name	= "create_fsync",
271		.type	= FIO_OPT_INT,
272		.off1	= td_var_offset(create_fsync)
273	},
274	{
275		.name	= "loops",
276		.type	= FIO_OPT_INT,
277		.off1	= td_var_offset(loops)
278	},
279	{
280		.name	= "numjobs",
281		.type	= FIO_OPT_INT,
282		.off1	= td_var_offset(numjobs)
283	},
284	{
285		.name	= "cpuload",
286		.type	= FIO_OPT_INT,
287		.off1	= td_var_offset(cpuload)
288	},
289	{
290		.name	= "cpuchunks",
291		.type	= FIO_OPT_INT,
292		.off1	= td_var_offset(cpucycle)
293	},
294	{
295		.name	= "direct",
296		.type	= FIO_OPT_INT,
297		.off1	= td_var_offset(odirect)
298	},
299	{
300		.name	= "overwrite",
301		.type	= FIO_OPT_INT,
302		.off1	= td_var_offset(overwrite)
303	},
304#ifdef FIO_HAVE_CPU_AFFINITY
305	{
306		.name	= "cpumask",
307		.type	= FIO_OPT_INT,
308		.cb	= str_cpumask_cb,
309	},
310#endif
311	{
312		.name	= "end_fsync",
313		.type	= FIO_OPT_INT,
314		.off1	= td_var_offset(end_fsync)
315	},
316	{
317		.name	= "unlink",
318		.type	= FIO_OPT_STR_SET,
319		.off1	= td_var_offset(unlink),
320	},
321	{
322		.name	= "exitall",
323		.type	= FIO_OPT_STR_SET,
324		.cb	= str_exitall_cb,
325	},
326	{
327		.name	= "stonewall",
328		.type	= FIO_OPT_STR_SET,
329		.off1	= td_var_offset(stonewall),
330	},
331	{
332		.name	= "thread",
333		.type	= FIO_OPT_STR_SET,
334		.off1	= td_var_offset(thread),
335	},
336	{
337		.name	= "write_bw_log",
338		.type	= FIO_OPT_STR_SET,
339		.off1	= td_var_offset(write_bw_log),
340	},
341	{
342		.name	= "write_lat_log",
343		.type	= FIO_OPT_STR_SET,
344		.off1	= td_var_offset(write_lat_log),
345	},
346	{
347		.name	= "norandommap",
348		.type	= FIO_OPT_STR_SET,
349		.off1	= td_var_offset(norandommap),
350	},
351	{
352		.name = NULL,
353	},
354};
355
356#define FIO_JOB_OPTS	(sizeof(options) / sizeof(struct fio_option))
357#define FIO_CMD_OPTS	(16)
358#define FIO_GETOPT_JOB	(0x89988998)
359
360/*
361 * Command line options. These will contain the above, plus a few
362 * extra that only pertain to fio itself and not jobs.
363 */
364static struct option long_options[FIO_JOB_OPTS + FIO_CMD_OPTS] = {
365	{
366		.name		= "output",
367		.has_arg	= required_argument,
368		.val		= 'o',
369	},
370	{
371		.name		= "timeout",
372		.has_arg	= required_argument,
373		.val		= 't',
374	},
375	{
376		.name		= "latency-log",
377		.has_arg	= required_argument,
378		.val		= 'l',
379	},
380	{
381		.name		= "bandwidth-log",
382		.has_arg	= required_argument,
383		.val		= 'b',
384	},
385	{
386		.name		= "minimal",
387		.has_arg	= optional_argument,
388		.val		= 'm',
389	},
390	{
391		.name		= "version",
392		.has_arg	= no_argument,
393		.val		= 'v',
394	},
395	{
396		.name		= NULL,
397	},
398};
399
400static int def_timeout = DEF_TIMEOUT;
401
402static char fio_version_string[] = "fio 1.7";
403
404static char **ini_file;
405static int max_jobs = MAX_JOBS;
406
407struct thread_data def_thread;
408struct thread_data *threads = NULL;
409
410int rate_quit = 0;
411int exitall_on_terminate = 0;
412int terse_output = 0;
413unsigned long long mlock_size = 0;
414FILE *f_out = NULL;
415FILE *f_err = NULL;
416
417static int write_lat_log = DEF_WRITE_LAT_LOG;
418static int write_bw_log = DEF_WRITE_BW_LOG;
419
420/*
421 * Return a free job structure.
422 */
423static struct thread_data *get_new_job(int global, struct thread_data *parent)
424{
425	struct thread_data *td;
426
427	if (global)
428		return &def_thread;
429	if (thread_number >= max_jobs)
430		return NULL;
431
432	td = &threads[thread_number++];
433	*td = *parent;
434
435	td->thread_number = thread_number;
436	return td;
437}
438
439static void put_job(struct thread_data *td)
440{
441	if (td == &def_thread)
442		return;
443
444	memset(&threads[td->thread_number - 1], 0, sizeof(*td));
445	thread_number--;
446}
447
448/*
449 * Lazy way of fixing up options that depend on each other. We could also
450 * define option callback handlers, but this is easier.
451 */
452static void fixup_options(struct thread_data *td)
453{
454	if (!td->rwmixread && td->rwmixwrite)
455		td->rwmixread = 100 - td->rwmixwrite;
456
457	if (td->write_iolog_file && td->read_iolog_file) {
458		log_err("fio: read iolog overrides write_iolog\n");
459		free(td->write_iolog_file);
460		td->write_iolog_file = NULL;
461	}
462
463	if (td->io_ops->flags & FIO_SYNCIO)
464		td->iodepth = 1;
465	else {
466		if (!td->iodepth)
467			td->iodepth = td->nr_files;
468	}
469
470	/*
471	 * only really works for sequential io for now, and with 1 file
472	 */
473	if (td->zone_size && !td->sequential && td->nr_files == 1)
474		td->zone_size = 0;
475
476	/*
477	 * Reads can do overwrites, we always need to pre-create the file
478	 */
479	if (td_read(td) || td_rw(td))
480		td->overwrite = 1;
481
482	if (!td->min_bs)
483		td->min_bs = td->bs;
484	if (!td->max_bs)
485		td->max_bs = td->bs;
486	if (td_read(td) && !td_rw(td))
487		td->verify = 0;
488
489	if (td->norandommap && td->verify != VERIFY_NONE) {
490		log_err("fio: norandommap given, verify disabled\n");
491		td->verify = VERIFY_NONE;
492	}
493}
494
495/*
496 * Adds a job to the list of things todo. Sanitizes the various options
497 * to make sure we don't have conflicts, and initializes various
498 * members of td.
499 */
500static int add_job(struct thread_data *td, const char *jobname, int job_add_num)
501{
502	char *ddir_str[] = { "read", "write", "randread", "randwrite",
503			     "rw", NULL, "randrw" };
504	struct stat sb;
505	int numjobs, ddir, i;
506	struct fio_file *f;
507
508#ifndef FIO_HAVE_LIBAIO
509	if (td->io_engine == FIO_LIBAIO) {
510		log_err("Linux libaio not available\n");
511		return 1;
512	}
513#endif
514#ifndef FIO_HAVE_POSIXAIO
515	if (td->io_engine == FIO_POSIXAIO) {
516		log_err("posix aio not available\n");
517		return 1;
518	}
519#endif
520
521	/*
522	 * the def_thread is just for options, it's not a real job
523	 */
524	if (td == &def_thread)
525		return 0;
526
527	/*
528	 * Set default io engine, if none set
529	 */
530	if (!td->io_ops) {
531		td->io_ops = load_ioengine(td, DEF_IO_ENGINE_NAME);
532		if (!td->io_ops) {
533			log_err("default engine %s not there?\n", DEF_IO_ENGINE_NAME);
534			return 1;
535		}
536	}
537
538	fixup_options(td);
539
540	td->filetype = FIO_TYPE_FILE;
541	if (!stat(jobname, &sb)) {
542		if (S_ISBLK(sb.st_mode))
543			td->filetype = FIO_TYPE_BD;
544		else if (S_ISCHR(sb.st_mode))
545			td->filetype = FIO_TYPE_CHAR;
546	}
547
548	if (td->odirect)
549		td->io_ops->flags |= FIO_RAWIO;
550
551	if (td->filename)
552		td->nr_uniq_files = 1;
553	else
554		td->nr_uniq_files = td->nr_files;
555
556	if (td->filetype == FIO_TYPE_FILE || td->filename) {
557		char tmp[PATH_MAX];
558		int len = 0;
559		int i;
560
561		if (td->directory && td->directory[0] != '\0')
562			sprintf(tmp, "%s/", td->directory);
563
564		td->files = malloc(sizeof(struct fio_file) * td->nr_files);
565
566		for_each_file(td, f, i) {
567			memset(f, 0, sizeof(*f));
568			f->fd = -1;
569
570			if (td->filename)
571				sprintf(tmp + len, "%s", td->filename);
572			else
573				sprintf(tmp + len, "%s.%d.%d", jobname, td->thread_number, i);
574			f->file_name = strdup(tmp);
575		}
576	} else {
577		td->nr_files = 1;
578		td->files = malloc(sizeof(struct fio_file));
579		f = &td->files[0];
580
581		memset(f, 0, sizeof(*f));
582		f->fd = -1;
583		f->file_name = strdup(jobname);
584	}
585
586	for_each_file(td, f, i) {
587		f->file_size = td->total_file_size / td->nr_files;
588		f->file_offset = td->start_offset;
589	}
590
591	fio_sem_init(&td->mutex, 0);
592
593	td->clat_stat[0].min_val = td->clat_stat[1].min_val = ULONG_MAX;
594	td->slat_stat[0].min_val = td->slat_stat[1].min_val = ULONG_MAX;
595	td->bw_stat[0].min_val = td->bw_stat[1].min_val = ULONG_MAX;
596
597	if (td->stonewall && td->thread_number > 1)
598		groupid++;
599
600	td->groupid = groupid;
601
602	if (setup_rate(td))
603		goto err;
604
605	if (td->write_lat_log) {
606		setup_log(&td->slat_log);
607		setup_log(&td->clat_log);
608	}
609	if (td->write_bw_log)
610		setup_log(&td->bw_log);
611
612	if (!td->name)
613		td->name = strdup(jobname);
614
615	ddir = td->ddir + (!td->sequential << 1) + (td->iomix << 2);
616
617	if (!terse_output) {
618		if (!job_add_num) {
619			if (td->io_ops->flags & FIO_CPUIO)
620				fprintf(f_out, "%s: ioengine=cpu, cpuload=%u, cpucycle=%u\n", td->name, td->cpuload, td->cpucycle);
621			else
622				fprintf(f_out, "%s: (g=%d): rw=%s, odir=%d, bs=%d-%d, rate=%d, ioengine=%s, iodepth=%d\n", td->name, td->groupid, ddir_str[ddir], td->odirect, td->min_bs, td->max_bs, td->rate, td->io_ops->name, td->iodepth);
623		} else if (job_add_num == 1)
624			fprintf(f_out, "...\n");
625	}
626
627	/*
628	 * recurse add identical jobs, clear numjobs and stonewall options
629	 * as they don't apply to sub-jobs
630	 */
631	numjobs = td->numjobs;
632	while (--numjobs) {
633		struct thread_data *td_new = get_new_job(0, td);
634
635		if (!td_new)
636			goto err;
637
638		td_new->numjobs = 1;
639		td_new->stonewall = 0;
640		job_add_num = numjobs - 1;
641
642		if (add_job(td_new, jobname, job_add_num))
643			goto err;
644	}
645	return 0;
646err:
647	put_job(td);
648	return -1;
649}
650
651/*
652 * Initialize the various random states we need (random io, block size ranges,
653 * read/write mix, etc).
654 */
655int init_random_state(struct thread_data *td)
656{
657	unsigned long seeds[4];
658	int fd, num_maps, blocks, i;
659	struct fio_file *f;
660
661	if (td->io_ops->flags & FIO_CPUIO)
662		return 0;
663
664	fd = open("/dev/urandom", O_RDONLY);
665	if (fd == -1) {
666		td_verror(td, errno);
667		return 1;
668	}
669
670	if (read(fd, seeds, sizeof(seeds)) < (int) sizeof(seeds)) {
671		td_verror(td, EIO);
672		close(fd);
673		return 1;
674	}
675
676	close(fd);
677
678	os_random_seed(seeds[0], &td->bsrange_state);
679	os_random_seed(seeds[1], &td->verify_state);
680	os_random_seed(seeds[2], &td->rwmix_state);
681
682	if (td->sequential)
683		return 0;
684
685	if (td->rand_repeatable)
686		seeds[3] = DEF_RANDSEED;
687
688	if (!td->norandommap) {
689		for_each_file(td, f, i) {
690			blocks = (f->file_size + td->min_bs - 1) / td->min_bs;
691			num_maps = blocks / BLOCKS_PER_MAP;
692			f->file_map = malloc(num_maps * sizeof(long));
693			f->num_maps = num_maps;
694			memset(f->file_map, 0, num_maps * sizeof(long));
695		}
696	}
697
698	os_random_seed(seeds[3], &td->random_state);
699	return 0;
700}
701
702static void fill_cpu_mask(os_cpu_mask_t cpumask, int cpu)
703{
704#ifdef FIO_HAVE_CPU_AFFINITY
705	unsigned int i;
706
707	CPU_ZERO(&cpumask);
708
709	for (i = 0; i < sizeof(int) * 8; i++) {
710		if ((1 << i) & cpu)
711			CPU_SET(i, &cpumask);
712	}
713#endif
714}
715
716static int is_empty_or_comment(char *line)
717{
718	unsigned int i;
719
720	for (i = 0; i < strlen(line); i++) {
721		if (line[i] == ';')
722			return 1;
723		if (!isspace(line[i]) && !iscntrl(line[i]))
724			return 0;
725	}
726
727	return 1;
728}
729
730static int str_rw_cb(void *data, const char *mem)
731{
732	struct thread_data *td = data;
733
734	if (!strncmp(mem, "read", 4) || !strncmp(mem, "0", 1)) {
735		td->ddir = DDIR_READ;
736		td->sequential = 1;
737		return 0;
738	} else if (!strncmp(mem, "randread", 8)) {
739		td->ddir = DDIR_READ;
740		td->sequential = 0;
741		return 0;
742	} else if (!strncmp(mem, "write", 5) || !strncmp(mem, "1", 1)) {
743		td->ddir = DDIR_WRITE;
744		td->sequential = 1;
745		return 0;
746	} else if (!strncmp(mem, "randwrite", 9)) {
747		td->ddir = DDIR_WRITE;
748		td->sequential = 0;
749		return 0;
750	} else if (!strncmp(mem, "rw", 2)) {
751		td->ddir = 0;
752		td->iomix = 1;
753		td->sequential = 1;
754		return 0;
755	} else if (!strncmp(mem, "randrw", 6)) {
756		td->ddir = 0;
757		td->iomix = 1;
758		td->sequential = 0;
759		return 0;
760	}
761
762	log_err("fio: data direction: read, write, randread, randwrite, rw, randrw\n");
763	return 1;
764}
765
766static int str_verify_cb(void *data, const char *mem)
767{
768	struct thread_data *td = data;
769
770	if (!strncmp(mem, "0", 1)) {
771		td->verify = VERIFY_NONE;
772		return 0;
773	} else if (!strncmp(mem, "md5", 3) || !strncmp(mem, "1", 1)) {
774		td->verify = VERIFY_MD5;
775		return 0;
776	} else if (!strncmp(mem, "crc32", 5)) {
777		td->verify = VERIFY_CRC32;
778		return 0;
779	}
780
781	log_err("fio: verify types: md5, crc32\n");
782	return 1;
783}
784
785static int str_mem_cb(void *data, const char *mem)
786{
787	struct thread_data *td = data;
788
789	if (!strncmp(mem, "malloc", 6)) {
790		td->mem_type = MEM_MALLOC;
791		return 0;
792	} else if (!strncmp(mem, "shm", 3)) {
793		td->mem_type = MEM_SHM;
794		return 0;
795	} else if (!strncmp(mem, "mmap", 4)) {
796		td->mem_type = MEM_MMAP;
797		return 0;
798	}
799
800	log_err("fio: mem type: malloc, shm, mmap\n");
801	return 1;
802}
803
804static int str_ioengine_cb(void *data, const char *str)
805{
806	struct thread_data *td = data;
807
808	td->io_ops = load_ioengine(td, str);
809	if (td->io_ops)
810		return 0;
811
812	log_err("fio: ioengine: { linuxaio, aio, libaio }, posixaio, sync, mmap, sgio, splice, cpu\n");
813	return 1;
814}
815
816static int str_lockmem_cb(void fio_unused *data, unsigned long *val)
817{
818	mlock_size = *val;
819	return 0;
820}
821
822static int str_prioclass_cb(void *data, unsigned int *val)
823{
824	struct thread_data *td = data;
825
826	td->ioprio |= *val << IOPRIO_CLASS_SHIFT;
827	return 0;
828}
829
830static int str_prio_cb(void *data, unsigned int *val)
831{
832	struct thread_data *td = data;
833
834	td->ioprio |= *val;
835	return 0;
836}
837
838static int str_exitall_cb(void)
839{
840	exitall_on_terminate = 1;
841	return 0;
842}
843
844static int str_cpumask_cb(void *data, unsigned int *val)
845{
846	struct thread_data *td = data;
847
848	fill_cpu_mask(td->cpumask, *val);
849	return 0;
850}
851
852/*
853 * This is our [ini] type file parser.
854 */
855int parse_jobs_ini(char *file, int stonewall_flag)
856{
857	unsigned int global;
858	struct thread_data *td;
859	char *string, *name;
860	fpos_t off;
861	FILE *f;
862	char *p;
863	int ret = 0, stonewall;
864
865	f = fopen(file, "r");
866	if (!f) {
867		perror("fopen job file");
868		return 1;
869	}
870
871	string = malloc(4096);
872	name = malloc(256);
873	memset(name, 0, 256);
874
875	stonewall = stonewall_flag;
876	do {
877		p = fgets(string, 4095, f);
878		if (!p)
879			break;
880		if (is_empty_or_comment(p))
881			continue;
882		if (sscanf(p, "[%255s]", name) != 1)
883			continue;
884
885		global = !strncmp(name, "global", 6);
886
887		name[strlen(name) - 1] = '\0';
888
889		td = get_new_job(global, &def_thread);
890		if (!td) {
891			ret = 1;
892			break;
893		}
894
895		/*
896		 * Seperate multiple job files by a stonewall
897		 */
898		if (!global && stonewall) {
899			td->stonewall = stonewall;
900			stonewall = 0;
901		}
902
903		fgetpos(f, &off);
904		while ((p = fgets(string, 4096, f)) != NULL) {
905			if (is_empty_or_comment(p))
906				continue;
907
908			strip_blank_front(&p);
909
910			if (p[0] == '[')
911				break;
912
913			strip_blank_end(p);
914
915			fgetpos(f, &off);
916
917			/*
918			 * Don't break here, continue parsing options so we
919			 * dump all the bad ones. Makes trial/error fixups
920			 * easier on the user.
921			 */
922			ret |= parse_option(p, options, td);
923		}
924
925		if (!ret) {
926			fsetpos(f, &off);
927			ret = add_job(td, name, 0);
928		}
929	} while (!ret);
930
931	free(string);
932	free(name);
933	fclose(f);
934	return ret;
935}
936
937static int fill_def_thread(void)
938{
939	memset(&def_thread, 0, sizeof(def_thread));
940
941	if (fio_getaffinity(getpid(), &def_thread.cpumask) == -1) {
942		perror("sched_getaffinity");
943		return 1;
944	}
945
946	/*
947	 * fill globals
948	 */
949	def_thread.ddir = DDIR_READ;
950	def_thread.iomix = 0;
951	def_thread.bs = DEF_BS;
952	def_thread.min_bs = 0;
953	def_thread.max_bs = 0;
954	def_thread.odirect = DEF_ODIRECT;
955	def_thread.ratecycle = DEF_RATE_CYCLE;
956	def_thread.sequential = DEF_SEQUENTIAL;
957	def_thread.timeout = def_timeout;
958	def_thread.overwrite = DEF_OVERWRITE;
959	def_thread.invalidate_cache = DEF_INVALIDATE;
960	def_thread.sync_io = DEF_SYNCIO;
961	def_thread.mem_type = MEM_MALLOC;
962	def_thread.bw_avg_time = DEF_BWAVGTIME;
963	def_thread.create_serialize = DEF_CREATE_SER;
964	def_thread.create_fsync = DEF_CREATE_FSYNC;
965	def_thread.loops = DEF_LOOPS;
966	def_thread.verify = DEF_VERIFY;
967	def_thread.stonewall = DEF_STONEWALL;
968	def_thread.numjobs = DEF_NUMJOBS;
969	def_thread.use_thread = DEF_USE_THREAD;
970	def_thread.rwmixcycle = DEF_RWMIX_CYCLE;
971	def_thread.rwmixread = DEF_RWMIX_READ;
972	def_thread.nice = DEF_NICE;
973	def_thread.rand_repeatable = DEF_RAND_REPEAT;
974	def_thread.nr_files = DEF_NR_FILES;
975	def_thread.unlink = DEF_UNLINK;
976	def_thread.write_bw_log = write_bw_log;
977	def_thread.write_lat_log = write_lat_log;
978	def_thread.norandommap = DEF_NO_RAND_MAP;
979#ifdef FIO_HAVE_DISK_UTIL
980	def_thread.do_disk_util = 1;
981#endif
982
983	return 0;
984}
985
986static void usage(void)
987{
988	printf("%s\n", fio_version_string);
989	printf("\t--output\tWrite output to file\n");
990	printf("\t--timeout\tRuntime in seconds\n");
991	printf("\t--latency-log\tGenerate per-job latency logs\n");
992	printf("\t--bandwidth-log\tGenerate per-job bandwidth logs\n");
993	printf("\t--minimal\tMinimal (terse) output\n");
994	printf("\t--version\tPrint version info and exit\n");
995}
996
997static int parse_cmd_line(int argc, char *argv[])
998{
999	struct thread_data *td = NULL;
1000	int c, ini_idx = 0, lidx, ret;
1001
1002	while ((c = getopt_long(argc, argv, "", long_options, &lidx)) != -1) {
1003		switch (c) {
1004		case 't':
1005			def_timeout = atoi(optarg);
1006			break;
1007		case 'l':
1008			write_lat_log = 1;
1009			break;
1010		case 'w':
1011			write_bw_log = 1;
1012			break;
1013		case 'o':
1014			f_out = fopen(optarg, "w+");
1015			if (!f_out) {
1016				perror("fopen output");
1017				exit(1);
1018			}
1019			f_err = f_out;
1020			break;
1021		case 'm':
1022			terse_output = 1;
1023			break;
1024		case 'h':
1025			usage();
1026			exit(0);
1027		case 'v':
1028			printf("%s\n", fio_version_string);
1029			exit(0);
1030		case FIO_GETOPT_JOB: {
1031			const char *opt = long_options[lidx].name;
1032			char *val = optarg;
1033
1034			if (!strncmp(opt, "name", 4) && td) {
1035				ret = add_job(td, td->name ?: "fio", 0);
1036				if (ret) {
1037					put_job(td);
1038					return 0;
1039				}
1040				td = NULL;
1041			}
1042			if (!td) {
1043				int global = !strncmp(val, "global", 6);
1044
1045				td = get_new_job(global, &def_thread);
1046				if (!td)
1047					return 0;
1048			}
1049
1050			parse_cmd_option(opt, val, options, td);
1051			break;
1052		}
1053		default:
1054			printf("optarg <<%s>>\n", argv[optind]);
1055			break;
1056		}
1057	}
1058
1059	if (td) {
1060		ret = add_job(td, td->name ?: "fio", 0);
1061		if (ret)
1062			put_job(td);
1063	}
1064
1065	while (optind < argc) {
1066		ini_idx++;
1067		ini_file = realloc(ini_file, ini_idx * sizeof(char *));
1068		ini_file[ini_idx - 1] = strdup(argv[optind]);
1069		optind++;
1070	}
1071
1072	return ini_idx;
1073}
1074
1075static void free_shm(void)
1076{
1077	struct shmid_ds sbuf;
1078
1079	if (threads) {
1080		shmdt((void *) threads);
1081		threads = NULL;
1082		shmctl(shm_id, IPC_RMID, &sbuf);
1083	}
1084}
1085
1086/*
1087 * The thread area is shared between the main process and the job
1088 * threads/processes. So setup a shared memory segment that will hold
1089 * all the job info.
1090 */
1091static int setup_thread_area(void)
1092{
1093	/*
1094	 * 1024 is too much on some machines, scale max_jobs if
1095	 * we get a failure that looks like too large a shm segment
1096	 */
1097	do {
1098		size_t size = max_jobs * sizeof(struct thread_data);
1099
1100		shm_id = shmget(0, size, IPC_CREAT | 0600);
1101		if (shm_id != -1)
1102			break;
1103		if (errno != EINVAL) {
1104			perror("shmget");
1105			break;
1106		}
1107
1108		max_jobs >>= 1;
1109	} while (max_jobs);
1110
1111	if (shm_id == -1)
1112		return 1;
1113
1114	threads = shmat(shm_id, NULL, 0);
1115	if (threads == (void *) -1) {
1116		perror("shmat");
1117		return 1;
1118	}
1119
1120	atexit(free_shm);
1121	return 0;
1122}
1123
1124/*
1125 * Copy the fio options into the long options map, so we mirror
1126 * job and cmd line options.
1127 */
1128static void dupe_job_options(void)
1129{
1130	struct fio_option *o;
1131	unsigned int i;
1132
1133	i = 0;
1134	while (long_options[i].name)
1135		i++;
1136
1137	o = &options[0];
1138	while (o->name) {
1139		long_options[i].name = o->name;
1140		long_options[i].val = FIO_GETOPT_JOB;
1141		if (o->type == FIO_OPT_STR_SET)
1142			long_options[i].has_arg = no_argument;
1143		else
1144			long_options[i].has_arg = required_argument;
1145
1146		i++;
1147		o++;
1148		assert(i < FIO_JOB_OPTS + FIO_CMD_OPTS);
1149	}
1150}
1151
1152int parse_options(int argc, char *argv[])
1153{
1154	int job_files, i;
1155
1156	f_out = stdout;
1157	f_err = stderr;
1158
1159	dupe_job_options();
1160
1161	if (setup_thread_area())
1162		return 1;
1163	if (fill_def_thread())
1164		return 1;
1165
1166	job_files = parse_cmd_line(argc, argv);
1167
1168	for (i = 0; i < job_files; i++) {
1169		if (fill_def_thread())
1170			return 1;
1171		if (parse_jobs_ini(ini_file[i], i))
1172			return 1;
1173		free(ini_file[i]);
1174	}
1175
1176	free(ini_file);
1177
1178	if (!thread_number) {
1179		log_err("No jobs defined(s)\n");
1180		usage();
1181		return 1;
1182	}
1183
1184	return 0;
1185}
1186