Add options to have fio latency profile a device
[fio.git] / init.c
diff --git a/init.c b/init.c
index bdee8a21bf8e43cbdba7b106a920b57e1cc6717d..ba22f785957d86c54523646807445f6db96053f6 100644 (file)
--- a/init.c
+++ b/init.c
@@ -9,19 +9,24 @@
 #include <string.h>
 #include <errno.h>
 #include <sys/ipc.h>
-#include <sys/shm.h>
 #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"
 #include "verify.h"
 #include "profile.h"
 #include "server.h"
+#include "idletime.h"
 
 #include "lib/getopt.h"
+#include "lib/strcasestr.h"
 
 const char fio_version_string[] = FIO_VERSION;
 
@@ -31,13 +36,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;
 FILE *f_out = NULL;
 FILE *f_err = NULL;
 char **job_sections = NULL;
@@ -51,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;
 
@@ -63,26 +71,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)
 
 /*
@@ -136,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",
@@ -155,11 +143,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,
@@ -204,6 +202,21 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
                .has_arg        = required_argument,
                .val            = 'C',
        },
+       {
+               .name           = (char *) "cpuclock-test",
+               .has_arg        = no_argument,
+               .val            = 'T',
+       },
+       {
+               .name           = (char *) "idle-prof",
+               .has_arg        = required_argument,
+               .val            = 'I',
+       },
+       {
+               .name           = (char *) "status-interval",
+               .has_arg        = required_argument,
+               .val            = 'L',
+       },
        {
                .name           = NULL,
        },
@@ -422,7 +435,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;
 
        /*
@@ -496,17 +509,8 @@ static int fixup_options(struct thread_data *td)
        /*
         * The low water mark cannot be bigger than the iodepth
         */
-       if (o->iodepth_low > o->iodepth || !o->iodepth_low) {
-               /*
-                * syslet work around - if the workload is sequential,
-                * we want to let the queue drain all the way down to
-                * avoid seeking between async threads
-                */
-               if (!strcmp(td->io_ops->name, "syslet-rw") && !td_random(td))
-                       o->iodepth_low = 1;
-               else
-                       o->iodepth_low = o->iodepth;
-       }
+       if (o->iodepth_low > o->iodepth || !o->iodepth_low)
+               o->iodepth_low = o->iodepth;
 
        /*
         * If batch number isn't set, default to the same as iodepth
@@ -569,7 +573,14 @@ static int fixup_options(struct thread_data *td)
                }
        }
 
-#ifndef FIO_HAVE_FDATASYNC
+       if (!o->unit_base) {
+               if (td->io_ops->flags & FIO_BIT_BASED)
+                       o->unit_base = 1;
+               else
+                       o->unit_base = 8;
+       }
+
+#ifndef CONFIG_FDATASYNC
        if (o->fdatasync_blocks) {
                log_info("fio: this platform does not support fdatasync()"
                         " falling back to using fsync().  Use the 'fsync'"
@@ -603,6 +614,27 @@ static int fixup_options(struct thread_data *td)
                td->o.compress_percentage = 0;
        }
 
+       /*
+        * Using a non-uniform random distribution excludes usage of
+        * a random map
+        */
+       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;
 }
 
@@ -623,7 +655,7 @@ char *fio_uint_to_kmg(unsigned int val)
                p++;
        } while (*p);
 
-       snprintf(buf, 31, "%u%c", val, *p);
+       snprintf(buf, 32, "%u%c", val, *p);
        return buf;
 }
 
@@ -658,44 +690,51 @@ static int exists_and_not_file(const char *filename)
 
 static void td_fill_rand_seeds_os(struct thread_data *td)
 {
-       os_random_seed(td->rand_seeds[0], &td->bsrange_state);
-       os_random_seed(td->rand_seeds[1], &td->verify_state);
-       os_random_seed(td->rand_seeds[2], &td->rwmix_state);
+       os_random_seed(td->rand_seeds[FIO_RAND_BS_OFF], &td->bsrange_state);
+       os_random_seed(td->rand_seeds[FIO_RAND_VER_OFF], &td->verify_state);
+       os_random_seed(td->rand_seeds[FIO_RAND_MIX_OFF], &td->rwmix_state);
 
        if (td->o.file_service_type == FIO_FSERVICE_RANDOM)
-               os_random_seed(td->rand_seeds[3], &td->next_file_state);
+               os_random_seed(td->rand_seeds[FIO_RAND_FILE_OFF], &td->next_file_state);
 
-       os_random_seed(td->rand_seeds[5], &td->file_size_state);
-       os_random_seed(td->rand_seeds[6], &td->trim_state);
+       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);
 
        if (!td_random(td))
                return;
 
        if (td->o.rand_repeatable)
