Merge branch 'master' into gfio
[fio.git] / init.c
diff --git a/init.c b/init.c
index f0ad01936a33bf136dc1bc7f7b54d4d9a4657626..27370bb43eb04d4dceb184cedecb106057058c3b 100644 (file)
--- a/init.c
+++ b/init.c
@@ -9,13 +9,14 @@
 #include <string.h>
 #include <errno.h>
 #include <sys/ipc.h>
-#ifndef FIO_NO_HAVE_SHM_H
-#include <sys/shm.h>
-#endif
 #include <sys/types.h>
 #include <sys/stat.h>
 
 #include "fio.h"
+#ifndef FIO_NO_HAVE_SHM_H
+#include <sys/shm.h>
+#endif
+
 #include "parse.h"
 #include "smalloc.h"
 #include "filehash.h"
@@ -34,13 +35,15 @@ static char **ini_file;
 static int max_jobs = FIO_MAX_JOBS;
 static int dump_cmdline;
 static int def_timeout;
+static int parse_only;
 
 static struct thread_data def_thread;
 struct thread_data *threads = NULL;
 
 int exitall_on_terminate = 0;
 int output_format = FIO_OUTPUT_NORMAL;
-int eta_print;
+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;
@@ -67,26 +70,6 @@ unsigned int *fio_debug_jobp = NULL;
 static char cmd_optstr[256];
 static int did_arg;
 
-const fio_fp64_t def_percentile_list[FIO_IO_U_LIST_MAX_LEN] = {
-       { .u.f  =  1.00 },
-       { .u.f  =  5.00 },
-       { .u.f  = 10.00 },
-       { .u.f  = 20.00 },
-       { .u.f  = 30.00 },
-       { .u.f  = 40.00 },
-       { .u.f  = 50.00 },
-       { .u.f  = 60.00 },
-       { .u.f  = 70.00 },
-       { .u.f  = 80.00 },
-       { .u.f  = 90.00 },
-       { .u.f  = 95.00 },
-       { .u.f  = 99.00 },
-       { .u.f  = 99.50 },
-       { .u.f  = 99.90 },
-       { .u.f  = 99.95 },
-       { .u.f  = 99.99 },
-};
-
 #define FIO_CLIENT_FLAG                (1 << 16)
 
 /*
@@ -159,11 +142,21 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
                .has_arg        = required_argument,
                .val            = 'e' | FIO_CLIENT_FLAG,
        },
+       {
+               .name           = (char *) "eta-newline",
+               .has_arg        = required_argument,
+               .val            = 'E' | FIO_CLIENT_FLAG,
+       },
        {
                .name           = (char *) "debug",
                .has_arg        = required_argument,
                .val            = 'd' | FIO_CLIENT_FLAG,
        },
+       {
+               .name           = (char *) "parse-only",
+               .has_arg        = no_argument,
+               .val            = 'P' | FIO_CLIENT_FLAG,
+       },
        {
                .name           = (char *) "section",
                .has_arg        = required_argument,
@@ -223,7 +216,7 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
        },
 };
 
-static void free_shm(void)
+void free_threads_shm(void)
 {
        struct shmid_ds sbuf;
 
@@ -231,11 +224,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();
@@ -428,7 +429,7 @@ static int fixup_options(struct thread_data *td)
        /*
         * only really works with 1 file
         */
