fio: add multi directory support
[fio.git] / init.c
diff --git a/init.c b/init.c
index 0cc6a520b15269c177ff1fa7ab01a4fddc2ac038..7f8b3178ebd26f36e47c434dbcf035a6b8ceac28 100644 (file)
--- a/init.c
+++ b/init.c
@@ -100,7 +100,7 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
        },
        {
                .name           = (char *) "minimal",
-               .has_arg        = optional_argument,
+               .has_arg        = no_argument,
                .val            = 'm' | FIO_CLIENT_FLAG,
        },
        {
@@ -207,6 +207,11 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
                .has_arg        = no_argument,
                .val            = 'T',
        },
+       {
+               .name           = (char *) "crctest",
+               .has_arg        = optional_argument,
+               .val            = 'G',
+       },
        {
                .name           = (char *) "idle-prof",
                .has_arg        = required_argument,
@@ -245,6 +250,7 @@ void free_shm(void)
                free_threads_shm();
        }
 
+       options_free(fio_options, &def_thread);
        scleanup();
 }
 
@@ -406,6 +412,26 @@ static int fixed_block_size(struct thread_options *o)
                o->min_bs[DDIR_READ] == o->min_bs[DDIR_TRIM];
 }
 
+
+static unsigned long long get_rand_start_delay(struct thread_data *td)
+{
+       unsigned long long delayrange;
+       unsigned long r;
+
+       delayrange = td->o.start_delay_high - td->o.start_delay;
+
+       if (td->o.use_os_rand) {
+               r = os_random_long(&td->delay_state);
+               delayrange = (unsigned long long) ((double) delayrange * (r / (OS_RAND_MAX + 1.0)));
+       } else {
+               r = __rand(&td->__delay_state);
+               delayrange = (unsigned long long) ((double) delayrange * (r / (FRAND_MAX + 1.0)));
+       }
+
+       delayrange += td->o.start_delay;
+       return delayrange;
+}
+
 /*
  * Lazy way of fixing up options that depend on each other. We could also
  * define option callback handlers, but this is easier.
@@ -490,6 +516,9 @@ static int fixup_options(struct thread_data *td)
        if (!o->file_size_high)
                o->file_size_high = o->file_size_low;
 
+       if (o->start_delay_high)
+               o->start_delay = get_rand_start_delay(td);
+
        if (o->norandommap && o->verify != VERIFY_NONE
            && !fixed_block_size(o))  {
                log_err("fio: norandommap given for variable block sizes, "
@@ -629,6 +658,18 @@ static int fixup_options(struct thread_data *td)
                ret = 1;
        }
 
+       /*
+        * O_ATOMIC implies O_DIRECT
+        */
+       if (td->o.oatomic)
+               td->o.odirect = 1;
+
+       /*
+        * If randseed is set, that overrides randrepeat
+        */
+       if (td->o.rand_seed)
+               td->o.rand_repeatable = 0;
+
        return ret;
 }
 
@@ -693,6 +734,7 @@ static void td_fill_rand_seeds_os(struct thread_data *td)
 
        os_random_seed(td->rand_seeds[FIO_RAND_FILE_SIZE_OFF], &td->file_size_state);
        os_random_seed(td->rand_seeds[FIO_RAND_TRIM_OFF], &td->trim_state);
+       os_random_seed(td->rand_seeds[FIO_RAND_START_DELAY], &td->delay_state);
 
        if (!td_random(td))
                return;
@@ -701,7 +743,10 @@ static void td_fill_rand_seeds_os(struct thread_data *td)
                td->rand_seeds[FIO_RAND_BLOCK_OFF] = FIO_RANDSEED * td->thread_number;
 
        os_random_seed(td->rand_seeds[FIO_RAND_BLOCK_OFF], &td->random_state);
-       os_random_seed(td->rand_seeds[FIO_RAND_SEQ_RAND_OFF], &td->seq_rand_state);
+
+       os_random_seed(td->rand_seeds[FIO_RAND_SEQ_RAND_READ_OFF], &td->seq_rand_state[DDIR_READ]);
+       os_random_seed(td->rand_seeds[FIO_RAND_SEQ_RAND_WRITE_OFF], &td->seq_rand_state[DDIR_WRITE]);
+       os_random_seed(td->rand_seeds[FIO_RAND_SEQ_RAND_TRIM_OFF], &td->seq_rand_state[DDIR_TRIM]);
 }
 
 static void td_fill_rand_seeds_internal(struct thread_data *td)
