blktrace: add 'reply_skip' option
[fio.git] / options.c
index 3c9adfbe8faa7546cc605a986add7349d9a86fd0..047e493daec44dcaa787043959fcb86d81130b08 100644 (file)
--- a/options.c
+++ b/options.c
@@ -4,16 +4,12 @@
 #include <ctype.h>
 #include <string.h>
 #include <assert.h>
-#include <libgen.h>
-#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"
@@ -22,7 +18,7 @@ char client_sockaddr_str[INET6_ADDRSTRLEN] = { 0 };
 
 #define cb_data_to_td(data)    container_of(data, struct thread_data, o)
 
-struct pattern_fmt_desc fmt_desc[] = {
+static struct pattern_fmt_desc fmt_desc[] = {
        {
                .fmt   = "%o",
                .len   = FIELD_SIZE(struct io_u *, offset),
@@ -56,14 +52,16 @@ static int bs_cmp(const void *p1, const void *p2)
 
 struct split {
        unsigned int nr;
-       unsigned int val1[100];
-       unsigned int val2[100];
+       unsigned int val1[ZONESPLIT_MAX];
+       unsigned long long val2[ZONESPLIT_MAX];
 };
 
 static int split_parse_ddir(struct thread_options *o, struct split *split,
-                           enum fio_ddir ddir, char *str)
+                           enum fio_ddir ddir, char *str, bool absolute,
+                           unsigned int max_splits)
 {
-       unsigned int i, perc;
+       unsigned long long perc;
+       unsigned int i;
        long long val;
        char *fname;
 
@@ -80,31 +78,46 @@ static int split_parse_ddir(struct thread_options *o, struct split *split,
                if (perc_str) {
                        *perc_str = '\0';
                        perc_str++;
-                       perc = atoi(perc_str);
-                       if (perc > 100)
-                               perc = 100;
-                       else if (!perc)
+                       if (absolute) {
+                               if (str_to_decimal(perc_str, &val, 1, o, 0, 0)) {
+                                       log_err("fio: split conversion failed\n");
+                                       return 1;
+                               }
+                               perc = val;
+                       } else {
+                               perc = atoi(perc_str);
+                               if (perc > 100)
+                                       perc = 100;
+                               else if (!perc)
+                                       perc = -1U;
+                       }
+               } else {
+                       if (absolute)
+                               perc = 0;
+                       else
                                perc = -1U;
-               } else
-                       perc = -1U;
+               }
 
                if (str_to_decimal(fname, &val, 1, o, 0, 0)) {
-                       log_err("fio: bssplit conversion failed\n");
+                       log_err("fio: split conversion failed\n");
                        return 1;
                }
 
                split->val1[i] = val;
                split->val2[i] = perc;
                i++;
-               if (i == 100)
+               if (i == max_splits) {
+                       log_err("fio: hit max of %d split entries\n", i);
                        break;
+               }
        }
 
        split->nr = i;
        return 0;
 }
 
-static int bssplit_ddir(struct thread_options *o, enum fio_ddir ddir, char *str)
+static int bssplit_ddir(struct thread_options *o, enum fio_ddir ddir, char *str,
+                       bool data)
 {
        unsigned int i, perc, perc_missing;
        unsigned int max_bs, min_bs;
@@ -112,7 +125,7 @@ static int bssplit_ddir(struct thread_options *o, enum fio_ddir ddir, char *str)
 
        memset(&split, 0, sizeof(split));
 
-       if (split_parse_ddir(o, &split, ddir, str))
+       if (split_parse_ddir(o, &split, ddir, str, data, BSSPLIT_MAX))
                return 1;
        if (!split.nr)
                return 0;
@@ -176,9 +189,10 @@ static int bssplit_ddir(struct thread_options *o, enum fio_ddir ddir, char *str)
        return 0;
 }
 
-typedef int (split_parse_fn)(struct thread_options *, enum fio_ddir, char *);
+typedef int (split_parse_fn)(struct thread_options *, enum fio_ddir, char *, bool);
 
-static int str_split_parse(struct thread_data *td, char *str, split_parse_fn *fn)
+static int str_split_parse(struct thread_data *td, char *str,
+                          split_parse_fn *fn, bool data)
 {
        char *odir, *ddir;
        int ret = 0;
@@ -187,37 +201,37 @@ static int str_split_parse(struct thread_data *td, char *str, split_parse_fn *fn
        if (odir) {
                ddir = strchr(odir + 1, ',');
                if (ddir) {
-                       ret = fn(&td->o, DDIR_TRIM, ddir + 1);
+                       ret = fn(&td->o, DDIR_TRIM, ddir + 1, data);
                        if (!ret)
                                *ddir = '\0';
                } else {
                        char *op;
 
                        op = strdup(odir + 1);
-                       ret = fn(&td->o, DDIR_TRIM, op);
+                       ret = fn(&td->o, DDIR_TRIM, op, data);
 
                        free(op);
                }
                if (!ret)
-                       ret = fn(&td->o, DDIR_WRITE, odir + 1);
+                       ret = fn(&td->o, DDIR_WRITE, odir + 1, data);
                if (!ret) {
                        *odir = '\0';
-                       ret = fn(&td->o, DDIR_READ, str);
+                       ret = fn(&td->o, DDIR_READ, str, data);
                }
        } else {
                char *op;
 
                op = strdup(str);
-               ret = fn(&td->o, DDIR_WRITE, op);
+               ret = fn(&td->o, DDIR_WRITE, op, data);
                free(op);
 
                if (!ret) {
                        op = strdup(str);
-                       ret = fn(&td->o, DDIR_TRIM, op);
+                       ret = fn(&td->o, DDIR_TRIM, op, data);
                        free(op);
                }
                if (!ret)
-                       ret = fn(&td->o, DDIR_READ, str);
+                       ret = fn(&td->o, DDIR_READ, str, data);
        }
 
        return ret;
@@ -234,7 +248,7 @@ static int str_bssplit_cb(void *data, const char *input)
        strip_blank_front(&str);
        strip_blank_end(str);
 
-       ret = str_split_parse(td, str, bssplit_ddir);
+       ret = str_split_parse(td, str, bssplit_ddir, false);
 
        if (parse_dryrun()) {
                int i;
@@ -270,7 +284,8 @@ static int str2error(char *str)
        return 0;
 }
 
-static int ignore_error_type(struct thread_data *td, int etype, char *str)
+static int ignore_error_type(struct thread_data *td, enum error_type_bit etype,
+                               char *str)
 {
        unsigned int i;
        int *error;
@@ -282,7 +297,7 @@ static int ignore_error_type(struct thread_data *td, int etype, char *str)
        }
 
        td->o.ignore_error_nr[etype] = 4;
-       error = malloc(4 * sizeof(struct bssplit));
+       error = calloc(4, sizeof(int));
 
        i = 0;
        while ((fname = strsep(&str, ":")) != NULL) {
@@ -306,8 +321,9 @@ static int ignore_error_type(struct thread_data *td, int etype, char *str)
                                error[i] = -error[i];
                }
                if (!error[i]) {
-                       log_err("Unknown error %s, please use number value \n",
+                       log_err("Unknown error %s, please use number value\n",
                                  fname);
+                       td->o.ignore_error_nr[etype] = 0;
                        free(error);
                        return 1;
                }
@@ -317,18 +333,58 @@ static int ignore_error_type(struct thread_data *td, int etype, char *str)
                td->o.continue_on_error |= 1 << etype;
                td->o.ignore_error_nr[etype] = i;
                td->o.ignore_error[etype] = error;
-       } else
+       } else {
+               td->o.ignore_error_nr[etype] = 0;
                free(error);
+       }
 
        return 0;
 
 }
 
+static int str_replay_skip_cb(void *data, const char *input)
+{
+       struct thread_data *td = cb_data_to_td(data);
+       char *str, *p, *n;
+       int ret = 0;
+
+       if (parse_dryrun())
+               return 0;
+
+       p = str = strdup(input);
+
+       strip_blank_front(&str);
+       strip_blank_end(str);
+
+       while (p) {
+               n = strchr(p, ',');
+               if (n)
+                       *n++ = '\0';
+               if (!strcmp(p, "read"))
+                       td->o.replay_skip |= 1u << DDIR_READ;
+               else if (!strcmp(p, "write"))
+                       td->o.replay_skip |= 1u << DDIR_WRITE;
+               else if (!strcmp(p, "trim"))
+                       td->o.replay_skip |= 1u << DDIR_TRIM;
+               else if (!strcmp(p, "sync"))
+                       td->o.replay_skip |= 1u << DDIR_SYNC;
+               else {
+                       log_err("Unknown skip type: %s\n", p);
+                       ret = 1;
+                       break;
+               }
+               p = n;
+       }
+       free(str);
+       return ret;
+}
+
 static int str_ignore_error_cb(void *data, const char *input)
 {
        struct thread_data *td = cb_data_to_td(data);
        char *str, *p, *n;
-       int type = 0, ret = 1;
+       int ret = 1;
+       enum error_type_bit type = 0;
 
        if (parse_dryrun())
                return 0;
@@ -818,23 +874,15 @@ static int str_sfr_cb(void *data, const char *str)
 }
 #endif
 
-static int zone_cmp(const void *p1, const void *p2)
-{
-       const struct zone_split *zsp1 = p1;
-       const struct zone_split *zsp2 = p2;
-
-       return (int) zsp2->access_perc - (int) zsp1->access_perc;
-}
-
 static int zone_split_ddir(struct thread_options *o, enum fio_ddir ddir,
-                          char *str)
+                          char *str, bool absolute)
 {
        unsigned int i, perc, perc_missing, sperc, sperc_missing;
        struct split split;
 
        memset(&split, 0, sizeof(split));
 
-       if (split_parse_ddir(o, &split, ddir, str))
+       if (split_parse_ddir(o, &split, ddir, str, absolute, ZONESPLIT_MAX))
                return 1;
        if (!split.nr)
                return 0;
@@ -843,7 +891,10 @@ static int zone_split_ddir(struct thread_options *o, enum fio_ddir ddir,
        o->zone_split_nr[ddir] = split.nr;
        for (i = 0; i < split.nr; i++) {
                o->zone_split[ddir][i].access_perc = split.val1[i];
-               o->zone_split[ddir][i].size_perc = split.val2[i];
+               if (absolute)
+                       o->zone_split[ddir][i].size = split.val2[i];
+               else
+                       o->zone_split[ddir][i].size_perc = split.val2[i];
        }
 
        /*
@@ -859,11 +910,12 @@ static int zone_split_ddir(struct thread_options *o, enum fio_ddir ddir,
                else
                        perc += zsp->access_perc;
 
-               if (zsp->size_perc == (uint8_t) -1U)
-                       sperc_missing++;
-               else
-                       sperc += zsp->size_perc;
-
+               if (!absolute) {
+                       if (zsp->size_perc == (uint8_t) -1U)
+                               sperc_missing++;
+                       else
+                               sperc += zsp->size_perc;
+               }
        }
 
        if (perc > 100 || sperc > 100) {
@@ -905,20 +957,17 @@ static int zone_split_ddir(struct thread_options *o, enum fio_ddir ddir,
                }
        }
 
-       /*
-        * now sort based on percentages, for ease of lookup
-        */
-       qsort(o->zone_split[ddir], o->zone_split_nr[ddir], sizeof(struct zone_split), zone_cmp);
        return 0;
 }
 
 static void __td_zone_gen_index(struct thread_data *td, enum fio_ddir ddir)
 {
        unsigned int i, j, sprev, aprev;
+       uint64_t sprev_sz;
 
        td->zone_state_index[ddir] = malloc(sizeof(struct zone_split_index) * 100);
 
-       sprev = aprev = 0;
+       sprev_sz = sprev = aprev = 0;
        for (i = 0; i < td->o.zone_split_nr[ddir]; i++) {
                struct zone_split *zsp = &td->o.zone_split[ddir][i];
 
@@ -927,10 +976,14 @@ static void __td_zone_gen_index(struct thread_data *td, enum fio_ddir ddir)
 
                        zsi->size_perc = sprev + zsp->size_perc;
                        zsi->size_perc_prev = sprev;
+
+                       zsi->size = sprev_sz + zsp->size;
+                       zsi->size_prev = sprev_sz;
                }
 
                aprev += zsp->access_perc;
                sprev += zsp->size_perc;
+               sprev_sz += zsp->size;
        }
 }
 
