log: add support for logging max instead of averages
[fio.git] / options.c
index afca72705d1f3ae474ac9717348bbd7ab81de75b..390208729f0db12955cc3a19f72f1d04d9a40b1f 100644 (file)
--- a/options.c
+++ b/options.c
@@ -8,14 +8,17 @@
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <netinet/in.h>
 
 #include "fio.h"
 #include "verify.h"
 #include "parse.h"
 #include "lib/fls.h"
+#include "lib/pattern.h"
 #include "options.h"
+#include "optgroup.h"
 
-#include "crc/crc32c.h"
+char client_sockaddr_str[INET6_ADDRSTRLEN] = { 0 };
 
 /*
  * Check if mmap/mmaphuge has a :/foo/bar/file at the end. If so, return that.
@@ -33,32 +36,12 @@ static char *get_opt_postfix(const char *str)
        return strdup(p);
 }
 
-static int converthexchartoint(char a)
-{
-       int base;
-
-       switch (a) {
-       case '0'...'9':
-               base = '0';
-               break;
-       case 'A'...'F':
-               base = 'A' - 10;
-               break;
-       case 'a'...'f':
-               base = 'a' - 10;
-               break;
-       default:
-               base = 0;
-       }
-       return a - base;
-}
-
 static int bs_cmp(const void *p1, const void *p2)
 {
        const struct bssplit *bsp1 = p1;
        const struct bssplit *bsp2 = p2;
 
-       return bsp1->perc < bsp2->perc;
+       return (int) bsp1->perc - (int) bsp2->perc;
 }
 
 static int bssplit_ddir(struct thread_options *o, int ddir, char *str)
@@ -212,7 +195,8 @@ static int str_bssplit_cb(void *data, const char *input)
                        ret = bssplit_ddir(&td->o, DDIR_TRIM, op);
                        free(op);
                }
-               ret = bssplit_ddir(&td->o, DDIR_READ, str);
+               if (!ret)
+                       ret = bssplit_ddir(&td->o, DDIR_READ, str);
        }
 
        free(p);
@@ -359,7 +343,8 @@ static int str_mem_cb(void *data, const char *mem)
 {
        struct thread_data *td = data;
 
-       if (td->o.mem_type == MEM_MMAPHUGE || td->o.mem_type == MEM_MMAP)
+       if (td->o.mem_type == MEM_MMAPHUGE || td->o.mem_type == MEM_MMAP ||
+           td->o.mem_type == MEM_MMAPSHARED)
                td->o.mmapfile = get_opt_postfix(mem);
 
        return 0;
@@ -443,9 +428,9 @@ static int str_cpumask_cb(void *data, unsigned long long *val)
 
        for (i = 0; i < sizeof(int) * 8; i++) {
                if ((1 << i) & *val) {
-                       if (i > max_cpu) {
+                       if (i >= max_cpu) {
                                log_err("fio: CPU %d too large (max=%ld)\n", i,
-                                                               max_cpu);
+                                                               max_cpu - 1);
                                return 1;
                        }
                        dprint(FD_PARSE, "set cpu allowed %d\n", i);
@@ -503,9 +488,9 @@ static int set_cpus_allowed(struct thread_data *td, os_cpu_mask_t *mask,
                                ret = 1;
                                break;
                        }
-                       if (icpu > max_cpu) {
+                       if (icpu >= max_cpu) {
                                log_err("fio: CPU %d too large (max=%ld)\n",
-                                                       icpu, max_cpu);
+                                                       icpu, max_cpu - 1);
                                ret = 1;
                                break;
                        }
@@ -536,9 +521,25 @@ static int str_verify_cpus_allowed_cb(void *data, const char *input)
 {
        struct thread_data *td = data;
 
+       if (parse_dryrun())
+               return 0;
+
        return set_cpus_allowed(td, &td->o.verify_cpumask, input);
 }
-#endif
+
+#ifdef CONFIG_ZLIB
+static int str_log_cpus_allowed_cb(void *data, const char *input)
+{
+       struct thread_data *td = data;
+
+       if (parse_dryrun())
+               return 0;
+
+       return set_cpus_allowed(td, &td->o.log_gz_cpumask, input);
+}
+#endif /* CONFIG_ZLIB */
+
+#endif /* FIO_HAVE_CPU_AFFINITY */
 
 #ifdef CONFIG_LIBNUMA
 static int str_numa_cpunodes_cb(void *data, char *input)
@@ -715,9 +716,11 @@ static int str_random_distribution_cb(void *data, const char *str)
                return 0;
 
        if (td->o.random_distribution == FIO_RAND_DIST_ZIPF)