@@ -715,6 +760,7 @@ static void td_fill_rand_seeds_internal(struct thread_data *td)
 
        init_rand_seed(&td->__file_size_state, td->rand_seeds[FIO_RAND_FILE_SIZE_OFF]);
        init_rand_seed(&td->__trim_state, td->rand_seeds[FIO_RAND_TRIM_OFF]);
+       init_rand_seed(&td->__delay_state, td->rand_seeds[FIO_RAND_START_DELAY]);
 
        if (!td_random(td))
                return;
@@ -723,11 +769,19 @@ static void td_fill_rand_seeds_internal(struct thread_data *td)
                td->rand_seeds[FIO_RAND_BLOCK_OFF] = FIO_RANDSEED * td->thread_number;
 
        init_rand_seed(&td->__random_state, td->rand_seeds[FIO_RAND_BLOCK_OFF]);
-       init_rand_seed(&td->__seq_rand_state, td->rand_seeds[FIO_RAND_SEQ_RAND_OFF]);
+       init_rand_seed(&td->__seq_rand_state[DDIR_READ], td->rand_seeds[FIO_RAND_SEQ_RAND_READ_OFF]);
+       init_rand_seed(&td->__seq_rand_state[DDIR_WRITE], td->rand_seeds[FIO_RAND_SEQ_RAND_WRITE_OFF]);
+       init_rand_seed(&td->__seq_rand_state[DDIR_TRIM], td->rand_seeds[FIO_RAND_SEQ_RAND_TRIM_OFF]);
 }
 
 void td_fill_rand_seeds(struct thread_data *td)
 {
+       if (td->o.allrand_repeatable) {
+               for (int i = 0; i < FIO_RAND_NR_OFFS; i++)
+                       td->rand_seeds[i] = FIO_RANDSEED * td->thread_number
+                               + i;
+       }
+
        if (td->o.use_os_rand)
                td_fill_rand_seeds_os(td);
        else
@@ -749,6 +803,10 @@ int ioengine_load(struct thread_data *td)
         */
        if (td->io_ops)
                return 0;
+       if (!td->o.ioengine) {
+               log_err("fio: internal fault, no IO engine specified\n");
+               return 1;
+       }
 
        engine = get_engine_name(td->o.ioengine);
        td->io_ops = load_ioengine(td, engine);
@@ -815,10 +873,15 @@ static int setup_random_seeds(struct thread_data *td)
        unsigned long seed;
        unsigned int i;
 
-       if (!td->o.rand_repeatable)
+       if (!td->o.rand_repeatable && !td->o.rand_seed)
                return init_random_state(td, td->rand_seeds, sizeof(td->rand_seeds));
 
-       for (seed = 0x89, i = 0; i < 4; i++)
+       if (!td->o.rand_seed)
+               seed = 0x89;
+       else
+               seed = td->o.rand_seed;
+
+       for (i = 0; i < 4; i++)
                seed *= 0x9e370001UL;
 
        for (i = 0; i < FIO_RAND_NR_OFFS; i++) {
@@ -906,6 +969,12 @@ static char *make_filename(char *buf, struct thread_options *o,
 
        return buf;
 }
+
+int parse_dryrun(void)
+{
+       return dump_cmdline || parse_only;
+}
+
 /*
  * Adds a job to the list of things todo. Sanitizes the various options
  * to make sure we don't have conflicts, and initializes various
@@ -930,7 +999,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
        /*
         * if we are just dumping the output command line, don't add the job
         */
-       if (dump_cmdline || parse_only) {
+       if (parse_dryrun()) {
                put_job(td);
                return 0;
        }
@@ -951,10 +1020,10 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                file_alloced = 1;
 
                if (o->nr_files == 1 && exists_and_not_file(jobname))
-                       add_file(td, jobname);
+                       add_file(td, jobname, job_add_num);
                else {
                        for (i = 0; i < o->nr_files; i++)
-                               add_file(td, make_filename(fname, o, jobname, td->thread_number, i));
+                               add_file(td, make_filename(fname, o, jobname, job_add_num, i), job_add_num);
                }
        }
 
@@ -1026,20 +1095,31 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                                fio_server_send_add_job(td);
 
                        if (!(td->io_ops->flags & FIO_NOIO)) {
-                               char *c1, *c2, *c3, *c4, *c5, *c6;
+                               char *c1, *c2, *c3, *c4;
+                               char *c5 = NULL, *c6 = NULL;
 
                                c1 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
                                c2 = fio_uint_to_kmg(o->max_bs[DDIR_READ]);
                                c3 = fio_uint_to_kmg(o->min_bs[DDIR_WRITE]);
                                c4 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
-                               c5 = fio_uint_to_kmg(o->min_bs[DDIR_TRIM]);
-                               c6 = fio_uint_to_kmg(o->max_bs[DDIR_TRIM]);
-
-                               log_info("%s: (g=%d): rw=%s, bs=%s-%s/%s-%s/%s-%s,"
-                                        " ioengine=%s, iodepth=%u\n",
-                                               td->o.name, td->groupid,
-                                               ddir_str(o->td_ddir),
-                                               c1, c2, c3, c4, c5, c6,
+
+                               if (!o->bs_is_seq_rand) {
+                                       c5 = fio_uint_to_kmg(o->min_bs[DDIR_TRIM]);
+                                       c6 = fio_uint_to_kmg(o->max_bs[DDIR_TRIM]);
+                               }
+
+                               log_info("%s: (g=%d): rw=%s, ", td->o.name,
+                                                       td->groupid,
+                                                       ddir_str(o->td_ddir));
+
+                               if (o->bs_is_seq_rand)
+                                       log_info("bs(seq/rand)=%s-%s/%s-%s, ",
+                                                       c1, c2, c3, c4);
+                               else
+                                       log_info("bs=%s-%s/%s-%s/%s-%s, ",
+                                                       c1, c2, c3, c4, c5, c6);
+
+                               log_info("ioengine=%s, iodepth=%u\n",
                                                td->io_ops->name, o->iodepth);
 
                                free(c1);
@@ -1069,15 +1149,24 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                td_new->o.new_group = 0;
 
                if (file_alloced) {
-                       td_new->o.filename = NULL;
                        td_new->files_index = 0;
                        td_new->files_size = 0;
-                       td_new->files = NULL;
+                       if (td_new->files) {
+                               struct fio_file *f;
+                               for_each_file(td_new, f, i) {
+                                       if (f->file_name)
+                                               free(f->file_name);
+                                       free(f);
+                               }
+                               td_new->files = NULL;
+                       }
+                       if (td_new->o.filename) {
+                               free(td_new->o.filename);
+                               td_new->o.filename = NULL;
+                       }
                }
 
-               job_add_num = numjobs - 1;
-
-               if (add_job(td_new, jobname, job_add_num, 1, client_type))
+               if (add_job(td_new, jobname, numjobs, 1, client_type))
                        goto err;
        }
 
@@ -1114,9 +1203,9 @@ void add_job_opts(const char **o, int client_type)
                        td = get_new_job(0, td_parent, 0);
                }
                if (in_global)
-                       fio_options_parse(td_parent, (char **) &o[i], 1);
+                       fio_options_parse(td_parent, (char **) &o[i], 1, 0);
                else
-                       fio_options_parse(td, (char **) &o[i], 1);
+                       fio_options_parse(td, (char **) &o[i], 1, 0);
                i++;
        }
 
@@ -1253,7 +1342,7 @@ int parse_jobs_ini(char *file, int is_buf, int stonewall_flag, int type)
                }
 
                /*
-                * Seperate multiple job files by a stonewall
+                * Separate multiple job files by a stonewall
                 */
                if (!global && stonewall) {
                        td->o.stonewall = stonewall;
@@ -1297,14 +1386,10 @@ int parse_jobs_ini(char *file, int is_buf, int stonewall_flag, int type)
                        num_opts++;
                }
 