@@ -949,8 +1002,10 @@ static void td_zone_gen_index(struct thread_data *td)
                __td_zone_gen_index(td, i);
 }
 
-static int parse_zoned_distribution(struct thread_data *td, const char *input)
+static int parse_zoned_distribution(struct thread_data *td, const char *input,
+                                   bool absolute)
 {
+       const char *pre = absolute ? "zoned_abs:" : "zoned:";
        char *str, *p;
        int i, ret = 0;
 
@@ -960,14 +1015,14 @@ static int parse_zoned_distribution(struct thread_data *td, const char *input)
        strip_blank_end(str);
 
        /* We expect it to start like that, bail if not */
-       if (strncmp(str, "zoned:", 6)) {
+       if (strncmp(str, pre, strlen(pre))) {
                log_err("fio: mismatch in zoned input <%s>\n", str);
                free(p);
                return 1;
        }
-       str += strlen("zoned:");
+       str += strlen(pre);
 
-       ret = str_split_parse(td, str, zone_split_ddir);
+       ret = str_split_parse(td, str, zone_split_ddir, absolute);
 
        free(p);
 
@@ -979,8 +1034,15 @@ static int parse_zoned_distribution(struct thread_data *td, const char *input)
                for (j = 0; j < td->o.zone_split_nr[i]; j++) {
                        struct zone_split *zsp = &td->o.zone_split[i][j];
 
-                       dprint(FD_PARSE, "\t%d: %u/%u\n", j, zsp->access_perc,
-                                                               zsp->size_perc);
+                       if (absolute) {
+                               dprint(FD_PARSE, "\t%d: %u/%llu\n", j,
+                                               zsp->access_perc,
+                                               (unsigned long long) zsp->size);
+                       } else {
+                               dprint(FD_PARSE, "\t%d: %u/%u\n", j,
+                                               zsp->access_perc,
+                                               zsp->size_perc);
+                       }
                }
        }
 
@@ -1019,7 +1081,9 @@ static int str_random_distribution_cb(void *data, const char *str)
        else if (td->o.random_distribution == FIO_RAND_DIST_GAUSS)
                val = 0.0;
        else if (td->o.random_distribution == FIO_RAND_DIST_ZONED)
-               return parse_zoned_distribution(td, str);
+               return parse_zoned_distribution(td, str, false);
+       else if (td->o.random_distribution == FIO_RAND_DIST_ZONED_ABS)
+               return parse_zoned_distribution(td, str, true);
        else
                return 0;
 
@@ -1061,6 +1125,78 @@ static int str_random_distribution_cb(void *data, const char *str)
        return 0;
 }
 
