Add support for O_ATOMIC
[fio.git] / init.c
diff --git a/init.c b/init.c
index 44c74e315a4b758ccdd7bb8055094014d395b12e..1841ffc0fd152a81f72989a669aae08fe7dcc26e 100644 (file)
--- a/init.c
+++ b/init.c
@@ -45,7 +45,6 @@ int exitall_on_terminate = 0;
 int output_format = FIO_OUTPUT_NORMAL;
 int eta_print = FIO_ETA_AUTO;
 int eta_new_line = 0;
-unsigned long long mlock_size = 0;
 FILE *f_out = NULL;
 FILE *f_err = NULL;
 char **job_sections = NULL;
@@ -59,6 +58,7 @@ int log_syslog = 0;
 
 int write_bw_log = 0;
 int read_only = 0;
+int status_interval = 0;
 
 static int write_lat_log;
 
@@ -124,9 +124,9 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
                .val            = 'c' | FIO_CLIENT_FLAG,
        },
        {
-               .name              = (char *) "enghelp",
+               .name           = (char *) "enghelp",
                .has_arg        = optional_argument,
-               .val                = 'i' | FIO_CLIENT_FLAG,
+               .val            = 'i' | FIO_CLIENT_FLAG,
        },
        {
                .name           = (char *) "showcmd",
@@ -212,12 +212,17 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
                .has_arg        = required_argument,
                .val            = 'I',
        },
+       {
+               .name           = (char *) "status-interval",
+               .has_arg        = required_argument,
+               .val            = 'L',
+       },
        {
                .name           = NULL,
        },
 };
 
-static void free_shm(void)
+void free_threads_shm(void)
 {
        struct shmid_ds sbuf;
 
@@ -225,11 +230,19 @@ static void free_shm(void)
                void *tp = threads;
 
                threads = NULL;
+               shmdt(tp);
+               shmctl(shm_id, IPC_RMID, &sbuf);
+               shm_id = -1;
+       }
+}
+
+void free_shm(void)
+{
+       if (threads) {
                file_hash_exit();
                flow_exit();
                fio_debug_jobp = NULL;
-               shmdt(tp);
-               shmctl(shm_id, IPC_RMID, &sbuf);
+               free_threads_shm();
        }
 
        scleanup();
@@ -608,13 +621,27 @@ static int fixup_options(struct thread_data *td)
        if (td->o.random_distribution != FIO_RAND_DIST_RANDOM)
                td->o.norandommap = 1;
 
+       /*
+        * If size is set but less than the min block size, complain
+        */
+       if (o->size && o->size < td_min_bs(td)) {
+               log_err("fio: size too small, must be larger than the IO size: %llu\n", (unsigned long long) o->size);
+               ret = 1;
+       }
+
+       /*
+        * O_ATOMIC implies O_DIRECT
+        */
+       if (td->o.oatomic)
+               td->o.odirect = 1;
+
        return ret;
 }
 
 /*
  * This function leaks the buffer
  */
-static char *to_kmg(unsigned int val)
+char *fio_uint_to_kmg(unsigned int val)
 {
        char *buf = malloc(32);
        char post[] = { 0, 'K', 'M', 'G', 'P', 'E', 0 };
@@ -680,6 +707,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_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)
@@ -701,6 +732,9 @@ 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[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)
@@ -726,6 +760,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);
@@ -883,16 +921,20 @@ 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
  * members of td.
  */
-static int add_job(struct thread_data *td, const char *jobname, int job_add_num)
+static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
+                  int recursed, int client_type)
 {
-       const char *ddir_str[] = { NULL, "read", "write", "rw", NULL,
-                                  "randread", "randwrite", "randrw",
-                                  "trim", NULL, NULL, NULL, "randtrim" };
        unsigned int i;
        char fname[PATH_MAX];
        int numjobs, file_alloced;
@@ -909,22 +951,19 @@ 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;
        }
 
+       td->client_type = client_type;
+
        if (profile_td_init(td))
                goto err;
 
        if (ioengine_load(td))
                goto err;
 
-       if (o->use_thread)
-               nr_thread++;
-       else
-               nr_process++;
-
        if (o->odirect)
                td->io_ops->flags |= FIO_RAWIO;
 