-               val = 1.1;
+               val = FIO_DEF_ZIPF;
        else if (td->o.random_distribution == FIO_RAND_DIST_PARETO)
-               val = 0.2;
+               val = FIO_DEF_PARETO;
+       else if (td->o.random_distribution == FIO_RAND_DIST_GAUSS)
+               val = 0.0;
        else
                return 0;
 
@@ -736,12 +739,18 @@ static int str_random_distribution_cb(void *data, const char *str)
                        return 1;
                }
                td->o.zipf_theta.u.f = val;
-       } else {
+       } else if (td->o.random_distribution == FIO_RAND_DIST_PARETO) {
                if (val <= 0.00 || val >= 1.00) {
                        log_err("fio: pareto input out of range (0 < input < 1.0)\n");
                        return 1;
                }
                td->o.pareto_h.u.f = val;
+       } else {
+               if (val <= 0.00 || val >= 100.0) {
+                       log_err("fio: normal deviation out of range (0 < input < 100.0)\n");
+                       return 1;
+               }
+               td->o.gauss_dev.u.f = val;
        }
 
        return 0;
@@ -812,7 +821,7 @@ static int get_max_name_idx(char *input)
  * Returns the directory at the index, indexes > entires will be
  * assigned via modulo division of the index
  */
-int set_name_idx(char *target, char *input, int index)
+int set_name_idx(char *target, size_t tlen, char *input, int index)
 {
        unsigned int cur_idx;
        int len;
@@ -824,7 +833,13 @@ int set_name_idx(char *target, char *input, int index)
        for (cur_idx = 0; cur_idx <= index; cur_idx++)
                fname = get_next_name(&str);
 
-       len = sprintf(target, "%s/", fname);
+       if (client_sockaddr_str[0]) {
+               len = snprintf(target, tlen, "%s/%s.", fname,
+                               client_sockaddr_str);
+       } else
+               len = snprintf(target, tlen, "%s/", fname);
+
+       target[tlen - 1] = '\0';
        free(p);
 
        return len;
@@ -884,18 +899,6 @@ out:
        return ret;
 }
 
-static int str_lockfile_cb(void *data, const char fio_unused *str)
-{
-       struct thread_data *td = data;
-
-       if (td->files_index) {
-               log_err("fio: lockfile= option must precede filename=\n");
-               return 1;
-       }
-
-       return 0;
-}
-
 static int str_opendir_cb(void *data, const char fio_unused *str)
 {
        struct thread_data *td = data;
@@ -909,122 +912,25 @@ static int str_opendir_cb(void *data, const char fio_unused *str)
        return add_dir_files(td, td->o.opendir);
 }
 
-static int pattern_cb(char *pattern, unsigned int max_size,
-                     const char *input, unsigned int *pattern_bytes)
-{
-       long off;
-       int i = 0, j = 0, len, k, base = 10;
-       uint32_t pattern_length;
-       char *loc1, *loc2;
-
-       /*
-        * Check if it's a string input
-        */
-       loc1 = strchr(input, '\"');
-       if (loc1) {
-               do {
-                       loc1++;
-                       if (*loc1 == '\0' || *loc1 == '\"')
-                               break;
-
-                       pattern[i] = *loc1;
-                       i++;
-               } while (i < max_size);
-
-               if (!i)
-                       return 1;
-
-               goto fill;
-       }
-
-       /*
-        * No string, find out if it's decimal or hexidecimal
-        */
-       loc1 = strstr(input, "0x");
-       loc2 = strstr(input, "0X");
-       if (loc1 || loc2)
-               base = 16;
-       off = strtol(input, NULL, base);
-       if (off != LONG_MAX || errno != ERANGE) {
-               while (off) {
-                       pattern[i] = off & 0xff;
-                       off >>= 8;
-                       i++;
-               }
-       } else {
-               len = strlen(input);
-               k = len - 1;
-               if (base == 16) {
-                       if (loc1)
-                               j = loc1 - input + 2;
-                       else
-                               j = loc2 - input + 2;
-               } else
-                       return 1;
-               if (len - j < max_size * 2) {
-                       while (k >= j) {
-                               off = converthexchartoint(input[k--]);
-                               if (k >= j)
-                                       off += (converthexchartoint(input[k--])
-                                               * 16);
-                               pattern[i++] = (char) off;
-                       }
-               }
-       }
-
-       /*
-        * Fill the pattern all the way to the end. This greatly reduces
-        * the number of memcpy's we have to do when verifying the IO.
-        */
-fill:
-       pattern_length = i;
-       while (i > 1 && i * 2 <= max_size) {
-               memcpy(&pattern[i], &pattern[0], i);
-               i *= 2;
-       }
-
-       /*
-        * Fill remainder, if the pattern multiple ends up not being
-        * max_size.
-        */
-       while (i > 1 && i < max_size) {
-               unsigned int b = min(pattern_length, max_size - i);
-
-               memcpy(&pattern[i], &pattern[0], b);
-               i += b;
-       }
-
-       if (i == 1) {
-               /*
-                * The code in verify_io_u_pattern assumes a single byte
-                * pattern fills the whole verify pattern buffer.
-                */
-               memset(pattern, pattern[0], max_size);
-       }
-
-       *pattern_bytes = i;
-       return 0;
-}
-
 static int str_buffer_pattern_cb(void *data, const char *input)
 {
        struct thread_data *td = data;
        int ret;
 
-       ret = pattern_cb(td->o.buffer_pattern, MAX_PATTERN_SIZE, input,
-                               &td->o.buffer_pattern_bytes);
+       /* FIXME: for now buffer pattern does not support formats */
+       ret = parse_and_fill_pattern(input, strlen(input), td->o.buffer_pattern,
+                                    MAX_PATTERN_SIZE, NULL, 0, NULL, NULL);
+       if (ret < 0)
+               return 1;
 
-       if (!ret && td->o.buffer_pattern_bytes) {
-               if (!td->o.compress_percentage)
-                       td->o.refill_buffers = 0;
-               td->o.scramble_buffers = 0;
-               td->o.zero_buffers = 0;
-       } else {
-               log_err("fio: failed parsing pattern `%s`\n", input);
-               ret = 1;
-       }
+       assert(ret != 0);
+       td->o.buffer_pattern_bytes = ret;
+       if (!td->o.compress_percentage)
+               td->o.refill_buffers = 0;
+       td->o.scramble_buffers = 0;
+       td->o.zero_buffers = 0;
 
-       return ret;
+       return 0;
 }
 
 static int str_buffer_compress_cb(void *data, unsigned long long *il)