+static int str_steadystate_cb(void *data, const char *str)
+{
+       struct thread_data *td = cb_data_to_td(data);
+       double val;
+       char *nr;
+       char *pct;
+       long long ll;
+
+       if (td->o.ss_state != FIO_SS_IOPS && td->o.ss_state != FIO_SS_IOPS_SLOPE &&
+           td->o.ss_state != FIO_SS_BW && td->o.ss_state != FIO_SS_BW_SLOPE) {
+               /* should be impossible to get here */
+               log_err("fio: unknown steady state criterion\n");
+               return 1;
+       }
+
+       nr = get_opt_postfix(str);
+       if (!nr) {
+               log_err("fio: steadystate threshold must be specified in addition to criterion\n");
+               free(nr);
+               return 1;
+       }
+
+       /* ENHANCEMENT Allow fio to understand size=10.2% and use here */
+       pct = strstr(nr, "%");
+       if (pct) {
+               *pct = '\0';
+               strip_blank_end(nr);
+               if (!str_to_float(nr, &val, 0)) {
+                       log_err("fio: could not parse steadystate threshold percentage\n");
+                       free(nr);
+                       return 1;
+               }
+
+               dprint(FD_PARSE, "set steady state threshold to %f%%\n", val);
+               free(nr);
+               if (parse_dryrun())
+                       return 0;
+
+               td->o.ss_state |= FIO_SS_PCT;
+               td->o.ss_limit.u.f = val;
+       } else if (td->o.ss_state & FIO_SS_IOPS) {
+               if (!str_to_float(nr, &val, 0)) {
+                       log_err("fio: steadystate IOPS threshold postfix parsing failed\n");
+                       free(nr);
+                       return 1;
+               }
+
+               dprint(FD_PARSE, "set steady state IOPS threshold to %f\n", val);
+               free(nr);
+               if (parse_dryrun())
+                       return 0;
+
+               td->o.ss_limit.u.f = val;
+       } else {        /* bandwidth criterion */
+               if (str_to_decimal(nr, &ll, 1, td, 0, 0)) {
+                       log_err("fio: steadystate BW threshold postfix parsing failed\n");
+                       free(nr);
+                       return 1;
+               }
+
+               dprint(FD_PARSE, "set steady state BW threshold to %lld\n", ll);
+               free(nr);
+               if (parse_dryrun())
+                       return 0;
+
+               td->o.ss_limit.u.f = (double) ll;
+       }
+
+       td->ss.state = td->o.ss_state;
+       return 0;
+}
+
 /*
  * Return next name in the string. Files are separated with ':'. If the ':'
  * is escaped with a '\', then that ':' is part of the filename and does not
@@ -1161,6 +1297,9 @@ static int str_filename_cb(void *data, const char *input)
        strip_blank_front(&str);
        strip_blank_end(str);
 
+       /*
+        * Ignore what we may already have from nrfiles option.
+        */
        if (!td->files_index)
                td->o.nr_files = 0;
 