@@ -989,40 +1028,50 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num)
        if (setup_rate(td))
                goto err;
 
-       if (o->write_lat_log) {
-               setup_log(&td->lat_log, o->log_avg_msec);
-               setup_log(&td->slat_log, o->log_avg_msec);
-               setup_log(&td->clat_log, o->log_avg_msec);
+       if (o->lat_log_file) {
+               setup_log(&td->lat_log, o->log_avg_msec, IO_LOG_TYPE_LAT);
+               setup_log(&td->slat_log, o->log_avg_msec, IO_LOG_TYPE_SLAT);
+               setup_log(&td->clat_log, o->log_avg_msec, IO_LOG_TYPE_CLAT);
        }
-       if (o->write_bw_log)
-               setup_log(&td->bw_log, o->log_avg_msec);
-       if (o->write_iops_log)
-               setup_log(&td->iops_log, o->log_avg_msec);
+       if (o->bw_log_file)
+               setup_log(&td->bw_log, o->log_avg_msec, IO_LOG_TYPE_BW);
+       if (o->iops_log_file)
+               setup_log(&td->iops_log, o->log_avg_msec, IO_LOG_TYPE_IOPS);
 
        if (!o->name)
                o->name = strdup(jobname);
 
        if (output_format == FIO_OUTPUT_NORMAL) {
                if (!job_add_num) {
-                       if (!strcmp(td->io_ops->name, "cpuio")) {
-                               log_info("%s: ioengine=cpu, cpuload=%u,"
-                                        " cpucycle=%u\n", o->name,
-                                               o->cpuload, o->cpucycle);
-                       } else {
-                               char *c1, *c2, *c3, *c4, *c5, *c6;
-
-                               c1 = to_kmg(o->min_bs[DDIR_READ]);
-                               c2 = to_kmg(o->max_bs[DDIR_READ]);
-                               c3 = to_kmg(o->min_bs[DDIR_WRITE]);
-                               c4 = to_kmg(o->max_bs[DDIR_WRITE]);
-                               c5 = to_kmg(o->min_bs[DDIR_TRIM]);
-                               c6 = 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",
-                                               o->name, td->groupid,
-                                               ddir_str[o->td_ddir],
-                                               c1, c2, c3, c4, c5, c6,
+                       if (is_backend && !recursed)
+                               fio_server_send_add_job(td);
+
+                       if (!(td->io_ops->flags & FIO_NOIO)) {
+                               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]);
+
+                               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);
@@ -1060,7 +1109,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num)
 
                job_add_num = numjobs - 1;
 
-               if (add_job(td_new, jobname, job_add_num))
+               if (add_job(td_new, jobname, job_add_num, 1, client_type))
                        goto err;
        }
 
@@ -1073,7 +1122,7 @@ err:
 /*
  * Parse as if 'o' was a command line
  */
-void add_job_opts(const char **o)
+void add_job_opts(const char **o, int client_type)
 {
        struct thread_data *td, *td_parent;
        int i, in_global = 1;
@@ -1085,7 +1134,7 @@ void add_job_opts(const char **o)
                if (!strncmp(o[i], "name", 4)) {
                        in_global = 0;
                        if (td)
-                               add_job(td, jobname, 0);
+                               add_job(td, jobname, 0, 0, client_type);
                        td = NULL;
                        sprintf(jobname, "%s", o[i] + 5);
                }
@@ -1104,7 +1153,7 @@ void add_job_opts(const char **o)
        }
 
        if (td)
-               add_job(td, jobname, 0);
+               add_job(td, jobname, 0, 0, client_type);
 }
 
 static int skip_this_section(const char *name)
@@ -1142,7 +1191,7 @@ static int is_empty_or_comment(char *line)
 /*
  * This is our [ini] type file parser.
  */