-               td->rand_seeds[4] = FIO_RANDSEED * td->thread_number;
+               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[4], &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)
 {
-       init_rand_seed(&td->__bsrange_state, td->rand_seeds[0]);
-       init_rand_seed(&td->__verify_state, td->rand_seeds[1]);
-       init_rand_seed(&td->__rwmix_state, td->rand_seeds[2]);
+       init_rand_seed(&td->__bsrange_state, td->rand_seeds[FIO_RAND_BS_OFF]);
+       init_rand_seed(&td->__verify_state, td->rand_seeds[FIO_RAND_VER_OFF]);
+       init_rand_seed(&td->__rwmix_state, td->rand_seeds[FIO_RAND_MIX_OFF]);
 
        if (td->o.file_service_type == FIO_FSERVICE_RANDOM)
-               init_rand_seed(&td->__next_file_state, td->rand_seeds[3]);
+               init_rand_seed(&td->__next_file_state, td->rand_seeds[FIO_RAND_FILE_OFF]);
 
-       init_rand_seed(&td->__file_size_state, td->rand_seeds[5]);
-       init_rand_seed(&td->__trim_state, td->rand_seeds[6]);
+       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]);
 
        if (!td_random(td))
                return;
 
        if (td->o.rand_repeatable)
-               td->rand_seeds[4] = FIO_RANDSEED * td->thread_number;
+               td->rand_seeds[FIO_RAND_BLOCK_OFF] = FIO_RANDSEED * td->thread_number;
 
-       init_rand_seed(&td->__random_state, td->rand_seeds[4]);
+       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)
@@ -705,10 +744,9 @@ void td_fill_rand_seeds(struct thread_data *td)
        else
                td_fill_rand_seeds_internal(td);
 
-       init_rand_seed(&td->buf_state, td->rand_seeds[7]);
+       init_rand_seed(&td->buf_state, td->rand_seeds[FIO_RAND_BUF_OFF]);
 }
 