-       if (o->zone_size && o->open_files == 1)
+       if (o->zone_size && o->open_files > 1)
                o->zone_size = 0;
 
        /*
@@ -613,7 +614,7 @@ static int fixup_options(struct thread_data *td)
 /*
  * 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 };
@@ -627,7 +628,7 @@ static char *to_kmg(unsigned int val)
                p++;
        } while (*p);
 
-       snprintf(buf, 31, "%u%c", val, *p);
+       snprintf(buf, 32, "%u%c", val, *p);
        return buf;
 }
 
@@ -811,11 +812,9 @@ static int setup_random_seeds(struct thread_data *td)
  * 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;
@@ -831,22 +830,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) {
+       if (dump_cmdline || parse_only) {
                put_job(td);
                return 0;
        }
 
+       td->client_type = client_type;
+
        if (profile_td_init(td))
                goto err;
 
        if (ioengine_load(td))
                goto err;
 
-       if (td->o.use_thread)
-               nr_thread++;
-       else
-               nr_process++;
-
        if (td->o.odirect)
                td->io_ops->flags |= FIO_RAWIO;
 
@@ -887,10 +883,8 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num)
        td->mutex = fio_mutex_init(FIO_MUTEX_LOCKED);
 
        td->ts.clat_percentiles = td->o.clat_percentiles;
-       if (td->o.overwrite_plist)
-               memcpy(td->ts.percentile_list, td->o.percentile_list, sizeof(td->o.percentile_list));
-       else
-               memcpy(td->ts.percentile_list, def_percentile_list, sizeof(def_percentile_list));
+       td->ts.percentile_precision = td->o.percentile_precision;
+       memcpy(td->ts.percentile_list, td->o.percentile_list, sizeof(td->o.percentile_list));
 
        for (i = 0; i < DDIR_RWDIR_CNT; i++) {
                td->ts.clat_stat[i].min_val = ULONG_MAX;
@@ -916,40 +910,38 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num)
        if (setup_rate(td))
                goto err;
 
-       if (td->o.write_lat_log) {
-               setup_log(&td->lat_log, td->o.log_avg_msec);
-               setup_log(&td->slat_log, td->o.log_avg_msec);
-               setup_log(&td->clat_log, td->o.log_avg_msec);
+       if (td->o.lat_log_file) {
+               setup_log(&td->lat_log, td->o.log_avg_msec, IO_LOG_TYPE_LAT);
+               setup_log(&td->slat_log, td->o.log_avg_msec, IO_LOG_TYPE_SLAT);
+               setup_log(&td->clat_log, td->o.log_avg_msec, IO_LOG_TYPE_CLAT);
        }
-       if (td->o.write_bw_log)
-               setup_log(&td->bw_log, td->o.log_avg_msec);
-       if (td->o.write_iops_log)
-               setup_log(&td->iops_log, td->o.log_avg_msec);
+       if (td->o.bw_log_file)
+               setup_log(&td->bw_log, td->o.log_avg_msec, IO_LOG_TYPE_BW);
+       if (td->o.iops_log_file)
+               setup_log(&td->iops_log, td->o.log_avg_msec, IO_LOG_TYPE_IOPS);
 
        if (!td->o.name)
                td->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", td->o.name,
-                                                       td->o.cpuload,
-                                                       td->o.cpucycle);
-                       } else {
+                       if (is_backend && !recursed)
+                               fio_server_send_add_job(td);
+
+                       if (!(td->io_ops->flags & FIO_NOIO)) {
                                char *c1, *c2, *c3, *c4, *c5, *c6;
 
-                               c1 = to_kmg(td->o.min_bs[DDIR_READ]);
-                               c2 = to_kmg(td->o.max_bs[DDIR_READ]);
-                               c3 = to_kmg(td->o.min_bs[DDIR_WRITE]);
-                               c4 = to_kmg(td->o.max_bs[DDIR_WRITE]);
-                               c5 = to_kmg(td->o.min_bs[DDIR_TRIM]);
-                               c6 = to_kmg(td->o.max_bs[DDIR_TRIM]);
+                               c1 = fio_uint_to_kmg(td->o.min_bs[DDIR_READ]);
+                               c2 = fio_uint_to_kmg(td->o.max_bs[DDIR_READ]);
+                               c3 = fio_uint_to_kmg(td->o.min_bs[DDIR_WRITE]);
+                               c4 = fio_uint_to_kmg(td->o.max_bs[DDIR_WRITE]);
+                               c5 = fio_uint_to_kmg(td->o.min_bs[DDIR_TRIM]);
+                               c6 = fio_uint_to_kmg(td->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[td->o.td_ddir],
+                                               ddir_str(td->o.td_ddir),
                                                c1, c2, c3, c4, c5, c6,
                                                td->io_ops->name,
                                                td->o.iodepth);
@@ -989,7 +981,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;
        }
 
@@ -1002,7 +994,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;
@@ -1014,7 +1006,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);
                }
@@ -1033,7 +1025,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)
@@ -1071,7 +1063,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;
@@ -1215,7 +1207,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);
@@ -1267,6 +1259,7 @@ static void usage(const char *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");
+       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");
        printf("  --latency-log\t\tGenerate per-job latency logs\n");
@@ -1286,6 +1279,8 @@ static void usage(const char *name)
        printf("  --showcmd\t\tTurn a job file into command line options\n");
        printf("  --eta=when\t\tWhen ETA estimate should be printed\n");
        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("  --readonly\t\tTurn on safety read-only checks, preventing"
                " writes\n");
        printf("  --section=name\tOnly run specified section in job file\n");
@@ -1305,20 +1300,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, },
 };
 
@@ -1425,7 +1462,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;
@@ -1527,10 +1564,24 @@ int parse_cmd_line(int argc, char *argv[])
                        else if (!strcmp("never", optarg))
                                eta_print = FIO_ETA_NEVER;
                        break;
+               case 'E': {
+                       long long t = 0;
+
+                       if (str_to_decimal(optarg, &t, 0, NULL)) {
+                               log_err("fio: failed parsing eta time %s\n", optarg);
+                               exit_val = 1;
+                               do_exit++;
+                       }
+                       eta_new_line = t;
+                       break;
+                       }
                case 'd':
                        if (set_debug(optarg))
                                do_exit++;
                        break;
+               case 'P':
+                       parse_only = 1;
+                       break;
                case 'x': {
                        size_t new_size;
 
@@ -1555,7 +1606,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;
@@ -1635,7 +1686,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;
@@ -1686,7 +1737,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) {
@@ -1699,10 +1750,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;
 
@@ -1714,7 +1763,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++) {
@@ -1725,7 +1789,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]);
                        }
@@ -1741,7 +1805,7 @@ int parse_options(int argc, char *argv[])
        fio_options_free(&def_thread);
 
        if (!thread_number) {
-               if (dump_cmdline)
+               if (dump_cmdline || parse_only)
                        return 0;
                if (exec_profile)
                        return 0;
@@ -1771,3 +1835,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));
+}