@@ -1048,19 +954,32 @@ static int str_dedupe_cb(void *data, unsigned long long *il)
 
 static int str_verify_pattern_cb(void *data, const char *input)
 {
+       struct pattern_fmt_desc fmt_desc[] = {
+               {
+                       .fmt   = "%o",
+                       .len   = FIELD_SIZE(struct io_u *, offset),
+                       .paste = paste_blockoff
+               }
+       };
        struct thread_data *td = data;
        int ret;
 
-       ret = pattern_cb(td->o.verify_pattern, MAX_PATTERN_SIZE, input,
-                               &td->o.verify_pattern_bytes);
+       td->o.verify_fmt_sz = ARRAY_SIZE(td->o.verify_fmt);
+       ret = parse_and_fill_pattern(input, strlen(input), td->o.verify_pattern,
+                       MAX_PATTERN_SIZE, fmt_desc, sizeof(fmt_desc),
+                       td->o.verify_fmt, &td->o.verify_fmt_sz);
+       if (ret < 0)
+               return 1;
 
+       assert(ret != 0);
+       td->o.verify_pattern_bytes = ret;
        /*
-        * VERIFY_META could already be set
+        * VERIFY_* could already be set
         */
-       if (!ret && td->o.verify == VERIFY_NONE)
+       if (!fio_option_is_set(&td->o, verify))
                td->o.verify = VERIFY_PATTERN;
 
-       return ret;
+       return 0;
 }
 
 static int str_gtod_reduce_cb(void *data, int *il)
@@ -1079,16 +998,6 @@ static int str_gtod_reduce_cb(void *data, int *il)
        return 0;
 }
 
-static int str_gtod_cpu_cb(void *data, long long *il)
-{
-       struct thread_data *td = data;
-       int val = *il;
-
-       td->o.gtod_cpu = val;
-       td->o.gtod_offload = 1;
-       return 0;
-}
-
 static int str_size_cb(void *data, unsigned long long *__val)
 {
        struct thread_data *td = data;
@@ -1131,165 +1040,6 @@ static int gtod_cpu_verify(struct fio_option *o, void *data)
        return 0;
 }
 