@@ -1231,8 +1370,17 @@ static int str_buffer_pattern_cb(void *data, const char *input)
 
        assert(ret != 0);
        td->o.buffer_pattern_bytes = ret;
-       if (!td->o.compress_percentage)
+
+       /*
+        * If this job is doing any reading or has compression set,
+        * ensure that we refill buffers for writes or we could be
+        * invalidating the pattern through reads.
+        */
+       if (!td->o.compress_percentage && !td_read(td))
                td->o.refill_buffers = 0;
+       else
+               td->o.refill_buffers = 1;
+
        td->o.scramble_buffers = 0;
        td->o.zero_buffers = 0;
 
@@ -1292,7 +1440,23 @@ static int str_gtod_reduce_cb(void *data, int *il)
        td->o.disable_bw = !!val;
        td->o.clat_percentiles = !val;
        if (val)
-               td->tv_cache_mask = 63;
+               td->ts_cache_mask = 63;
+
+       return 0;
+}
+
+static int str_offset_cb(void *data, unsigned long long *__val)
+{
+       struct thread_data *td = cb_data_to_td(data);
+       unsigned long long v = *__val;
+
+       if (parse_is_percent(v)) {
+               td->o.start_offset = 0;
+               td->o.start_offset_percent = -1ULL - v;
+               dprint(FD_PARSE, "SET start_offset_percent %d\n",
+                                       td->o.start_offset_percent);
+       } else
+               td->o.start_offset = v;
 
        return 0;
 }
@@ -1305,6 +1469,8 @@ static int str_size_cb(void *data, unsigned long long *__val)
        if (parse_is_percent(v)) {
                td->o.size = 0;
                td->o.size_percent = -1ULL - v;
+               dprint(FD_PARSE, "SET size_percent %d\n",
+                                       td->o.size_percent);
        } else
                td->o.size = v;
 
@@ -1355,7 +1521,40 @@ static int str_write_hist_log_cb(void *data, const char *str)
        return 0;
 }
 
-static int rw_verify(struct fio_option *o, void *data)
+/*
+ * str is supposed to be a substring of the strdup'd original string,
+ * and is valid only if it's a regular file path.
+ * This function keeps the pointer to the path as needed later.
+ *
+ * "external:/path/to/so\0" <- original pointer updated with strdup'd
+ * "external\0"             <- above pointer after parsed, i.e. ->ioengine
+ *          "/path/to/so\0" <- str argument, i.e. ->ioengine_so_path
+ */
+static int str_ioengine_external_cb(void *data, const char *str)
+{
+       struct thread_data *td = cb_data_to_td(data);
+       struct stat sb;
+       char *p;
+
+       if (!str) {
+               log_err("fio: null external ioengine path\n");
+               return 1;
+       }
+
+       p = (char *)str; /* str is mutable */
+       strip_blank_front(&p);
+       strip_blank_end(p);
+
+       if (stat(p, &sb) || !S_ISREG(sb.st_mode)) {
+               log_err("fio: invalid external ioengine path \"%s\"\n", p);
+               return 1;
+       }
+
+       td->o.ioengine_so_path = p;
+       return 0;
+}
+
+static int rw_verify(const struct fio_option *o, void *data)
 {
        struct thread_data *td = cb_data_to_td(data);
 
@@ -1368,7 +1567,7 @@ static int rw_verify(struct fio_option *o, void *data)
        return 0;
 }
 
-static int gtod_cpu_verify(struct fio_option *o, void *data)
+static int gtod_cpu_verify(const struct fio_option *o, void *data)
 {
 #ifndef FIO_HAVE_CPU_AFFINITY
        struct thread_data *td = cb_data_to_td(data);
@@ -1654,11 +1853,6 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                            .help = "GUASI IO engine",
                          },
 #endif
-#ifdef FIO_HAVE_BINJECT
-                         { .ival = "binject",
-                           .help = "binject direct inject block engine",
-                         },
-#endif
 #ifdef CONFIG_RDMA
                          { .ival = "rdma",
                            .help = "RDMA IO engine",
@@ -1694,13 +1888,28 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
 #endif
 #ifdef CONFIG_PMEMBLK
                          { .ival = "pmemblk",
-                           .help = "NVML libpmemblk based IO engine",
+                           .help = "PMDK libpmemblk based IO engine",
                          },
 
 #endif
+#ifdef CONFIG_LINUX_DEVDAX
+                         { .ival = "dev-dax",
+                           .help = "DAX Device based IO engine",
+                         },
+#endif
+                         {
+                           .ival = "filecreate",
+                           .help = "File creation engine",
+                         },
                          { .ival = "external",
                            .help = "Load external engine (append name)",
+                           .cb = str_ioengine_external_cb,
+                         },
+#ifdef CONFIG_LIBPMEM
+                         { .ival = "libpmem",
+                           .help = "PMDK libpmem based IO engine",
                          },
+#endif
                },
        },
        {
@@ -1769,6 +1978,17 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_IO_BASIC,
        },