-               ret = fio_options_parse(td, opts, num_opts);
-               if (!ret) {
-                       if (dump_cmdline)
-                               for (i = 0; i < num_opts; i++)
-                                       log_info("--%s ", opts[i]);
-
+               ret = fio_options_parse(td, opts, num_opts, dump_cmdline);
+               if (!ret)
                        ret = add_job(td, name, 0, 0, type);
-               else {
+               else {
                        log_err("fio: job %s dropped\n", name);
                        put_job(td);
                }
@@ -1354,7 +1439,7 @@ static void usage(const char *name)
        printf("%s [options] [job options] <job file(s)>\n", name);
        printf("  --debug=options\tEnable debug logging. May be one/more of:\n"
                "\t\t\tprocess,file,io,mem,blktrace,verify,random,parse,\n"
-               "\t\t\tdiskutil,job,mutex,profile,time,net\n");
+               "\t\t\tdiskutil,job,mutex,profile,time,net,rate\n");
        printf("  --parse-only\t\tParse options only, don't start any IO\n");
        printf("  --output\t\tWrite output to file\n");
        printf("  --runtime\t\tRuntime in seconds\n");
@@ -1366,6 +1451,7 @@ static void usage(const char *name)
        printf("  --version\t\tPrint version info and exit\n");
        printf("  --help\t\tPrint this page\n");
        printf("  --cpuclock-test\tPerform test/validation of CPU clock\n");
+       printf("  --crctest\t\tTest speed of checksum functions\n");
        printf("  --cmdhelp=cmd\t\tPrint command help, \"all\" for all of"
                " them\n");
        printf("  --enghelp=engine\tPrint ioengine help, or list"
@@ -1393,7 +1479,8 @@ static void usage(const char *name)
                "\t\t\t(option=system,percpu) or run unit work\n"
                "\t\t\tcalibration only (option=calibrate)\n");
        printf("\nFio was written by Jens Axboe <jens.axboe@oracle.com>");
-       printf("\n                   Jens Axboe <jaxboe@fusionio.com>\n");
+       printf("\n                   Jens Axboe <jaxboe@fusionio.com>");
+       printf("\n                   Jens Axboe <axboe@fb.com>\n");
 }
 
 #ifdef FIO_INC_DEBUG