-/*
- * Option grouping
- */
-static struct opt_group fio_opt_groups[] = {
-       {
-               .name   = "General",
-               .mask   = FIO_OPT_C_GENERAL,
-       },
-       {
-               .name   = "I/O",
-               .mask   = FIO_OPT_C_IO,
-       },
-       {
-               .name   = "File",
-               .mask   = FIO_OPT_C_FILE,
-       },
-       {
-               .name   = "Statistics",
-               .mask   = FIO_OPT_C_STAT,
-       },
-       {
-               .name   = "Logging",
-               .mask   = FIO_OPT_C_LOG,
-       },
-       {
-               .name   = "Profiles",
-               .mask   = FIO_OPT_C_PROFILE,
-       },
-       {
-               .name   = NULL,
-       },
-};
-
-static struct opt_group *__opt_group_from_mask(struct opt_group *ogs, unsigned int *mask,
-                                              unsigned int inv_mask)
-{
-       struct opt_group *og;
-       int i;
-
-       if (*mask == inv_mask || !*mask)
-               return NULL;
-
-       for (i = 0; ogs[i].name; i++) {
-               og = &ogs[i];
-
-               if (*mask & og->mask) {
-                       *mask &= ~(og->mask);
-                       return og;
-               }
-       }
-
-       return NULL;
-}
-
-struct opt_group *opt_group_from_mask(unsigned int *mask)
-{
-       return __opt_group_from_mask(fio_opt_groups, mask, FIO_OPT_C_INVALID);
-}
-
-static struct opt_group fio_opt_cat_groups[] = {
-       {
-               .name   = "Latency profiling",
-               .mask   = FIO_OPT_G_LATPROF,
-       },
-       {
-               .name   = "Rate",
-               .mask   = FIO_OPT_G_RATE,
-       },
-       {
-               .name   = "Zone",
-               .mask   = FIO_OPT_G_ZONE,
-       },
-       {
-               .name   = "Read/write mix",
-               .mask   = FIO_OPT_G_RWMIX,
-       },
-       {
-               .name   = "Verify",
-               .mask   = FIO_OPT_G_VERIFY,
-       },
-       {
-               .name   = "Trim",
-               .mask   = FIO_OPT_G_TRIM,
-       },
-       {
-               .name   = "I/O Logging",
-               .mask   = FIO_OPT_G_IOLOG,
-       },
-       {
-               .name   = "I/O Depth",
-               .mask   = FIO_OPT_G_IO_DEPTH,
-       },
-       {
-               .name   = "I/O Flow",
-               .mask   = FIO_OPT_G_IO_FLOW,
-       },
-       {
-               .name   = "Description",
-               .mask   = FIO_OPT_G_DESC,
-       },
-       {
-               .name   = "Filename",
-               .mask   = FIO_OPT_G_FILENAME,
-       },
-       {
-               .name   = "General I/O",
-               .mask   = FIO_OPT_G_IO_BASIC,
-       },
-       {
-               .name   = "Cgroups",
-               .mask   = FIO_OPT_G_CGROUP,
-       },
-       {
-               .name   = "Runtime",
-               .mask   = FIO_OPT_G_RUNTIME,
-       },
-       {
-               .name   = "Process",
-               .mask   = FIO_OPT_G_PROCESS,
-       },
-       {
-               .name   = "Job credentials / priority",
-               .mask   = FIO_OPT_G_CRED,
-       },
-       {
-               .name   = "Clock settings",
-               .mask   = FIO_OPT_G_CLOCK,
-       },
-       {
-               .name   = "I/O Type",
-               .mask   = FIO_OPT_G_IO_TYPE,
-       },
-       {
-               .name   = "I/O Thinktime",
-               .mask   = FIO_OPT_G_THINKTIME,
-       },
-       {
-               .name   = "Randomizations",
-               .mask   = FIO_OPT_G_RANDOM,
-       },
-       {
-               .name   = "I/O buffers",
-               .mask   = FIO_OPT_G_IO_BUF,
-       },
-       {
-               .name   = "Tiobench profile",
-               .mask   = FIO_OPT_G_TIOBENCH,
-       },
-
-       {
-               .name   = NULL,
-       }
-};
-
-struct opt_group *opt_group_cat_from_mask(unsigned int *mask)
-{
-       return __opt_group_from_mask(fio_opt_cat_groups, mask, FIO_OPT_G_INVALID);
-}
-
 /*
  * Map of job/command line options
  */
@@ -1312,6 +1062,15 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_GENERAL,
                .group  = FIO_OPT_G_DESC,
        },