+       {
+               .name   = "serialize_overlap",
+               .lname  = "Serialize overlap",
+               .off1   = offsetof(struct thread_options, serialize_overlap),
+               .type   = FIO_OPT_BOOL,
+               .help   = "Wait for in-flight IOs that collide to complete",
+               .parent = "iodepth",
+               .def    = "0",
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IO_BASIC,
+       },
        {
                .name   = "io_submit_mode",
                .lname  = "IO submit mode",
@@ -1805,7 +2025,8 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .alias  = "io_limit",
                .lname  = "IO Size",
                .type   = FIO_OPT_STR_VAL,
-               .off1   = offsetof(struct thread_options, io_limit),
+               .off1   = offsetof(struct thread_options, io_size),
+               .help   = "Total size of I/O to be performed",
                .interval = 1024 * 1024,
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_INVALID,
@@ -1848,6 +2069,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .lname  = "IO offset",
                .alias  = "fileoffset",
                .type   = FIO_OPT_STR_VAL,
+               .cb     = str_offset_cb,
                .off1   = offsetof(struct thread_options, start_offset),
                .help   = "Start IO from this offset",
                .def    = "0",
@@ -1855,6 +2077,17 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_INVALID,
        },
+       {
+               .name   = "offset_align",
+               .lname  = "IO offset alignment",
+               .type   = FIO_OPT_INT,
+               .off1   = offsetof(struct thread_options, start_offset_align),
+               .help   = "Start IO from this offset alignment",
+               .def    = "0",
+               .interval = 512,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_INVALID,
+       },
        {
                .name   = "offset_increment",
                .lname  = "IO offset increment",
@@ -1888,7 +2121,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .off3   = offsetof(struct thread_options, bs[DDIR_TRIM]),
                .minval = 1,
                .help   = "Block size unit",
-               .def    = "4k",
+               .def    = "4096",
                .parent = "rw",
                .hide   = 1,
                .interval = 512,
@@ -2074,7 +2307,10 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                            .oval = FIO_RAND_DIST_ZONED,
                            .help = "Zoned random distribution",
                          },
-
+                         { .ival = "zoned_abs",
+                           .oval = FIO_RAND_DIST_ZONED_ABS,
+                           .help = "Zoned absolute random distribution",
+                         },
                },
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_RANDOM,
@@ -2155,9 +2391,13 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                            .oval = FIO_FSERVICE_PARETO,
                            .help = "Pareto randomized",
                          },
+                         { .ival = "normal",
+                           .oval = FIO_FSERVICE_GAUSS,
+                           .help = "Normal (Gaussian) randomized",
+                         },
                          { .ival = "gauss",
                            .oval = FIO_FSERVICE_GAUSS,
-                           .help = "Normal (guassian) distribution",
+                           .help = "Alias for normal",
                          },
                          { .ival = "roundrobin",
                            .oval = FIO_FSERVICE_RR,
@@ -2171,14 +2411,14 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .parent = "nrfiles",
                .hide   = 1,
        },
-#ifdef CONFIG_POSIX_FALLOCATE
+#ifdef FIO_HAVE_ANY_FALLOCATE
        {
                .name   = "fallocate",
                .lname  = "Fallocate",
                .type   = FIO_OPT_STR,
                .off1   = offsetof(struct thread_options, fallocate_mode),
                .help   = "Whether pre-allocation is performed when laying out files",
-               .def    = "posix",
+               .def    = "native",
                .category = FIO_OPT_C_FILE,
                .group  = FIO_OPT_G_INVALID,
                .posval = {
@@ -2186,10 +2426,16 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                            .oval = FIO_FALLOCATE_NONE,
                            .help = "Do not pre-allocate space",
                          },
+                         { .ival = "native",
+                           .oval = FIO_FALLOCATE_NATIVE,
+                           .help = "Use native pre-allocation if possible",
+                         },
+#ifdef CONFIG_POSIX_FALLOCATE
                          { .ival = "posix",
                            .oval = FIO_FALLOCATE_POSIX,
                            .help = "Use posix_fallocate()",
                          },
+#endif
 #ifdef CONFIG_LINUX_FALLOCATE
                          { .ival = "keep",
                            .oval = FIO_FALLOCATE_KEEP_SIZE,
@@ -2201,48 +2447,50 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                            .oval = FIO_FALLOCATE_NONE,
                            .help = "Alias for 'none'",
                          },
+#ifdef CONFIG_POSIX_FALLOCATE
                          { .ival = "1",
                            .oval = FIO_FALLOCATE_POSIX,
                            .help = "Alias for 'posix'",
                          },
+#endif
                },
        },
-#else  /* CONFIG_POSIX_FALLOCATE */
+#else  /* FIO_HAVE_ANY_FALLOCATE */
        {
                .name   = "fallocate",
                .lname  = "Fallocate",
                .type   = FIO_OPT_UNSUPPORTED,
                .help   = "Your platform does not support fallocate",
        },
-#endif /* CONFIG_POSIX_FALLOCATE */
+#endif /* FIO_HAVE_ANY_FALLOCATE */
        {
                .name   = "fadvise_hint",
                .lname  = "Fadvise hint",
-               .type   = FIO_OPT_BOOL,
+               .type   = FIO_OPT_STR,
                .off1   = offsetof(struct thread_options, fadvise_hint),
+               .posval = {
+                         { .ival = "0",
+                           .oval = F_ADV_NONE,
+                           .help = "Don't issue fadvise/madvise",
+                         },
+                         { .ival = "1",
+                           .oval = F_ADV_TYPE,
+                           .help = "Advise using fio IO pattern",
+                         },
+                         { .ival = "random",
+                           .oval = F_ADV_RANDOM,
+                           .help = "Advise using FADV_RANDOM",
+                         },
+                         { .ival = "sequential",
+                           .oval = F_ADV_SEQUENTIAL,
+                           .help = "Advise using FADV_SEQUENTIAL",
+                         },
+               },
                .help   = "Use fadvise() to advise the kernel on IO pattern",
                .def    = "1",
                .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   = offsetof(struct thread_options, fadvise_stream),
-               .help   = "Use fadvise() to set stream ID",
-               .category = FIO_OPT_C_FILE,
-               .group  = FIO_OPT_G_INVALID,
-       },
-#else
-       {
-               .name   = "fadvise_stream",
-               .lname  = "Fadvise stream",
-               .type   = FIO_OPT_UNSUPPORTED,
-               .help   = "Your platform does not support fadvise stream ID",
-       },
-#endif
        {
                .name   = "fsync",
                .lname  = "Fsync",
@@ -2504,6 +2752,12 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                            .oval = MEM_MMAPHUGE,
                            .help = "Like mmap, but use huge pages",
                          },