-
 /*
  * Initializes the ioengine configured for a job, if it has not been done so
  * already.
@@ -722,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);
@@ -765,6 +807,126 @@ int ioengine_load(struct thread_data *td)
        return 0;
 }
 
+static void init_flags(struct thread_data *td)
+{
+       struct thread_options *o = &td->o;
+
+       if (o->verify_backlog)
+               td->flags |= TD_F_VER_BACKLOG;
+       if (o->trim_backlog)
+               td->flags |= TD_F_TRIM_BACKLOG;
+       if (o->read_iolog_file)
+               td->flags |= TD_F_READ_IOLOG;
+       if (o->refill_buffers)
+               td->flags |= TD_F_REFILL_BUFFERS;
+       if (o->scramble_buffers)
+               td->flags |= TD_F_SCRAMBLE_BUFFERS;
+       if (o->verify != VERIFY_NONE)
+               td->flags |= TD_F_VER_NONE;
+}
+
+static int setup_random_seeds(struct thread_data *td)
+{
+       unsigned long seed;
+       unsigned int i;
+
+       if (!td->o.rand_repeatable)
+               return init_random_state(td, td->rand_seeds, sizeof(td->rand_seeds));
+
+       for (seed = 0x89, i = 0; i < 4; i++)
+               seed *= 0x9e370001UL;
+
+       for (i = 0; i < FIO_RAND_NR_OFFS; i++) {
+               td->rand_seeds[i] = seed;
+               seed *= 0x9e370001UL;
+       }
+
+       td_fill_rand_seeds(td);
+       return 0;
+}
+
+enum {
+       FPRE_NONE = 0,
+       FPRE_JOBNAME,
+       FPRE_JOBNUM,
+       FPRE_FILENUM
+};
+
+static struct fpre_keyword {
+       const char *keyword;
+       size_t strlen;
+       int key;
+} fpre_keywords[] = {
+       { .keyword = "$jobname",        .key = FPRE_JOBNAME, },
+       { .keyword = "$jobnum",         .key = FPRE_JOBNUM, },
+       { .keyword = "$filenum",        .key = FPRE_FILENUM, },
+       { .keyword = NULL, },
+       };
+
+static char *make_filename(char *buf, struct thread_options *o,
+                          const char *jobname, int jobnum, int filenum)
+{
+       struct fpre_keyword *f;
+       char copy[PATH_MAX];
+
+       if (!o->filename_format || !strlen(o->filename_format)) {
+               sprintf(buf, "%s.%d.%d", jobname, jobnum, filenum);
+               return NULL;
+       }
+
+       for (f = &fpre_keywords[0]; f->keyword; f++)
+               f->strlen = strlen(f->keyword);
+
+       strcpy(buf, o->filename_format);
+       memset(copy, 0, sizeof(copy));
+       for (f = &fpre_keywords[0]; f->keyword; f++) {
+               do {
+                       size_t pre_len, post_start = 0;
+                       char *str, *dst = copy;
+
+                       str = strcasestr(buf, f->keyword);
+                       if (!str)
+                               break;
+
+                       pre_len = str - buf;
+                       if (strlen(str) != f->strlen)
+                               post_start = pre_len + f->strlen;
+
+                       if (pre_len) {
+                               strncpy(dst, buf, pre_len);
+                               dst += pre_len;
+                       }
+
+                       switch (f->key) {
+                       case FPRE_JOBNAME:
+                               dst += sprintf(dst, "%s", jobname);
+                               break;
+                       case FPRE_JOBNUM:
+                               dst += sprintf(dst, "%d", jobnum);
+                               break;
+                       case FPRE_FILENUM:
+                               dst += sprintf(dst, "%d", filenum);
+                               break;
+                       default:
+                               assert(0);
+                               break;
+                       }
+
+                       if (post_start)
+                               strcpy(dst, buf + post_start);
+
+                       strcpy(buf, copy);
+               } while (1);
+       }
+
+       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
@@ -776,6 +938,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
        unsigned int i;
        char fname[PATH_MAX];
        int numjobs, file_alloced;
+       struct thread_options *o = &td->o;
 
        /*
         * the def_thread is just for options, it's not a real job
@@ -783,10 +946,12 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
        if (td == &def_thread)
                return 0;
 
+       init_flags(td);
+
        /*
         * if we are just dumping the output command line, don't add the job
         */