+       {
+               .name   = "wait_for",
+               .lname  = "Waitee name",
+               .type   = FIO_OPT_STR_STORE,
+               .off1   = td_var_offset(wait_for),
+               .help   = "Name of the job this one wants to wait for before starting",
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_DESC,
+       },
        {
                .name   = "filename",
                .lname  = "Filename(s)",
@@ -1353,7 +1112,6 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .parent = "filename",
                .hide   = 0,
                .def    = "none",
-               .cb     = str_lockfile_cb,
                .category = FIO_OPT_C_FILE,
                .group  = FIO_OPT_G_FILENAME,
                .posval = {
@@ -1431,6 +1189,10 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                            .oval = TD_DDIR_RANDRW,
                            .help = "Random read and write mix"
                          },
+                         { .ival = "trimwrite",
+                           .oval = TD_DDIR_TRIMWRITE,
+                           .help = "Trim and write mix, trims preceding writes"
+                         },
                },
        },
        {
@@ -1604,11 +1366,12 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .group  = FIO_OPT_G_IO_BASIC,
        },
        {
-               .name   = "iodepth_batch_complete",
-               .lname  = "IO Depth batch complete",
+               .name   = "iodepth_batch_complete_min",
+               .lname  = "Min IO depth batch complete",
+               .alias  = "iodepth_batch_complete",
                .type   = FIO_OPT_INT,
-               .off1   = td_var_offset(iodepth_batch_complete),
-               .help   = "Number of IO buffers to retrieve in one go",
+               .off1   = td_var_offset(iodepth_batch_complete_min),
+               .help   = "Min number of IO buffers to retrieve in one go",
                .parent = "iodepth",
                .hide   = 1,
                .minval = 0,
@@ -1617,6 +1380,19 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_IO_BASIC,
        },
+       {
+               .name   = "iodepth_batch_complete_max",
+               .lname  = "Max IO depth batch complete",
+               .type   = FIO_OPT_INT,
+               .off1   = td_var_offset(iodepth_batch_complete_max),
+               .help   = "Max number of IO buffers to retrieve in one go",
+               .parent = "iodepth",
+               .hide   = 1,
+               .minval = 0,
+               .interval = 1,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IO_BASIC,
+       },
        {
                .name   = "iodepth_low",
                .lname  = "IO Depth batch low",
@@ -1629,6 +1405,26 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_IO_BASIC,
        },
+       {
+               .name   = "io_submit_mode",
+               .lname  = "IO submit mode",
+               .type   = FIO_OPT_STR,
+               .off1   = td_var_offset(io_submit_mode),
+               .help   = "How IO submissions and completions are done",
+               .def    = "inline",
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IO_BASIC,
+               .posval = {
+                         { .ival = "inline",
+                           .oval = IO_MODE_INLINE,
+                           .help = "Submit and complete IO inline",
+                         },
+                         { .ival = "offload",
+                           .oval = IO_MODE_OFFLOAD,
+                           .help = "Offload submit and complete to threads",
+                         },
+               },
+       },
        {
                .name   = "size",
                .lname  = "Size",
@@ -1641,8 +1437,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .group  = FIO_OPT_G_INVALID,
        },
        {
-               .name   = "io_limit",
-               .lname  = "IO Limit",
+               .name   = "io_size",
+               .alias  = "io_limit",
+               .lname  = "IO Size",
                .type   = FIO_OPT_STR_VAL,
                .off1   = td_var_offset(io_limit),
                .interval = 1024 * 1024,
@@ -1822,6 +1619,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .type   = FIO_OPT_STR_VAL,
                .off1   = td_var_offset(rand_seed),
                .help   = "Set the random generator seed value",
+               .def    = "0x89",
                .parent = "rw",
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_RANDOM,
@@ -1873,6 +1671,11 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                            .oval = FIO_RAND_GEN_LFSR,
                            .help = "Variable length LFSR",
                          },
+                         {
+                           .ival = "tausworthe64",
+                           .oval = FIO_RAND_GEN_TAUSWORTHE64,
+                           .help = "64-bit Tausworthe variant",
+                         },
                },
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_RANDOM,
@@ -1897,6 +1700,10 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                            .oval = FIO_RAND_DIST_PARETO,
                            .help = "Pareto distribution",
                          },
+                         { .ival = "normal",
+                           .oval = FIO_RAND_DIST_GAUSS,
+                           .help = "Normal (gaussian) distribution",
+                         },
                },
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_RANDOM,
@@ -2027,6 +1834,17 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_FILE,
                .group  = FIO_OPT_G_INVALID,
        },
+#ifdef FIO_HAVE_STREAMID
+       {
+               .name   = "fadvise_stream",
+               .lname  = "Fadvise stream",
+               .type   = FIO_OPT_INT,
+               .off1   = td_var_offset(fadvise_stream),
+               .help   = "Use fadvise() to set stream ID",
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_INVALID,
+       },
+#endif
        {
                .name   = "fsync",
                .lname  = "Fsync",
@@ -2272,6 +2090,10 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                            .oval = MEM_MMAP,
                            .help = "Use mmap(2) (file or anon) for IO buffers",
                          },