+#endif
+#ifdef CONFIG_CUDA
+                         { .ival = "cudamalloc",
+                           .oval = MEM_CUDA_MALLOC,
+                           .help = "Allocate GPU device memory for GPUDirect RDMA",
+                         },
 #endif
                  },
        },
@@ -2575,6 +2829,22 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                            .oval = VERIFY_SHA512,
                            .help = "Use sha512 checksums for verification",
                          },
+                         { .ival = "sha3-224",
+                           .oval = VERIFY_SHA3_224,
+                           .help = "Use sha3-224 checksums for verification",
+                         },
+                         { .ival = "sha3-256",
+                           .oval = VERIFY_SHA3_256,
+                           .help = "Use sha3-256 checksums for verification",
+                         },
+                         { .ival = "sha3-384",
+                           .oval = VERIFY_SHA3_384,
+                           .help = "Use sha3-384 checksums for verification",
+                         },
+                         { .ival = "sha3-512",
+                           .oval = VERIFY_SHA3_512,
+                           .help = "Use sha3-512 checksums for verification",
+                         },
                          { .ival = "xxhash",
                            .oval = VERIFY_XXHASH,
                            .help = "Use xxhash checksums for verification",
@@ -2613,25 +2883,14 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
        {
                .name   = "verifysort",
                .lname  = "Verify sort",
-               .type   = FIO_OPT_BOOL,
-               .off1   = offsetof(struct thread_options, verifysort),
-               .help   = "Sort written verify blocks for read back",
-               .def    = "1",
-               .parent = "verify",
-               .hide   = 1,
+               .type   = FIO_OPT_SOFT_DEPRECATED,
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_VERIFY,
        },
        {
                .name   = "verifysort_nr",
                .lname  = "Verify Sort Nr",
-               .type   = FIO_OPT_INT,
-               .off1   = offsetof(struct thread_options, verifysort_nr),
-               .help   = "Pre-load and sort verify blocks for a read workload",
-               .minval = 0,
-               .maxval = 131072,
-               .def    = "1024",
-               .parent = "verify",
+               .type   = FIO_OPT_SOFT_DEPRECATED,
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_VERIFY,
        },
@@ -2790,7 +3049,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .off1   = offsetof(struct thread_options, trim_percentage),
                .minval = 0,
                .maxval = 100,
-               .help   = "Number of verify blocks to discard/trim",
+               .help   = "Number of verify blocks to trim (i.e., discard)",
                .parent = "verify",
                .def    = "0",
                .interval = 1,
@@ -2802,7 +3061,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .name   = "trim_verify_zero",
                .lname  = "Verify trim zero",
                .type   = FIO_OPT_BOOL,
-               .help   = "Verify that trim/discarded blocks are returned as zeroes",
+               .help   = "Verify that trimmed (i.e., discarded) blocks are returned as zeroes",
                .off1   = offsetof(struct thread_options, trim_zero),
                .parent = "trim_percentage",
                .hide   = 1,
@@ -2923,6 +3182,30 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .group  = FIO_OPT_G_IOLOG,
                .pow2   = 1,
        },
+       {
+               .name   = "replay_time_scale",
+               .lname  = "Replay Time Scale",
+               .type   = FIO_OPT_INT,
+               .off1   = offsetof(struct thread_options, replay_time_scale),
+               .def    = "100",
+               .minval = 1,
+               .parent = "read_iolog",
+               .hide   = 1,
+               .help   = "Scale time for replay events",
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IOLOG,
+       },
+       {
+               .name   = "replay_skip",
+               .lname  = "Replay Skip",
+               .type   = FIO_OPT_STR,
+               .cb     = str_replay_skip_cb,
+               .off1   = offsetof(struct thread_options, replay_skip),
+               .parent = "read_iolog",
+               .help   = "Skip certain IO types (read,write,trim,flush)",
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_IOLOG,
+       },
        {
                .name   = "exec_prerun",
                .lname  = "Pre-execute runnable",
@@ -3044,8 +3327,8 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .type   = FIO_OPT_INT,
                .off1   = offsetof(struct thread_options, nice),
                .help   = "Set job CPU nice value",
-               .minval = -19,
-               .maxval = 20,
+               .minval = -20,
+               .maxval = 19,
                .def    = "0",
                .interval = 1,
                .category = FIO_OPT_C_GENERAL,
@@ -3217,10 +3500,20 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_RATE,
        },
+       {
+               .name   = "rate_ignore_thinktime",
+               .lname  = "Rate ignore thinktime",
+               .type   = FIO_OPT_BOOL,
+               .off1   = offsetof(struct thread_options, rate_ign_think),
+               .help   = "Rated IO ignores thinktime settings",
+               .parent = "rate",
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_RATE,
+       },
        {
                .name   = "max_latency",
-               .lname  = "Max Latency",
-               .type   = FIO_OPT_INT,
+               .lname  = "Max Latency (usec)",
+               .type   = FIO_OPT_STR_VAL_TIME,
                .off1   = offsetof(struct thread_options, max_latency),
                .help   = "Maximum tolerated IO latency (usec)",
                .is_time = 1,
@@ -3282,6 +3575,34 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_IO_TYPE,
        },