-       if (dump_cmdline) {
+       if (parse_dryrun()) {
                put_job(td);
                return 0;
        }
@@ -799,21 +964,18 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
        if (ioengine_load(td))
                goto err;
 
-       if (td->o.odirect)
+       if (o->odirect)
                td->io_ops->flags |= FIO_RAWIO;
 
        file_alloced = 0;
-       if (!td->o.filename && !td->files_index && !td->o.read_iolog_file) {
+       if (!o->filename && !td->files_index && !o->read_iolog_file) {
                file_alloced = 1;
 
-               if (td->o.nr_files == 1 && exists_and_not_file(jobname))
+               if (o->nr_files == 1 && exists_and_not_file(jobname))
                        add_file(td, jobname);
                else {
-                       for (i = 0; i < td->o.nr_files; i++) {
-                               sprintf(fname, "%s.%d.%d", jobname,
-                                                       td->thread_number, i);
-                               add_file(td, fname);
-                       }
+                       for (i = 0; i < o->nr_files; i++)
+                               add_file(td, make_filename(fname, o, jobname, td->thread_number, i));
                }
        }
 
@@ -838,11 +1000,9 @@ 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.clat_percentiles = o->clat_percentiles;
+       td->ts.percentile_precision = o->percentile_precision;
+       memcpy(td->ts.percentile_list, o->percentile_list, sizeof(o->percentile_list));
 
        for (i = 0; i < DDIR_RWDIR_CNT; i++) {
                td->ts.clat_stat[i].min_val = ULONG_MAX;
@@ -850,9 +1010,9 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
                td->ts.lat_stat[i].min_val = ULONG_MAX;
                td->ts.bw_stat[i].min_val = ULONG_MAX;
        }
-       td->ddir_seq_nr = td->o.ddir_seq_nr;
+       td->ddir_seq_nr = o->ddir_seq_nr;
 
-       if ((td->o.stonewall || td->o.new_group) && prev_group_jobs) {
+       if ((o->stonewall || o->new_group) && prev_group_jobs) {
                prev_group_jobs = 0;
                groupid++;
        }
@@ -860,7 +1020,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
        td->groupid = groupid;
        prev_group_jobs++;
 
-       if (init_random_state(td, td->rand_seeds, sizeof(td->rand_seeds))) {
+       if (setup_random_seeds(td)) {
                td_verror(td, errno, "init_random_state");
                goto err;
        }
@@ -868,18 +1028,18 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
        if (setup_rate(td))
                goto err;
 
-       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 (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 (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 (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 (!td->o.name)
-               td->o.name = strdup(jobname);
+       if (!o->name)
+               o->name = strdup(jobname);
 
        if (output_format == FIO_OUTPUT_NORMAL) {
                if (!job_add_num) {
@@ -887,22 +1047,32 @@ 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;
-
-                               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),
-                                               c1, c2, c3, c4, c5, c6,
-                                               td->io_ops->name,
-                                               td->o.iodepth);
+                               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);
                                free(c2);
@@ -919,7 +1089,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
         * recurse add identical jobs, clear numjobs and stonewall options
         * as they don't apply to sub-jobs
         */
-       numjobs = td->o.numjobs;
+       numjobs = o->numjobs;
        while (--numjobs) {
                struct thread_data *td_new = get_new_job(0, td, 1);
 
@@ -976,9 +1146,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++;
        }
 
@@ -1115,7 +1285,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;
@@ -1159,14 +1329,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);
                }
@@ -1216,7 +1382,8 @@ 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");
        printf("  --latency-log\t\tGenerate per-job latency logs\n");
@@ -1226,6 +1393,7 @@ static void usage(const char *name)
        printf("  --terse-version=x\tSet terse version output format to 'x'\n");
        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("  --cmdhelp=cmd\t\tPrint command help, \"all\" for all of"
                " them\n");
        printf("  --enghelp=engine\tPrint ioengine help, or list"
@@ -1235,6 +1403,10 @@ 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("  --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");
@@ -1245,8 +1417,11 @@ static void usage(const char *name)
        printf("  --server=args\t\tStart a backend fio server\n");
        printf("  --daemonize=pidfile\tBackground fio server, write pid to file\n");
        printf("  --client=hostname\tTalk to remote backend fio server at hostname\n");
+       printf("  --idle-prof=option\tReport cpu idleness on a system or percpu basis\n"
+               "\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>\n");
 }
 
 #ifdef FIO_INC_DEBUG
@@ -1307,6 +1482,10 @@ struct debug_level debug_levels[] = {
          .help = "Network logging",
          .shift = FD_NET,
        },
+       { .name = "rate",
+         .help = "Rate logging",
+         .shift = FD_RATE,
+       },
        { .name = NULL, },
 };
 
@@ -1515,10 +1694,24 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        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;
 
@@ -1564,7 +1757,20 @@ 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);
+                                       td = NULL;
+                               }
+                               do_exit++;
+                       }
 
                        if (!ret && !strcmp(opt, "ioengine")) {
                                free_ioengine(td);
@@ -1577,8 +1783,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;
                }
@@ -1608,6 +1812,14 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                case 'D':
                        pid_file = strdup(optarg);
                        break;
+               case 'I':
+                       if ((ret = fio_idle_prof_parse_opt(optarg))) {
+                               /* exit on error and calibration only */
+                               do_exit++;
+                               if (ret == -1) 
+                                       exit_val = 1;
+                       }
+                       break;
                case 'C':
                        if (is_backend) {
                                log_err("fio: can't be both client and server\n");
@@ -1634,6 +1846,25 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                                optind++;
                        }
                        break;
+               case 'T':
+                       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]);
                default:
                        do_exit++;
                        exit_val = 1;
@@ -1643,10 +1874,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++;
@@ -1727,7 +1956,7 @@ int parse_options(int argc, char *argv[])
        fio_options_free(&def_thread);
 
        if (!thread_number) {
-               if (dump_cmdline)
+               if (parse_dryrun())
                        return 0;
                if (exec_profile)
                        return 0;