@@ -1454,6 +1541,10 @@ struct debug_level debug_levels[] = {
          .help = "Network logging",
          .shift = FD_NET,
        },
+       { .name = "rate",
+         .help = "Rate logging",
+         .shift = FD_RATE,
+       },
        { .name = NULL, },
 };
 
@@ -1560,6 +1651,8 @@ void parse_cmd_client(void *client, char *opt)
        fio_client_add_cmd_option(client, opt);
 }
 
+extern int fio_crctest(const char *);
+
 int parse_cmd_line(int argc, char *argv[], int client_type)
 {
        struct thread_data *td = NULL;
@@ -1608,6 +1701,12 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        output_format = FIO_OUTPUT_TERSE;
                        break;
                case 'F':
+                       if (!optarg) {
+                               log_err("fio: missing --output-format argument\n");
+                               exit_val = 1;
+                               do_exit++;
+                               break;
+                       }
                        if (!strcmp(optarg, "minimal") ||
                            !strcmp(optarg, "terse") ||
                            !strcmp(optarg, "csv"))
@@ -1725,7 +1824,13 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                                fio_options_set_ioengine_opts(l_opts, td);
                        }
 
-                       ret = fio_cmd_option_parse(td, opt, val);
+                       if ((!val || !strlen(val)) &&
+                           l_opts[lidx].has_arg == required_argument) {
+                               log_err("fio: option %s requires an argument\n", opt);
+                               ret = 1;
+                       } else
+                               ret = fio_cmd_option_parse(td, opt, val);
+
                        if (ret) {
                                if (td) {
                                        put_job(td);
@@ -1745,8 +1850,6 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                case FIO_GETOPT_IOENGINE: {
                        const char *opt = l_opts[lidx].name;
                        char *val = optarg;
-                       opt = l_opts[lidx].name;
-                       val = optarg;
                        ret = fio_cmd_ioengine_option_parse(td, opt, val);
                        break;
                }
@@ -1814,6 +1917,10 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        do_exit++;
                        exit_val = fio_monotonic_clocktest();
                        break;
+               case 'G':
+                       do_exit++;
+                       exit_val = fio_crctest(optarg);
+                       break;
                case 'L': {
                        long long val;
 
@@ -1838,10 +1945,8 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        break;
        }
 
-       if (do_exit) {
-               if (exit_val && !(is_backend || nr_clients))
-                       exit(exit_val);
-       }
+       if (do_exit && !(is_backend || nr_clients))
+               exit(exit_val);
 
        if (nr_clients && fio_clients_connect()) {
                do_exit++;
@@ -1920,9 +2025,10 @@ int parse_options(int argc, char *argv[])
 
        free(ini_file);
        fio_options_free(&def_thread);
+       filesetup_mem_free();
 
        if (!thread_number) {
-               if (dump_cmdline || parse_only)
+               if (parse_dryrun())
                        return 0;
                if (exec_profile)
                        return 0;