+#ifdef FIO_HAVE_WRITE_HINT
+       {
+               .name   = "write_hint",
+               .lname  = "Write hint",
+               .type   = FIO_OPT_STR,
+               .off1   = offsetof(struct thread_options, write_hint),
+               .help   = "Set expected write life time",
+               .category = FIO_OPT_C_ENGINE,
+               .group  = FIO_OPT_G_INVALID,
+               .posval = {
+                         { .ival = "none",
+                           .oval = RWH_WRITE_LIFE_NONE,
+                         },
+                         { .ival = "short",
+                           .oval = RWH_WRITE_LIFE_SHORT,
+                         },
+                         { .ival = "medium",
+                           .oval = RWH_WRITE_LIFE_MEDIUM,
+                         },
+                         { .ival = "long",
+                           .oval = RWH_WRITE_LIFE_LONG,
+                         },
+                         { .ival = "extreme",
+                           .oval = RWH_WRITE_LIFE_EXTREME,
+                         },
+               },
+       },
+#endif
        {
                .name   = "create_serialize",
                .lname  = "Create serialize",
@@ -3447,6 +3768,18 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .type   = FIO_OPT_UNSUPPORTED,
                .help   = "Build fio with libnuma-dev(el) to enable this option",
        },
+#endif
+#ifdef CONFIG_CUDA
+       {
+               .name   = "gpu_dev_id",
+               .lname  = "GPU device ID",
+               .type   = FIO_OPT_INT,
+               .off1   = offsetof(struct thread_options, gpu_dev_id),
+               .help   = "Set GPU device ID for GPUDirect RDMA",
+               .def    = "0",
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_INVALID,
+       },
 #endif
        {
                .name   = "end_fsync",
@@ -3750,6 +4083,16 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_STAT,
                .group  = FIO_OPT_G_INVALID,
        },
+       {
+               .name   = "stats",
+               .lname  = "Stats",
+               .type   = FIO_OPT_BOOL,
+               .off1   = offsetof(struct thread_options, stats),
+               .help   = "Enable collection of stats",
+               .def    = "1",
+               .category = FIO_OPT_C_STAT,
+               .group  = FIO_OPT_G_INVALID,
+       },
        {
                .name   = "zero_buffers",
                .lname  = "Zero I/O buffers",
@@ -3809,6 +4152,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .parent = "buffer_compress_percentage",
                .hide   = 1,
                .help   = "Size of compressible region in buffer",
+               .def    = "512",
                .interval = 256,
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_IO_BUF,
@@ -3833,6 +4177,18 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .off1   = offsetof(struct thread_options, clat_percentiles),
                .help   = "Enable the reporting of completion latency percentiles",
                .def    = "1",
+               .inverse = "lat_percentiles",
+               .category = FIO_OPT_C_STAT,
+               .group  = FIO_OPT_G_INVALID,
+       },
+       {
+               .name   = "lat_percentiles",
+               .lname  = "IO latency percentiles",
+               .type   = FIO_OPT_BOOL,
+               .off1   = offsetof(struct thread_options, lat_percentiles),
+               .help   = "Enable the reporting of IO latency percentiles",
+               .def    = "0",
+               .inverse = "clat_percentiles",
                .category = FIO_OPT_C_STAT,
                .group  = FIO_OPT_G_INVALID,
        },
@@ -3851,6 +4207,19 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_STAT,
                .group  = FIO_OPT_G_INVALID,
        },