-int parse_jobs_ini(char *file, int is_buf, int stonewall_flag)
+int parse_jobs_ini(char *file, int is_buf, int stonewall_flag, int type)
 {
        unsigned int global;
        struct thread_data *td;
@@ -1236,7 +1285,7 @@ int parse_jobs_ini(char *file, int is_buf, int stonewall_flag)
                }
 
                /*
-                * Seperate multiple job files by a stonewall
+                * Separate multiple job files by a stonewall
                 */
                if (!global && stonewall) {
                        td->o.stonewall = stonewall;
@@ -1286,7 +1335,7 @@ int parse_jobs_ini(char *file, int is_buf, int stonewall_flag)
                                for (i = 0; i < num_opts; i++)
                                        log_info("--%s ", opts[i]);
 
-                       ret = add_job(td, name, 0);
+                       ret = add_job(td, name, 0, 0, type);
                } else {
                        log_err("fio: job %s dropped\n", name);
                        put_job(td);
@@ -1360,6 +1409,8 @@ static void usage(const char *name)
        printf("            \t\tMay be \"always\", \"never\" or \"auto\"\n");
        printf("  --eta-newline=time\tForce a new line for every 'time'");
        printf(" period passed\n");
+       printf("  --status-interval=t\tForce full status dump every");
+       printf(" 't' period passed\n");
        printf("  --readonly\t\tTurn on safety read-only checks, preventing"
                " writes\n");
        printf("  --section=name\tOnly run specified section in job file\n");
@@ -1379,20 +1430,62 @@ static void usage(const char *name)
 
 #ifdef FIO_INC_DEBUG
 struct debug_level debug_levels[] = {
-       { .name = "process",    .shift = FD_PROCESS, },
-       { .name = "file",       .shift = FD_FILE, },
-       { .name = "io",         .shift = FD_IO, },
-       { .name = "mem",        .shift = FD_MEM, },
-       { .name = "blktrace",   .shift = FD_BLKTRACE },
-       { .name = "verify",     .shift = FD_VERIFY },
-       { .name = "random",     .shift = FD_RANDOM },
-       { .name = "parse",      .shift = FD_PARSE },
-       { .name = "diskutil",   .shift = FD_DISKUTIL },
-       { .name = "job",        .shift = FD_JOB },
-       { .name = "mutex",      .shift = FD_MUTEX },
-       { .name = "profile",    .shift = FD_PROFILE },
-       { .name = "time",       .shift = FD_TIME },
-       { .name = "net",        .shift = FD_NET },
+       { .name = "process",
+         .help = "Process creation/exit logging",
+         .shift = FD_PROCESS,
+       },
+       { .name = "file",
+         .help = "File related action logging",
+         .shift = FD_FILE,
+       },
+       { .name = "io",
+         .help = "IO and IO engine action logging (offsets, queue, completions, etc)",
+         .shift = FD_IO,
+       },
+       { .name = "mem",
+         .help = "Memory allocation/freeing logging",
+         .shift = FD_MEM,
+       },
+       { .name = "blktrace",
+         .help = "blktrace action logging",
+         .shift = FD_BLKTRACE,
+       },
+       { .name = "verify",
+         .help = "IO verification action logging",
+         .shift = FD_VERIFY,
+       },
+       { .name = "random",
+         .help = "Random generation logging",
+         .shift = FD_RANDOM,
+       },
+       { .name = "parse",
+         .help = "Parser logging",
+         .shift = FD_PARSE,
+       },
+       { .name = "diskutil",
+         .help = "Disk utility logging actions",
+         .shift = FD_DISKUTIL,
+       },
+       { .name = "job",
+         .help = "Logging related to creating/destroying jobs",
+         .shift = FD_JOB,
+       },
+       { .name = "mutex",
+         .help = "Mutex logging",
+         .shift = FD_MUTEX
+       },
+       { .name = "profile",
+         .help = "Logging related to profiles",
+         .shift = FD_PROFILE,
+       },
+       { .name = "time",
+         .help = "Logging related to time keeping functions",
+         .shift = FD_TIME,
+       },
+       { .name = "net",
+         .help = "Network logging",
+         .shift = FD_NET,
+       },
        { .name = NULL, },
 };
 
@@ -1499,7 +1592,7 @@ void parse_cmd_client(void *client, char *opt)
        fio_client_add_cmd_option(client, opt);
 }
 