+                         { .ival = "mmapshared",
+                           .oval = MEM_MMAPSHARED,
+                           .help = "Like mmap, but use the shared flag",
+                         },
 #ifdef FIO_HAVE_HUGETLB
                          { .ival = "mmaphuge",
                            .oval = MEM_MMAPHUGE,
@@ -2352,9 +2174,17 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                            .oval = VERIFY_XXHASH,
                            .help = "Use xxhash checksums for verification",
                          },
+                         /* Meta information was included into verify_header,
+                          * 'meta' verification is implied by default. */
                          { .ival = "meta",
-                           .oval = VERIFY_META,
-                           .help = "Use io information",
+                           .oval = VERIFY_HDR_ONLY,
+                           .help = "Use io information for verification. "
+                                   "Now is implied by default, thus option is obsolete, "
+                                   "don't use it",
+                         },
+                         { .ival = "pattern",
+                           .oval = VERIFY_PATTERN_NO_HDR,
+                           .help = "Verify strict pattern",
                          },
                          {
                            .ival = "null",
@@ -2632,6 +2462,28 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_IOLOG,
        },
+       {
+               .name   = "replay_scale",
+               .lname  = "Replace offset scale factor",
+               .type   = FIO_OPT_INT,
+               .off1   = td_var_offset(replay_scale),
+               .parent = "read_iolog",
+               .def    = "1",
+               .help   = "Align offsets to this blocksize",
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IOLOG,
+       },
+       {
+               .name   = "replay_align",
+               .lname  = "Replace alignment",
+               .type   = FIO_OPT_INT,
+               .off1   = td_var_offset(replay_align),
+               .parent = "read_iolog",
+               .help   = "Scale offset down by this factor",
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IOLOG,
+               .pow2   = 1,
+       },
        {
                .name   = "exec_prerun",
                .lname  = "Pre-execute runnable",
@@ -2827,7 +2679,8 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .group  = FIO_OPT_G_RATE,
        },
        {
-               .name   = "ratemin",
+               .name   = "rate_min",
+               .alias  = "ratemin",
                .lname  = "I/O min rate",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(ratemin[DDIR_READ]),
@@ -2865,7 +2718,30 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .group  = FIO_OPT_G_RATE,
        },
        {
-               .name   = "ratecycle",
+               .name   = "rate_process",
+               .lname  = "Rate Process",
+               .type   = FIO_OPT_STR,
+               .off1   = td_var_offset(rate_process),
+               .help   = "What process controls how rated IO is managed",
+               .def    = "linear",
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_RATE,
+               .posval = {
+                         { .ival = "linear",
+                           .oval = RATE_PROCESS_LINEAR,
+                           .help = "Linear rate of IO",
+                         },
+                         {
+                           .ival = "poisson",
+                           .oval = RATE_PROCESS_POISSON,
+                           .help = "Rate follows Poisson process",
+                         },
+               },
+               .parent = "rate",
+       },
+       {
+               .name   = "rate_cycle",
+               .alias  = "ratecycle",
                .lname  = "I/O rate cycle",
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(ratecycle),
@@ -2978,6 +2854,26 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_FILE,
                .def    = "0",
        },
+       {
+               .name   = "allow_file_create",
+               .lname  = "Allow file create",
+               .type   = FIO_OPT_BOOL,
+               .off1   = td_var_offset(allow_create),
+               .help   = "Permit fio to create files, if they don't exist",
+               .def    = "1",
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_FILENAME,
+       },
+       {
+               .name   = "allow_mounted_write",
+               .lname  = "Allow mounted write",
+               .type   = FIO_OPT_BOOL,
+               .off1   = td_var_offset(allow_mounted_write),
+               .help   = "Allow writes to a mounted partition",
+               .def    = "0",
+               .category = FIO_OPT_C_FILE,
+               .group  = FIO_OPT_G_FILENAME,
+       },
        {
                .name   = "pre_read",
                .lname  = "Pre-read files",
@@ -3090,6 +2986,15 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_GENERAL,
                .group  = FIO_OPT_G_PROCESS,
        },
+       {
+               .name   = "exitall_on_error",
+               .lname  = "Exit-all on terminate in error",
+               .type   = FIO_OPT_BOOL,
+               .off1   = td_var_offset(unlink),
+               .help   = "Terminate all jobs when one exits in error",
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_PROCESS,
+       },
        {
                .name   = "stonewall",
                .lname  = "Wait for previous",
@@ -3122,6 +3027,15 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_GENERAL,
                .group  = FIO_OPT_G_PROCESS,
        },
