init.c revision fee3bb48f748afe5cf10f8505e1efd36b87c627e
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	if (td == &def_thread)
436		return;
437
438	memset(&threads[td->thread_number - 1], 0, sizeof(*td));
439	thread_number--;
440}
441
442/*
443 * Lazy way of fixing up options that depend on each other. We could also
444 * define option callback handlers, but this is easier.
445 */
446static void fixup_options(struct thread_data *td)
447{
448	if (!td->rwmixread && td->rwmixwrite)
449		td->rwmixread = 100 - td->rwmixwrite;
450
451	if (td->write_iolog_file && td->read_iolog_file) {
452		log_err("fio: read iolog overrides write_iolog\n");
453		free(td->write_iolog_file);
454		td->write_iolog_file = NULL;
455	}
456
457	if (td->io_ops->flags & FIO_SYNCIO)
458		td->iodepth = 1;
459	else {
460		if (!td->iodepth)
461			td->iodepth = td->nr_files;
462	}
463
464	/*
465	 * only really works for sequential io for now, and with 1 file
466	 */
467	if (td->zone_size && !td->sequential && td->nr_files == 1)
468		td->zone_size = 0;
469
470	/*
471	 * Reads can do overwrites, we always need to pre-create the file
472	 */
473	if (td_read(td) || td_rw(td))
474		td->overwrite = 1;
475
476	if (!td->min_bs)
477		td->min_bs = td->bs;
478	if (!td->max_bs)
479		td->max_bs = td->bs;
480	if (td_read(td) && !td_rw(td))
481		td->verify = 0;
482}
483
484/*
485 * Adds a job to the list of things todo. Sanitizes the various options
486 * to make sure we don't have conflicts, and initializes various
487 * members of td.
488 */
489static int add_job(struct thread_data *td, const char *jobname, int job_add_num)
490{
491	char *ddir_str[] = { "read", "write", "randread", "randwrite",
492			     "rw", NULL, "randrw" };
493	struct stat sb;
494	int numjobs, ddir, i;
495	struct fio_file *f;
496
497#ifndef FIO_HAVE_LIBAIO
498	if (td->io_engine == FIO_LIBAIO) {
499		log_err("Linux libaio not available\n");
500		return 1;
501	}
502#endif
503#ifndef FIO_HAVE_POSIXAIO
504	if (td->io_engine == FIO_POSIXAIO) {
505		log_err("posix aio not available\n");
506		return 1;
507	}
508#endif
509
510	/*
511	 * the def_thread is just for options, it's not a real job
512	 */
513	if (td == &def_thread)
514		return 0;
515
516	/*
517	 * Set default io engine, if none set
518	 */
519	if (!td->io_ops) {
520		td->io_ops = load_ioengine(td, DEF_IO_ENGINE_NAME);
521		if (!td->io_ops) {
522			log_err("default engine %s not there?\n", DEF_IO_ENGINE_NAME);
523			return 1;
524		}
525	}
526
527	fixup_options(td);
528
529	td->filetype = FIO_TYPE_FILE;
530	if (!stat(jobname, &sb)) {
531		if (S_ISBLK(sb.st_mode))
532			td->filetype = FIO_TYPE_BD;
533		else if (S_ISCHR(sb.st_mode))
534			td->filetype = FIO_TYPE_CHAR;
535	}
536
537	if (td->odirect)
538		td->io_ops->flags |= FIO_RAWIO;
539
540	if (td->filename)
541		td->nr_uniq_files = 1;
542	else
543		td->nr_uniq_files = td->nr_files;
544
545	if (td->filetype == FIO_TYPE_FILE || td->filename) {
546		char tmp[PATH_MAX];
547		int len = 0;
548		int i;
549
550		if (td->directory && td->directory[0] != '\0')
551			sprintf(tmp, "%s/", td->directory);
552
553		td->files = malloc(sizeof(struct fio_file) * td->nr_files);
554
555		for_each_file(td, f, i) {
556			memset(f, 0, sizeof(*f));
557			f->fd = -1;
558
559			if (td->filename)
560				sprintf(tmp + len, "%s", td->filename);
561			else
562				sprintf(tmp + len, "%s.%d.%d", jobname, td->thread_number, i);
563			f->file_name = strdup(tmp);
564		}
565	} else {
566		td->nr_files = 1;
567		td->files = malloc(sizeof(struct fio_file));
568		f = &td->files[0];
569
570		memset(f, 0, sizeof(*f));
571		f->fd = -1;
572		f->file_name = strdup(jobname);
573	}
574
575	for_each_file(td, f, i) {
576		f->file_size = td->total_file_size / td->nr_files;
577		f->file_offset = td->start_offset;
578	}
579
580	fio_sem_init(&td->mutex, 0);
581
582	td->clat_stat[0].min_val = td->clat_stat[1].min_val = ULONG_MAX;
583	td->slat_stat[0].min_val = td->slat_stat[1].min_val = ULONG_MAX;
584	td->bw_stat[0].min_val = td->bw_stat[1].min_val = ULONG_MAX;
585
586	if (td->stonewall && td->thread_number > 1)
587		groupid++;
588
589	td->groupid = groupid;
590
591	if (setup_rate(td))
592		goto err;
593
594	if (td->write_lat_log) {
595		setup_log(&td->slat_log);
596		setup_log(&td->clat_log);
597	}
598	if (td->write_bw_log)
599		setup_log(&td->bw_log);
600
601	if (!td->name)
602		td->name = strdup(jobname);
603
604	ddir = td->ddir + (!td->sequential << 1) + (td->iomix << 2);
605
606	if (!terse_output) {
607		if (!job_add_num) {
608			if (td->io_ops->flags & FIO_CPUIO)
609				fprintf(f_out, "%s: ioengine=cpu, cpuload=%u, cpucycle=%u\n", td->name, td->cpuload, td->cpucycle);
610			else
611				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);
612		} else if (job_add_num == 1)
613			fprintf(f_out, "...\n");
614	}
615
616	/*
617	 * recurse add identical jobs, clear numjobs and stonewall options
618	 * as they don't apply to sub-jobs
619	 */
620	numjobs = td->numjobs;
621	while (--numjobs) {
622		struct thread_data *td_new = get_new_job(0, td);
623
624		if (!td_new)
625			goto err;
626
627		td_new->numjobs = 1;
628		td_new->stonewall = 0;
629		job_add_num = numjobs - 1;
630
631		if (add_job(td_new, jobname, job_add_num))
632			goto err;
633	}
634	return 0;
635err:
636	put_job(td);
637	return -1;
638}
639
640/*
641 * Initialize the various random states we need (random io, block size ranges,
642 * read/write mix, etc).
643 */
644int init_random_state(struct thread_data *td)
645{
646	unsigned long seeds[4];
647	int fd, num_maps, blocks, i;
648	struct fio_file *f;
649
650	if (td->io_ops->flags & FIO_CPUIO)
651		return 0;
652
653	fd = open("/dev/urandom", O_RDONLY);
654	if (fd == -1) {
655		td_verror(td, errno);
656		return 1;
657	}
658
659	if (read(fd, seeds, sizeof(seeds)) < (int) sizeof(seeds)) {
660		td_verror(td, EIO);
661		close(fd);
662		return 1;
663	}
664
665	close(fd);
666
667	os_random_seed(seeds[0], &td->bsrange_state);
668	os_random_seed(seeds[1], &td->verify_state);
669	os_random_seed(seeds[2], &td->rwmix_state);
670
671	if (td->sequential)
672		return 0;
673
674	if (td->rand_repeatable)
675		seeds[3] = DEF_RANDSEED;
676
677	for_each_file(td, f, i) {
678		blocks = (f->file_size + td->min_bs - 1) / td->min_bs;
679		num_maps = blocks / BLOCKS_PER_MAP;
680		f->file_map = malloc(num_maps * sizeof(long));
681		f->num_maps = num_maps;
682		memset(f->file_map, 0, num_maps * sizeof(long));
683	}
684
685	os_random_seed(seeds[3], &td->random_state);
686	return 0;
687}
688
689static void fill_cpu_mask(os_cpu_mask_t cpumask, int cpu)
690{
691#ifdef FIO_HAVE_CPU_AFFINITY
692	unsigned int i;
693
694	CPU_ZERO(&cpumask);
695
696	for (i = 0; i < sizeof(int) * 8; i++) {
697		if ((1 << i) & cpu)
698			CPU_SET(i, &cpumask);
699	}
700#endif
701}
702
703static int is_empty_or_comment(char *line)
704{
705	unsigned int i;
706
707	for (i = 0; i < strlen(line); i++) {
708		if (line[i] == ';')
709			return 1;
710		if (!isspace(line[i]) && !iscntrl(line[i]))
711			return 0;
712	}
713
714	return 1;
715}
716
717static int str_rw_cb(void *data, const char *mem)
718{
719	struct thread_data *td = data;
720
721	if (!strncmp(mem, "read", 4) || !strncmp(mem, "0", 1)) {
722		td->ddir = DDIR_READ;
723		td->sequential = 1;
724		return 0;
725	} else if (!strncmp(mem, "randread", 8)) {
726		td->ddir = DDIR_READ;
727		td->sequential = 0;
728		return 0;
729	} else if (!strncmp(mem, "write", 5) || !strncmp(mem, "1", 1)) {
730		td->ddir = DDIR_WRITE;
731		td->sequential = 1;
732		return 0;
733	} else if (!strncmp(mem, "randwrite", 9)) {
734		td->ddir = DDIR_WRITE;
735		td->sequential = 0;
736		return 0;
737	} else if (!strncmp(mem, "rw", 2)) {
738		td->ddir = 0;
739		td->iomix = 1;
740		td->sequential = 1;
741		return 0;
742	} else if (!strncmp(mem, "randrw", 6)) {
743		td->ddir = 0;
744		td->iomix = 1;
745		td->sequential = 0;
746		return 0;
747	}
748
749	log_err("fio: data direction: read, write, randread, randwrite, rw, randrw\n");
750	return 1;
751}
752
753static int str_verify_cb(void *data, const char *mem)
754{
755	struct thread_data *td = data;
756
757	if (!strncmp(mem, "0", 1)) {
758		td->verify = VERIFY_NONE;
759		return 0;
760	} else if (!strncmp(mem, "md5", 3) || !strncmp(mem, "1", 1)) {
761		td->verify = VERIFY_MD5;
762		return 0;
763	} else if (!strncmp(mem, "crc32", 5)) {
764		td->verify = VERIFY_CRC32;
765		return 0;
766	}
767
768	log_err("fio: verify types: md5, crc32\n");
769	return 1;
770}
771
772static int str_mem_cb(void *data, const char *mem)
773{
774	struct thread_data *td = data;
775
776	if (!strncmp(mem, "malloc", 6)) {
777		td->mem_type = MEM_MALLOC;
778		return 0;
779	} else if (!strncmp(mem, "shm", 3)) {
780		td->mem_type = MEM_SHM;
781		return 0;
782	} else if (!strncmp(mem, "mmap", 4)) {
783		td->mem_type = MEM_MMAP;
784		return 0;
785	}
786
787	log_err("fio: mem type: malloc, shm, mmap\n");
788	return 1;
789}
790
791static int str_ioengine_cb(void *data, const char *str)
792{
793	struct thread_data *td = data;
794
795	td->io_ops = load_ioengine(td, str);
796	if (td->io_ops)
797		return 0;
798
799	log_err("fio: ioengine: { linuxaio, aio, libaio }, posixaio, sync, mmap, sgio, splice, cpu\n");
800	return 1;
801}
802
803static int str_lockmem_cb(void fio_unused *data, unsigned long *val)
804{
805	mlock_size = *val;
806	return 0;
807}
808
809static int str_prioclass_cb(void *data, unsigned int *val)
810{
811	struct thread_data *td = data;
812
813	td->ioprio |= *val << IOPRIO_CLASS_SHIFT;
814	return 0;
815}
816
817static int str_prio_cb(void *data, unsigned int *val)
818{
819	struct thread_data *td = data;
820
821	td->ioprio |= *val;
822	return 0;
823}
824
825static int str_exitall_cb(void)
826{
827	exitall_on_terminate = 1;
828	return 0;
829}
830
831static int str_cpumask_cb(void *data, unsigned int *val)
832{
833	struct thread_data *td = data;
834
835	fill_cpu_mask(td->cpumask, *val);
836	return 0;
837}
838
839/*
840 * This is our [ini] type file parser.
841 */
842int parse_jobs_ini(char *file, int stonewall_flag)
843{
844	unsigned int global;
845	struct thread_data *td;
846	char *string, *name;
847	fpos_t off;
848	FILE *f;
849	char *p;
850	int ret = 0, stonewall;
851
852	f = fopen(file, "r");
853	if (!f) {
854		perror("fopen job file");
855		return 1;
856	}
857
858	string = malloc(4096);
859	name = malloc(256);
860	memset(name, 0, 256);
861
862	stonewall = stonewall_flag;
863	while ((p = fgets(string, 4096, f)) != NULL) {
864		if (ret)
865			break;
866		if (is_empty_or_comment(p))
867			continue;
868		if (sscanf(p, "[%255s]", name) != 1)
869			continue;
870
871		global = !strncmp(name, "global", 6);
872
873		name[strlen(name) - 1] = '\0';
874
875		td = get_new_job(global, &def_thread);
876		if (!td) {
877			ret = 1;
878			break;
879		}
880
881		/*
882		 * Seperate multiple job files by a stonewall
883		 */
884		if (!global && stonewall) {
885			td->stonewall = stonewall;
886			stonewall = 0;
887		}
888
889		fgetpos(f, &off);
890		while ((p = fgets(string, 4096, f)) != NULL) {
891			if (is_empty_or_comment(p))
892				continue;
893			if (strstr(p, "["))
894				break;
895
896			strip_blank_front(&p);
897			strip_blank_end(p);
898
899			fgetpos(f, &off);
900
901			/*
902			 * Don't break here, continue parsing options so we
903			 * dump all the bad ones. Makes trial/error fixups
904			 * easier on the user.
905			 */
906			ret = parse_option(p, options, td);
907		}
908
909		if (!ret) {
910			fsetpos(f, &off);
911			ret = add_job(td, name, 0);
912		}
913		if (ret)
914			break;
915	}
916
917	free(string);
918	free(name);
919	fclose(f);
920	return ret;
921}
922
923static int fill_def_thread(void)
924{
925	memset(&def_thread, 0, sizeof(def_thread));
926
927	if (fio_getaffinity(getpid(), &def_thread.cpumask) == -1) {
928		perror("sched_getaffinity");
929		return 1;
930	}
931
932	/*
933	 * fill globals
934	 */
935	def_thread.ddir = DDIR_READ;
936	def_thread.iomix = 0;
937	def_thread.bs = DEF_BS;
938	def_thread.min_bs = 0;
939	def_thread.max_bs = 0;
940	def_thread.odirect = DEF_ODIRECT;
941	def_thread.ratecycle = DEF_RATE_CYCLE;
942	def_thread.sequential = DEF_SEQUENTIAL;
943	def_thread.timeout = def_timeout;
944	def_thread.overwrite = DEF_OVERWRITE;
945	def_thread.invalidate_cache = DEF_INVALIDATE;
946	def_thread.sync_io = DEF_SYNCIO;
947	def_thread.mem_type = MEM_MALLOC;
948	def_thread.bw_avg_time = DEF_BWAVGTIME;
949	def_thread.create_serialize = DEF_CREATE_SER;
950	def_thread.create_fsync = DEF_CREATE_FSYNC;
951	def_thread.loops = DEF_LOOPS;
952	def_thread.verify = DEF_VERIFY;
953	def_thread.stonewall = DEF_STONEWALL;
954	def_thread.numjobs = DEF_NUMJOBS;
955	def_thread.use_thread = DEF_USE_THREAD;
956	def_thread.rwmixcycle = DEF_RWMIX_CYCLE;
957	def_thread.rwmixread = DEF_RWMIX_READ;
958	def_thread.nice = DEF_NICE;
959	def_thread.rand_repeatable = DEF_RAND_REPEAT;
960	def_thread.nr_files = DEF_NR_FILES;
961	def_thread.unlink = DEF_UNLINK;
962	def_thread.write_bw_log = write_bw_log;
963	def_thread.write_lat_log = write_lat_log;
964#ifdef FIO_HAVE_DISK_UTIL
965	def_thread.do_disk_util = 1;
966#endif
967
968	return 0;
969}
970
971static void usage(void)
972{
973	printf("%s\n", fio_version_string);
974	printf("\t--output\tWrite output to file\n");
975	printf("\t--timeout\tRuntime in seconds\n");
976	printf("\t--latency-log\tGenerate per-job latency logs\n");
977	printf("\t--bandwidth-log\tGenerate per-job bandwidth logs\n");
978	printf("\t--minimal\tMinimal (terse) output\n");
979	printf("\t--version\tPrint version info and exit\n");
980}
981
982static int parse_cmd_line(int argc, char *argv[])
983{
984	struct thread_data *td = NULL;
985	int c, ini_idx = 0, lidx, ret;
986
987	while ((c = getopt_long(argc, argv, "", long_options, &lidx)) != -1) {
988		switch (c) {
989		case 't':
990			def_timeout = atoi(optarg);
991			break;
992		case 'l':
993			write_lat_log = 1;
994			break;
995		case 'w':
996			write_bw_log = 1;
997			break;
998		case 'o':
999			f_out = fopen(optarg, "w+");
1000			if (!f_out) {
1001				perror("fopen output");
1002				exit(1);
1003			}
1004			f_err = f_out;
1005			break;
1006		case 'm':
1007			terse_output = 1;
1008			break;
1009		case 'h':
1010			usage();
1011			exit(0);
1012		case 'v':
1013			printf("%s\n", fio_version_string);
1014			exit(0);
1015		case FIO_GETOPT_JOB: {
1016			const char *opt = long_options[lidx].name;
1017			char *val = optarg;
1018
1019			if (!strncmp(opt, "name", 4) && td) {
1020				ret = add_job(td, td->name ?: "fio", 0);
1021				if (ret) {
1022					put_job(td);
1023					return 0;
1024				}
1025				td = NULL;
1026			}
1027			if (!td) {
1028				int global = !strncmp(val, "global", 6);
1029
1030				td = get_new_job(global, &def_thread);
1031				if (!td)
1032					return 0;
1033			}
1034
1035			parse_cmd_option(opt, val, options, td);
1036			break;
1037		}
1038		default:
1039			printf("optarg <<%s>>\n", argv[optind]);
1040			break;
1041		}
1042	}
1043
1044	if (td) {
1045		ret = add_job(td, td->name ?: "fio", 0);
1046		if (ret)
1047			put_job(td);
1048	}
1049
1050	while (optind < argc) {
1051		ini_idx++;
1052		ini_file = realloc(ini_file, ini_idx * sizeof(char *));
1053		ini_file[ini_idx - 1] = strdup(argv[optind]);
1054		optind++;
1055	}
1056
1057	return ini_idx;
1058}
1059
1060static void free_shm(void)
1061{
1062	struct shmid_ds sbuf;
1063
1064	if (threads) {
1065		shmdt((void *) threads);
1066		threads = NULL;
1067		shmctl(shm_id, IPC_RMID, &sbuf);
1068	}
1069}
1070
1071/*
1072 * The thread area is shared between the main process and the job
1073 * threads/processes. So setup a shared memory segment that will hold
1074 * all the job info.
1075 */
1076static int setup_thread_area(void)
1077{
1078	/*
1079	 * 1024 is too much on some machines, scale max_jobs if
1080	 * we get a failure that looks like too large a shm segment
1081	 */
1082	do {
1083		size_t size = max_jobs * sizeof(struct thread_data);
1084
1085		shm_id = shmget(0, size, IPC_CREAT | 0600);
1086		if (shm_id != -1)
1087			break;
1088		if (errno != EINVAL) {
1089			perror("shmget");
1090			break;
1091		}
1092
1093		max_jobs >>= 1;
1094	} while (max_jobs);
1095
1096	if (shm_id == -1)
1097		return 1;
1098
1099	threads = shmat(shm_id, NULL, 0);
1100	if (threads == (void *) -1) {
1101		perror("shmat");
1102		return 1;
1103	}
1104
1105	atexit(free_shm);
1106	return 0;
1107}
1108
1109/*
1110 * Copy the fio options into the long options map, so we mirror
1111 * job and cmd line options.
1112 */
1113static void dupe_job_options(void)
1114{
1115	struct fio_option *o;
1116	unsigned int i;
1117
1118	i = 0;
1119	while (long_options[i].name)
1120		i++;
1121
1122	o = &options[0];
1123	while (o->name) {
1124		long_options[i].name = o->name;
1125		long_options[i].val = FIO_GETOPT_JOB;
1126		if (o->type == FIO_OPT_STR_SET)
1127			long_options[i].has_arg = no_argument;
1128		else
1129			long_options[i].has_arg = required_argument;
1130
1131		i++;
1132		o++;
1133		assert(i < FIO_JOB_OPTS + FIO_CMD_OPTS);
1134	}
1135}
1136
1137int parse_options(int argc, char *argv[])
1138{
1139	int job_files, i;
1140
1141	f_out = stdout;
1142	f_err = stderr;
1143
1144	dupe_job_options();
1145
1146	if (setup_thread_area())
1147		return 1;
1148	if (fill_def_thread())
1149		return 1;
1150
1151	job_files = parse_cmd_line(argc, argv);
1152
1153	for (i = 0; i < job_files; i++) {
1154		if (fill_def_thread())
1155			return 1;
1156		if (parse_jobs_ini(ini_file[i], i))
1157			return 1;
1158		free(ini_file[i]);
1159	}
1160
1161	free(ini_file);
1162
1163	if (!thread_number) {
1164		log_err("No jobs defined(s)\n");
1165		usage();
1166		return 1;
1167	}
1168
1169	return 0;
1170}
1171