-int parse_cmd_line(int argc, char *argv[])
+int parse_cmd_line(int argc, char *argv[], int client_type)
 {
        struct thread_data *td = NULL;
        int c, ini_idx = 0, lidx, ret = 0, do_exit = 0, exit_val = 0;
@@ -1643,7 +1736,7 @@ int parse_cmd_line(int argc, char *argv[])
                        char *val = optarg;
 
                        if (!strncmp(opt, "name", 4) && td) {
-                               ret = add_job(td, td->o.name ?: "fio", 0);
+                               ret = add_job(td, td->o.name ?: "fio", 0, 0, client_type);
                                if (ret)
                                        return 0;
                                td = NULL;
@@ -1664,7 +1757,20 @@ int parse_cmd_line(int argc, char *argv[])
                                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);
+                                       td = NULL;
+                               }
+                               do_exit++;
+                       }
 
                        if (!ret && !strcmp(opt, "ioengine")) {
                                free_ioengine(td);
@@ -1677,8 +1783,6 @@ int parse_cmd_line(int argc, char *argv[])
                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;
                }
@@ -1723,7 +1827,7 @@ int parse_cmd_line(int argc, char *argv[])
                                exit_val = 1;
                                break;
                        }
-                       if (fio_client_add(optarg, &cur_client)) {
+                       if (fio_client_add(&fio_client_ops, optarg, &cur_client)) {
                                log_err("fio: failed adding client %s\n", optarg);
                                do_exit++;
                                exit_val = 1;
@@ -1746,6 +1850,18 @@ int parse_cmd_line(int argc, char *argv[])
                        do_exit++;
                        exit_val = fio_monotonic_clocktest();
                        break;
+               case 'L': {
+                       long long val;
+
+                       if (check_str_time(optarg, &val)) {
+                               log_err("fio: failed parsing time %s\n", optarg);
+                               do_exit++;
+                               exit_val = 1;
+                               break;
+                       }
+                       status_interval = val * 1000;
+                       break;
+                       }
                case '?':
                        log_err("%s: unrecognized option '%s'\n", argv[0],
                                                        argv[optind - 1]);
@@ -1758,10 +1874,8 @@ int parse_cmd_line(int argc, char *argv[])
                        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++;
@@ -1774,7 +1888,7 @@ int parse_cmd_line(int argc, char *argv[])
 
        if (td) {
                if (!ret)
-                       ret = add_job(td, td->o.name ?: "fio", 0);
+                       ret = add_job(td, td->o.name ?: "fio", 0, 0, client_type);
        }
 
        while (!ret && optind < argc) {
@@ -1787,10 +1901,8 @@ int parse_cmd_line(int argc, char *argv[])
        return ini_idx;
 }
 
-int parse_options(int argc, char *argv[])
+int fio_init_options(void)
 {
-       int job_files, i;
-
        f_out = stdout;
        f_err = stderr;
 
@@ -1802,7 +1914,22 @@ int parse_options(int argc, char *argv[])
        if (fill_def_thread())
                return 1;
 
-       job_files = parse_cmd_line(argc, argv);
+       return 0;
+}
+
+extern int fio_check_options(struct thread_options *);
+
+int parse_options(int argc, char *argv[])
+{
+       const int type = FIO_CLIENT_TYPE_CLI;
+       int job_files, i;
+
+       if (fio_init_options())
+               return 1;
+       if (fio_test_cconv(&def_thread.o))
+               log_err("fio: failed internal cconv test\n");
+
+       job_files = parse_cmd_line(argc, argv, type);
 
        if (job_files > 0) {
                for (i = 0; i < job_files; i++) {
@@ -1813,7 +1940,7 @@ int parse_options(int argc, char *argv[])
                                        return 1;
                                free(ini_file[i]);
                        } else if (!is_backend) {
-                               if (parse_jobs_ini(ini_file[i], 0, i))
+                               if (parse_jobs_ini(ini_file[i], 0, i, type))
                                        return 1;
                                free(ini_file[i]);
                        }
@@ -1829,7 +1956,7 @@ int parse_options(int argc, char *argv[])
        fio_options_free(&def_thread);
 
        if (!thread_number) {
-               if (dump_cmdline || parse_only)
+               if (parse_dryrun())
                        return 0;
                if (exec_profile)
                        return 0;
@@ -1859,3 +1986,8 @@ int parse_options(int argc, char *argv[])
 
        return 0;
 }
+
+void options_default_fill(struct thread_options *o)
+{
+       memcpy(o, &def_thread.o, sizeof(*o));
+}