+       {
+               .name   = "per_job_logs",
+               .type   = FIO_OPT_BOOL,
+               .off1   = td_var_offset(per_job_logs),
+               .help   = "Include job number in generated log files or not",
+               .def    = "1",
+               .category = FIO_OPT_C_LOG,
+               .group  = FIO_OPT_G_INVALID,
+       },
        {
                .name   = "write_bw_log",
                .lname  = "Write bandwidth log",
@@ -3159,6 +3073,16 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_LOG,
                .group  = FIO_OPT_G_INVALID,
        },
+       {
+               .name   = "log_max_value",
+               .lname  = "Log maximum instead of average",
+               .type   = FIO_OPT_BOOL,
+               .off1   = td_var_offset(log_max),
+               .help   = "Log max sample in a window instead of average",
+               .def    = "0",
+               .category = FIO_OPT_C_LOG,
+               .group  = FIO_OPT_G_INVALID,
+       },
        {
                .name   = "log_offset",
                .lname  = "Log offset of IO",
@@ -3176,11 +3100,24 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .type   = FIO_OPT_INT,
                .off1   = td_var_offset(log_gz),
                .help   = "Log in compressed chunks of this size",
-               .minval = 32 * 1024 * 1024ULL,
+               .minval = 1024ULL,
                .maxval = 512 * 1024 * 1024ULL,
                .category = FIO_OPT_C_LOG,
                .group  = FIO_OPT_G_INVALID,
        },
+#ifdef FIO_HAVE_CPU_AFFINITY
+       {
+               .name   = "log_compression_cpus",
+               .lname  = "Log Compression CPUs",
+               .type   = FIO_OPT_STR,
+               .cb     = str_log_cpus_allowed_cb,
+               .off1   = td_var_offset(log_gz_cpumask),
+               .parent = "log_compression",
+               .help   = "Limit log compression to these CPUs",
+               .category = FIO_OPT_C_LOG,
+               .group  = FIO_OPT_G_INVALID,
+       },
+#endif
        {
                .name   = "log_store_compressed",
                .lname  = "Log store compressed",
@@ -3191,6 +3128,16 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .group  = FIO_OPT_G_INVALID,
        },
 #endif
+       {
+               .name   = "block_error_percentiles",
+               .lname  = "Block error percentiles",
+               .type   = FIO_OPT_BOOL,
+               .off1   = td_var_offset(block_error_hist),
+               .help   = "Record trim block errors and make a histogram",
+               .def    = "0",
+               .category = FIO_OPT_C_LOG,
+               .group  = FIO_OPT_G_INVALID,
+       },
        {
                .name   = "bwavgtime",
                .lname  = "Bandwidth average time",
@@ -3315,11 +3262,12 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
        },
        {
                .name   = "percentile_list",
-               .lname  = "Completion latency percentile list",
+               .lname  = "Percentile list",
                .type   = FIO_OPT_FLOAT_LIST,
                .off1   = td_var_offset(percentile_list),
                .off2   = td_var_offset(percentile_precision),
-               .help   = "Specify a custom list of percentiles to report",
+               .help   = "Specify a custom list of percentiles to report for "
+                         "completion latency and block errors",
                .def    = "1:5:10:20:30:40:50:60:70:80:90:95:99:99.5:99.9:99.95:99.99",
                .maxlen = FIO_IO_U_LIST_MAX_LEN,
                .minfp  = 0.0,
@@ -3403,7 +3351,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .name   = "gtod_cpu",
                .lname  = "Dedicated gettimeofday() CPU",
                .type   = FIO_OPT_INT,
-               .cb     = str_gtod_cpu_cb,
+               .off1   = td_var_offset(gtod_cpu),
                .help   = "Set up dedicated gettimeofday() thread on this CPU",
                .verify = gtod_cpu_verify,
                .category = FIO_OPT_C_GENERAL,
@@ -3645,6 +3593,17 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_IO_FLOW,
        },
+       {
+               .name   = "skip_bad",
+               .lname  = "Skip operations against bad blocks",
+               .type   = FIO_OPT_BOOL,
+               .off1   = td_var_offset(skip_bad),
+               .help   = "Skip operations against known bad blocks.",
+               .hide   = 1,
+               .def    = "0",
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_MTD,
+       },
        {
                .name = NULL,
        },
@@ -3740,6 +3699,18 @@ static struct fio_keyword fio_keywords[] = {
        },
 };
 
+void fio_keywords_exit(void)
+{
+       struct fio_keyword *kw;
+
+       kw = &fio_keywords[0];
+       while (kw->word) {
+               free(kw->replace);
+               kw->replace = NULL;
+               kw++;
+       }
+}
+
 void fio_keywords_init(void)
 {
        unsigned long long mb_memory;
@@ -3958,14 +3929,13 @@ static void show_closest_option(const char *opt)
                i++;
        }
 