+       {
+               .name   = "significant_figures",
+               .lname  = "Significant figures",
+               .type   = FIO_OPT_INT,
+               .off1   = offsetof(struct thread_options, sig_figs),
+               .maxval = 10,
+               .minval = 1,
+               .help   = "Significant figures for output-format set to normal",
+               .def    = "4",
+               .interval = 1,
+               .category = FIO_OPT_C_STAT,
+               .group  = FIO_OPT_G_INVALID,
+       },
 
 #ifdef FIO_HAVE_DISK_UTIL
        {
@@ -4085,34 +4454,34 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .posval = {
                          { .ival = "1024",
                            .oval = 1024,
-                           .help = "Use 1024 as the K base",
+                           .help = "Inputs invert IEC and SI prefixes (for compatibility); outputs prefer binary",
                          },
                          { .ival = "1000",
                            .oval = 1000,
-                           .help = "Use 1000 as the K base",
+                           .help = "Inputs use IEC and SI prefixes; outputs prefer SI",
                          },
                },
-               .help   = "How many bytes per KB for reporting (1000 or 1024)",
+               .help   = "Unit prefix interpretation for quantities of data (IEC and SI)",
                .category = FIO_OPT_C_GENERAL,
                .group  = FIO_OPT_G_INVALID,
        },
        {
                .name   = "unit_base",
-               .lname  = "Base unit for reporting (Bits or Bytes)",
+               .lname  = "Unit for quantities of data (Bits or Bytes)",
                .type   = FIO_OPT_INT,
                .off1   = offsetof(struct thread_options, unit_base),
                .prio   = 1,
                .posval = {
                          { .ival = "0",
-                           .oval = 0,
+                           .oval = N2S_NONE,
                            .help = "Auto-detect",
                          },
                          { .ival = "8",
-                           .oval = 8,
+                           .oval = N2S_BYTEPERSEC,
                            .help = "Normal (byte based)",
                          },
                          { .ival = "1",
-                           .oval = 1,
+                           .oval = N2S_BITPERSEC,
                            .help = "Bit based",
                          },
                },
@@ -4181,15 +4550,63 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .group  = FIO_OPT_G_IO_FLOW,
        },
        {
-               .name   = "skip_bad",
-               .lname  = "Skip operations against bad blocks",
-               .type   = FIO_OPT_BOOL,
-               .off1   = offsetof(struct thread_options, skip_bad),
-               .help   = "Skip operations against known bad blocks.",
-               .hide   = 1,
-               .def    = "0",
-               .category = FIO_OPT_C_IO,
-               .group  = FIO_OPT_G_MTD,
+               .name   = "steadystate",
+               .lname  = "Steady state threshold",
+               .alias  = "ss",
+               .type   = FIO_OPT_STR,
+               .off1   = offsetof(struct thread_options, ss_state),
+               .cb     = str_steadystate_cb,
+               .help   = "Define the criterion and limit to judge when a job has reached steady state",
+               .def    = "iops_slope:0.01%",
+               .posval = {
+                         { .ival = "iops",
+                           .oval = FIO_SS_IOPS,
+                           .help = "maximum mean deviation of IOPS measurements",
+                         },
+                         { .ival = "iops_slope",
+                           .oval = FIO_SS_IOPS_SLOPE,
+                           .help = "slope calculated from IOPS measurements",
+                         },
+                         { .ival = "bw",
+                           .oval = FIO_SS_BW,
+                           .help = "maximum mean deviation of bandwidth measurements",
+                         },
+                         {
+                           .ival = "bw_slope",
+                           .oval = FIO_SS_BW_SLOPE,
+                           .help = "slope calculated from bandwidth measurements",
+                         },
+               },
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_RUNTIME,
+       },
+        {
+               .name   = "steadystate_duration",
+               .lname  = "Steady state duration",
+               .alias  = "ss_dur",
+               .parent = "steadystate",
+               .type   = FIO_OPT_STR_VAL_TIME,
+               .off1   = offsetof(struct thread_options, ss_dur),
+               .help   = "Stop workload upon attaining steady state for specified duration",
+               .def    = "0",
+               .is_seconds = 1,
+               .is_time = 1,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_RUNTIME,
+       },
+        {
+               .name   = "steadystate_ramp_time",
+               .lname  = "Steady state ramp time",
+               .alias  = "ss_ramp",
+               .parent = "steadystate",
+               .type   = FIO_OPT_STR_VAL_TIME,
+               .off1   = offsetof(struct thread_options, ss_ramp_time),
+               .help   = "Delay before initiation of data collection for steady state job termination testing",
+               .def    = "0",
+               .is_seconds = 1,
+               .is_time = 1,
+               .category = FIO_OPT_C_GENERAL,
+               .group  = FIO_OPT_G_RUNTIME,
        },
        {
                .name = NULL,
@@ -4532,7 +4949,7 @@ int fio_options_parse(struct thread_data *td, char **opts, int num_opts)
        opts_copy = dup_and_sub_options(opts, num_opts);
 
        for (ret = 0, i = 0, unknown = 0; i < num_opts; i++) {
-               struct fio_option *o;
+               const struct fio_option *o;
                int newret = parse_option(opts_copy[i], opts[i], fio_options,
                                                &o, &td->o, &td->opt_list);
 
@@ -4558,7 +4975,7 @@ int fio_options_parse(struct thread_data *td, char **opts, int num_opts)
                        opts = opts_copy;
                }
                for (i = 0; i < num_opts; i++) {
-                       struct fio_option *o = NULL;
+                       const struct fio_option *o = NULL;
                        int newret = 1;
 
                        if (!opts_copy[i])
@@ -4589,9 +5006,9 @@ int fio_cmd_option_parse(struct thread_data *td, const char *opt, char *val)
 
        ret = parse_cmd_option(opt, val, fio_options, &td->o, &td->opt_list);
        if (!ret) {
-               struct fio_option *o;
+               const struct fio_option *o;
 
-               o = find_option(fio_options, opt);
+               o = find_option_c(fio_options, opt);
                if (o)
                        fio_option_mark_set(&td->o, o);
        }
@@ -4617,34 +5034,19 @@ int fio_show_option_help(const char *opt)
        return show_cmd_help(fio_options, opt);
 }
 
-void options_mem_dupe(void *data, struct fio_option *options)
-{
-       struct fio_option *o;
-       char **ptr;
-
-       for (o = &options[0]; o->name; o++) {
-               if (o->type != FIO_OPT_STR_STORE)
-                       continue;
-
-               ptr = td_var(data, o, o->off1);
-               if (*ptr)
-                       *ptr = strdup(*ptr);
-       }
-}
-
 /*
  * dupe FIO_OPT_STR_STORE options
  */
 void fio_options_mem_dupe(struct thread_data *td)
 {
-       options_mem_dupe(&td->o, fio_options);
+       options_mem_dupe(fio_options, &td->o);
 
        if (td->eo && td->io_ops) {
                void *oldeo = td->eo;
 
                td->eo = malloc(td->io_ops->option_struct_size);
                memcpy(td->eo, oldeo, td->io_ops->option_struct_size);
-               options_mem_dupe(td->eo, td->io_ops->options);
+               options_mem_dupe(td->io_ops->options, td->eo);
        }
 }
 
@@ -4671,7 +5073,7 @@ unsigned int fio_get_kb_base(void *data)
        return kb_base;
 }
 
-int add_option(struct fio_option *o)
+int add_option(const struct fio_option *o)
 {
        struct fio_option *__o;
        int opt_index = 0;
@@ -4808,7 +5210,7 @@ bool __fio_option_is_set(struct thread_options *o, unsigned int off1)
        return false;
 }
 
-void fio_option_mark_set(struct thread_options *o, struct fio_option *opt)
+void fio_option_mark_set(struct thread_options *o, const struct fio_option *opt)
 {
        unsigned int opt_off, index, offset;