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