[PATCH] Add full command line parameter support
[fio.git] / init.c
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
57 static int str_rw_cb(void *, const char *);
58 static int str_ioengine_cb(void *, const char *);
59 static int str_mem_cb(void *, const char *);
60 static int str_verify_cb(void *, const char *);
61 static int str_lockmem_cb(void *, unsigned long *);
62 static int str_prio_cb(void *, unsigned int *);
63 static int str_prioclass_cb(void *, unsigned int *);
64 static int str_exitall_cb(void);
65 static int str_cpumask_cb(void *, unsigned int *);
66
67 /*
68  * Map of job/command line options
69  */
70 static 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  */
358 static 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
394 static int def_timeout = DEF_TIMEOUT;
395
396 static char fio_version_string[] = "fio 1.7";
397
398 static char **ini_file;
399 static int max_jobs = MAX_JOBS;
400
401 struct thread_data def_thread;
402 struct thread_data *threads = NULL;
403
404 int rate_quit = 0;
405 int exitall_on_terminate = 0;
406 int terse_output = 0;
407 unsigned long long mlock_size = 0;
408 FILE *f_out = NULL;
409 FILE *f_err = NULL;
410
411 static int write_lat_log = DEF_WRITE_LAT_LOG;
412 static int write_bw_log = DEF_WRITE_BW_LOG;
413
414 /*
415  * Return a free job structure.
416  */
417 static 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
433 static 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  */
443 static 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  */
465 static 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;
637 err:
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  */
646 int 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
691 static 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
705 static 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
719 static 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
755 static 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
774 static 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
793 static 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
805 static int str_lockmem_cb(void fio_unused *data, unsigned long *val)
806 {
807         mlock_size = *val;
808         return 0;
809 }
810
811 static 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
819 static 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
827 static int str_exitall_cb(void)
828 {
829         exitall_on_terminate = 1;
830         return 0;
831 }
832
833 static 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  */
844 int 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
926 static 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
974 static 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
985 static 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
1059 static 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  */
1075 static 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  */
1112 static 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
1136 int 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 }