-       if (best_option != -1)
+       if (best_option != -1 && string_distance_ok(name, best_distance))
                log_err("Did you mean %s?\n", fio_options[best_option].name);
 
        free(name);
 }
 
-int fio_options_parse(struct thread_data *td, char **opts, int num_opts,
-                       int dump_cmdline)
+int fio_options_parse(struct thread_data *td, char **opts, int num_opts)
 {
        int i, ret, unknown;
        char **opts_copy;
@@ -3976,7 +3946,7 @@ int fio_options_parse(struct thread_data *td, char **opts, int num_opts,
        for (ret = 0, i = 0, unknown = 0; i < num_opts; i++) {
                struct fio_option *o;
                int newret = parse_option(opts_copy[i], opts[i], fio_options,
-                                               &o, td, dump_cmdline);
+                                               &o, td, &td->opt_list);
 
                if (!newret && o)
                        fio_option_mark_set(&td->o, o);
@@ -4009,7 +3979,7 @@ int fio_options_parse(struct thread_data *td, char **opts, int num_opts,
                        if (td->eo)
                                newret = parse_option(opts_copy[i], opts[i],
                                                      td->io_ops->options, &o,
-                                                     td->eo, dump_cmdline);
+                                                     td->eo, &td->opt_list);
 
                        ret |= newret;
                        if (!o) {
@@ -4029,7 +3999,7 @@ int fio_cmd_option_parse(struct thread_data *td, const char *opt, char *val)
 {
        int ret;
 
-       ret = parse_cmd_option(opt, val, fio_options, td);
+       ret = parse_cmd_option(opt, val, fio_options, td, &td->opt_list);
        if (!ret) {
                struct fio_option *o;
 
@@ -4044,7 +4014,8 @@ int fio_cmd_option_parse(struct thread_data *td, const char *opt, char *val)
 int fio_cmd_ioengine_option_parse(struct thread_data *td, const char *opt,
                                char *val)
 {
-       return parse_cmd_option(opt, val, td->io_ops->options, td->eo);
+       return parse_cmd_option(opt, val, td->io_ops->options, td->eo,
+                                       &td->opt_list);
 }
 
 void fio_fill_default_options(struct thread_data *td)
@@ -4200,28 +4171,52 @@ struct fio_option *fio_option_find(const char *name)
        return find_option(fio_options, name);
 }
 
-int __fio_option_is_set(struct thread_options *o, unsigned int off1)
+static struct fio_option *find_next_opt(struct thread_options *o,
+                                       struct fio_option *from,
+                                       unsigned int off1)
 {
-       unsigned int opt_off, index, offset;
-       struct fio_option *opt = NULL;
-       int i;
+       struct fio_option *opt;
+
+       if (!from)
+               from = &fio_options[0];
+       else
+               from++;
 
-       for (i = 0; fio_options[i].name; i++) {
-               if (off1 == fio_options[i].off1) {
-                       opt = &fio_options[i];
+       opt = NULL;
+       do {
+               if (off1 == from->off1) {
+                       opt = from;
                        break;
                }
-       }
+               from++;
+       } while (from->name);
 
-       if (!opt) {
-               log_err("fio: no option found at offset %u\n", off1);
-               return 0;
-       }
+       return opt;
+}
+
+static int opt_is_set(struct thread_options *o, struct fio_option *opt)
+{
+       unsigned int opt_off, index, offset;
 
        opt_off = opt - &fio_options[0];
        index = opt_off / (8 * sizeof(uint64_t));
        offset = opt_off & ((8 * sizeof(uint64_t)) - 1);
-       return (o->set_options[index] & (1UL << offset)) != 0;
+       return (o->set_options[index] & ((uint64_t)1 << offset)) != 0;
+}
+
+bool __fio_option_is_set(struct thread_options *o, unsigned int off1)
+{
+       struct fio_option *opt, *next;
+
+       next = NULL;
+       while ((opt = find_next_opt(o, next, off1)) != NULL) {
+               if (opt_is_set(o, opt))
+                       return true;
+
+               next = opt;
+       }
+
+       return false;
 }
 
 void fio_option_mark_set(struct thread_options *o, struct fio_option *opt)
@@ -4231,5 +4226,5 @@ void fio_option_mark_set(struct thread_options *o, struct fio_option *opt)
        opt_off = opt - &fio_options[0];
        index = opt_off / (8 * sizeof(uint64_t));
        offset = opt_off & ((8 * sizeof(uint64_t)) - 1);
-       o->set_options[index] |= 1UL << offset;
+       o->set_options[index] |= (uint64_t)1 << offset;
 }