return (int) bsp1->perc - (int) bsp2->perc;
}
-struct split {
- unsigned int nr;
- unsigned long long val1[ZONESPLIT_MAX];
- unsigned long long val2[ZONESPLIT_MAX];
-};
-
-static int split_parse_ddir(struct thread_options *o, struct split *split,
+int split_parse_ddir(struct thread_options *o, struct split *split,
char *str, bool absolute, unsigned int max_splits)
{
unsigned long long perc;
return 0;
}
-static int bssplit_ddir(struct thread_options *o, enum fio_ddir ddir, char *str,
- bool data)
+static int bssplit_ddir(struct thread_options *o, void *eo,
+ enum fio_ddir ddir, char *str, bool data)
{
unsigned int i, perc, perc_missing;
unsigned long long max_bs, min_bs;
return 0;
}
-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, bool data)
+int str_split_parse(struct thread_data *td, char *str,
+ split_parse_fn *fn, void *eo, bool data)
{
char *odir, *ddir;
int ret = 0;
if (odir) {
ddir = strchr(odir + 1, ',');
if (ddir) {
- ret = fn(&td->o, DDIR_TRIM, ddir + 1, data);
+ ret = fn(&td->o, eo, DDIR_TRIM, ddir + 1, data);
if (!ret)
*ddir = '\0';
} else {
char *op;
op = strdup(odir + 1);
- ret = fn(&td->o, DDIR_TRIM, op, data);
+ ret = fn(&td->o, eo, DDIR_TRIM, op, data);
free(op);
}
if (!ret)
- ret = fn(&td->o, DDIR_WRITE, odir + 1, data);
+ ret = fn(&td->o, eo, DDIR_WRITE, odir + 1, data);
if (!ret) {
*odir = '\0';
- ret = fn(&td->o, DDIR_READ, str, data);
+ ret = fn(&td->o, eo, DDIR_READ, str, data);
}
} else {
char *op;
op = strdup(str);
- ret = fn(&td->o, DDIR_WRITE, op, data);
+ ret = fn(&td->o, eo, DDIR_WRITE, op, data);
free(op);
if (!ret) {
op = strdup(str);
- ret = fn(&td->o, DDIR_TRIM, op, data);
+ ret = fn(&td->o, eo, DDIR_TRIM, op, data);
free(op);
}
if (!ret)
- ret = fn(&td->o, DDIR_READ, str, data);
+ ret = fn(&td->o, eo, DDIR_READ, str, data);
}
return ret;
}
+static int fio_fdp_cmp(const void *p1, const void *p2)
+{
+ const uint16_t *t1 = p1;
+ const uint16_t *t2 = p2;
+
+ return *t1 - *t2;
+}
+
+static int str_fdp_pli_cb(void *data, const char *input)
+{
+ struct thread_data *td = cb_data_to_td(data);
+ char *str, *p, *v;
+ int i = 0;
+
+ p = str = strdup(input);
+ strip_blank_front(&str);
+ strip_blank_end(str);
+
+ while ((v = strsep(&str, ",")) != NULL && i < FIO_MAX_PLIS)
+ td->o.fdp_plis[i++] = strtoll(v, NULL, 0);
+ free(p);
+
+ qsort(td->o.fdp_plis, i, sizeof(*td->o.fdp_plis), fio_fdp_cmp);
+ td->o.fdp_nrpli = i;
+
+ return 0;
+}
+
static int str_bssplit_cb(void *data, const char *input)
{
struct thread_data *td = cb_data_to_td(data);
strip_blank_front(&str);
strip_blank_end(str);
- ret = str_split_parse(td, str, bssplit_ddir, false);
+ ret = str_split_parse(td, str, bssplit_ddir, NULL, false);
if (parse_dryrun()) {
int i;
return ret;
}
+static int parse_cmdprio_bssplit_entry(struct thread_options *o,
+ struct split_prio *entry, char *str)
+{
+ int matches = 0;
+ char *bs_str = NULL;
+ long long bs_val;
+ unsigned int perc = 0, class, level;
+
+ /*
+ * valid entry formats:
+ * bs/ - %s/ - set perc to 0, prio to -1.
+ * bs/perc - %s/%u - set prio to -1.
+ * bs/perc/class/level - %s/%u/%u/%u
+ */
+ matches = sscanf(str, "%m[^/]/%u/%u/%u", &bs_str, &perc, &class, &level);
+ if (matches < 1) {
+ log_err("fio: invalid cmdprio_bssplit format\n");
+ return 1;
+ }
+
+ if (str_to_decimal(bs_str, &bs_val, 1, o, 0, 0)) {
+ log_err("fio: split conversion failed\n");
+ free(bs_str);
+ return 1;
+ }
+ free(bs_str);
+
+ entry->bs = bs_val;
+ entry->perc = min(perc, 100u);
+ entry->prio = -1;
+ switch (matches) {
+ case 1: /* bs/ case */
+ case 2: /* bs/perc case */
+ break;
+ case 4: /* bs/perc/class/level case */
+ class = min(class, (unsigned int) IOPRIO_MAX_PRIO_CLASS);
+ level = min(level, (unsigned int) IOPRIO_MAX_PRIO);
+ entry->prio = ioprio_value(class, level);
+ break;
+ default:
+ log_err("fio: invalid cmdprio_bssplit format\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Returns a negative integer if the first argument should be before the second
+ * argument in the sorted list. A positive integer if the first argument should
+ * be after the second argument in the sorted list. A zero if they are equal.
+ */
+static int fio_split_prio_cmp(const void *p1, const void *p2)
+{
+ const struct split_prio *tmp1 = p1;
+ const struct split_prio *tmp2 = p2;
+
+ if (tmp1->bs > tmp2->bs)
+ return 1;
+ if (tmp1->bs < tmp2->bs)
+ return -1;
+ return 0;
+}
+
+int split_parse_prio_ddir(struct thread_options *o, struct split_prio **entries,
+ int *nr_entries, char *str)
+{
+ struct split_prio *tmp_entries;
+ unsigned int nr_bssplits;
+ char *str_cpy, *p, *fname;
+
+ /* strsep modifies the string, dup it so that we can use strsep twice */
+ p = str_cpy = strdup(str);
+ if (!p)
+ return 1;
+
+ nr_bssplits = 0;
+ while ((fname = strsep(&str_cpy, ":")) != NULL) {
+ if (!strlen(fname))
+ break;
+ nr_bssplits++;
+ }
+ free(p);
+
+ if (nr_bssplits > BSSPLIT_MAX) {
+ log_err("fio: too many cmdprio_bssplit entries\n");
+ return 1;
+ }
+
+ tmp_entries = calloc(nr_bssplits, sizeof(*tmp_entries));
+ if (!tmp_entries)
+ return 1;
+
+ nr_bssplits = 0;
+ while ((fname = strsep(&str, ":")) != NULL) {
+ struct split_prio *entry;
+
+ if (!strlen(fname))
+ break;
+
+ entry = &tmp_entries[nr_bssplits];
+
+ if (parse_cmdprio_bssplit_entry(o, entry, fname)) {
+ log_err("fio: failed to parse cmdprio_bssplit entry\n");
+ free(tmp_entries);
+ return 1;
+ }
+
+ /* skip zero perc entries, they provide no useful information */
+ if (entry->perc)
+ nr_bssplits++;
+ }
+
+ qsort(tmp_entries, nr_bssplits, sizeof(*tmp_entries),
+ fio_split_prio_cmp);
+
+ *entries = tmp_entries;
+ *nr_entries = nr_bssplits;
+
+ return 0;
+}
+
static int str2error(char *str)
{
const char *err[] = { "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO",
int fio_cpus_split(os_cpu_mask_t *mask, unsigned int cpu_index)
{
unsigned int i, index, cpus_in_mask;
- const long max_cpu = cpus_online();
+ const long max_cpu = cpus_configured();
cpus_in_mask = fio_cpu_count(mask);
if (!cpus_in_mask)
return 1;
}
- max_cpu = cpus_online();
+ max_cpu = cpus_configured();
for (i = 0; i < sizeof(int) * 8; i++) {
if ((1 << i) & *val) {
strip_blank_front(&str);
strip_blank_end(str);
- max_cpu = cpus_online();
+ max_cpu = cpus_configured();
while ((cpu = strsep(&str, ",")) != NULL) {
char *str2, *cpu2;
}
#endif
-static int zone_split_ddir(struct thread_options *o, enum fio_ddir ddir,
- char *str, bool absolute)
+static int zone_split_ddir(struct thread_options *o, void *eo,
+ enum fio_ddir ddir, char *str, bool absolute)
{
unsigned int i, perc, perc_missing, sperc, sperc_missing;
struct split split;
}
str += strlen(pre);
- ret = str_split_parse(td, str, zone_split_ddir, absolute);
+ ret = str_split_parse(td, str, zone_split_ddir, NULL, absolute);
free(p);
}
/*
- * Returns the directory at the index, indexes > entires will be
+ * Returns the directory at the index, indexes > entries will be
* assigned via modulo division of the index
*/
int set_name_idx(char *target, size_t tlen, char *input, int index,
int ret;
/* 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, NULL, NULL);
+ ret = parse_and_fill_pattern_alloc(input, strlen(input),
+ &td->o.buffer_pattern, NULL, NULL, NULL);
if (ret < 0)
return 1;
int ret;
td->o.verify_fmt_sz = FIO_ARRAY_SIZE(td->o.verify_fmt);
- ret = parse_and_fill_pattern(input, strlen(input), td->o.verify_pattern,
- MAX_PATTERN_SIZE, fmt_desc,
- td->o.verify_fmt, &td->o.verify_fmt_sz);
+ ret = parse_and_fill_pattern_alloc(input, strlen(input),
+ &td->o.verify_pattern, fmt_desc, td->o.verify_fmt,
+ &td->o.verify_fmt_sz);
if (ret < 0)
return 1;
int val = *il;
/*
- * Only modfiy options if gtod_reduce==1
+ * Only modify options if gtod_reduce==1
* Otherwise leave settings alone.
*/
if (val) {
.oval = TD_DDIR_TRIMWRITE,
.help = "Trim and write mix, trims preceding writes"
},
+ { .ival = "randtrimwrite",
+ .oval = TD_DDIR_RANDTRIMWRITE,
+ .help = "Randomly trim and write mix, trims preceding writes"
+ },
},
},
{
.help = "Hadoop Distributed Filesystem (HDFS) engine"
},
#endif
-#ifdef CONFIG_PMEMBLK
- { .ival = "pmemblk",
- .help = "PMDK libpmemblk based IO engine",
- },
-
-#endif
#ifdef CONFIG_IME
{ .ival = "ime_psync",
.help = "DDN's IME synchronous IO engine",
.help = "DAOS File System (dfs) IO engine",
},
#endif
-#ifdef CONFIG_NFS
+#ifdef CONFIG_LIBNFS
{ .ival = "nfs",
.help = "NFS IO engine",
},
+#endif
+#ifdef CONFIG_LIBXNVME
+ { .ival = "xnvme",
+ .help = "XNVME IO engine",
+ },
#endif
},
},
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_ZONE,
},
+ {
+ .name = "fdp",
+ .lname = "Flexible data placement",
+ .type = FIO_OPT_BOOL,
+ .off1 = offsetof(struct thread_options, fdp),
+ .help = "Use Data placement directive (FDP)",
+ .def = "0",
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_INVALID,
+ },
+ {
+ .name = "fdp_pli",
+ .lname = "FDP Placement ID indicies",
+ .type = FIO_OPT_STR,
+ .cb = str_fdp_pli_cb,
+ .off1 = offsetof(struct thread_options, fdp_plis),
+ .help = "Sets which placement ids to use (defaults to all)",
+ .hide = 1,
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_INVALID,
+ },
{
.name = "lockmem",
.lname = "Lock memory",
},
.parent = "thinktime",
},
+ {
+ .name = "thinktime_iotime",
+ .lname = "Thinktime interval",
+ .type = FIO_OPT_INT,
+ .off1 = offsetof(struct thread_options, thinktime_iotime),
+ .help = "IO time interval between 'thinktime'",
+ .def = "0",
+ .parent = "thinktime",
+ .hide = 1,
+ .is_seconds = 1,
+ .is_time = 1,
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_THINKTIME,
+ },
{
.name = "rate",
.lname = "I/O rate",
.category = FIO_OPT_C_LOG,
.group = FIO_OPT_G_INVALID,
},
+ {
+ .name = "log_entries",
+ .lname = "Log entries",
+ .type = FIO_OPT_INT,
+ .off1 = offsetof(struct thread_options, log_entries),
+ .help = "Initial number of entries in a job IO log",
+ .def = __fio_stringify(DEF_LOG_ENTRIES),
+ .minval = DEF_LOG_ENTRIES,
+ .maxval = MAX_LOG_ENTRIES,
+ .category = FIO_OPT_C_LOG,
+ .group = FIO_OPT_G_INVALID,
+ },
{
.name = "log_avg_msec",
.lname = "Log averaging (msec)",
.category = FIO_OPT_C_LOG,
.group = FIO_OPT_G_INVALID,
},
+ {
+ .name = "log_prio",
+ .lname = "Log priority of IO",
+ .type = FIO_OPT_BOOL,
+ .off1 = offsetof(struct thread_options, log_prio),
+ .help = "Include priority value of IO for each log entry",
+ .def = "0",
+ .category = FIO_OPT_C_LOG,
+ .group = FIO_OPT_G_INVALID,
+ },
#ifdef CONFIG_ZLIB
{
.name = "log_compression",
.category = FIO_OPT_C_LOG,
.group = FIO_OPT_G_INVALID,
},
+ {
+ .name = "log_alternate_epoch",
+ .lname = "Log epoch alternate",
+ .type = FIO_OPT_BOOL,
+ .off1 = offsetof(struct thread_options, log_alternate_epoch),
+ .help = "Use alternate epoch time in log files. Uses the same epoch as that is used by clock_gettime with specified log_alternate_epoch_clock_id.",
+ .category = FIO_OPT_C_LOG,
+ .group = FIO_OPT_G_INVALID,
+ },
+ {
+ .name = "log_alternate_epoch_clock_id",
+ .lname = "Log alternate epoch clock_id",
+ .type = FIO_OPT_INT,
+ .off1 = offsetof(struct thread_options, log_alternate_epoch_clock_id),
+ .help = "If log_alternate_epoch or log_unix_epoch is true, this option specifies the clock_id from clock_gettime whose epoch should be used. If neither of those is true, this option has no effect. Default value is 0, or CLOCK_REALTIME",
+ .category = FIO_OPT_C_LOG,
+ .group = FIO_OPT_G_INVALID,
+ },
{
.name = "block_error_percentiles",
.lname = "Block error percentiles",
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_IO_BUF,
},
+ {
+ .name = "dedupe_global",
+ .lname = "Global deduplication",
+ .type = FIO_OPT_BOOL,
+ .off1 = offsetof(struct thread_options, dedupe_global),
+ .help = "Share deduplication buffers across jobs",
+ .def = "0",
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_IO_BUF,
+ },
+ {
+ .name = "dedupe_mode",
+ .lname = "Dedupe mode",
+ .help = "Mode for the deduplication buffer generation",
+ .type = FIO_OPT_STR,
+ .off1 = offsetof(struct thread_options, dedupe_mode),
+ .parent = "dedupe_percentage",
+ .def = "repeat",
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_IO_BUF,
+ .posval = {
+ { .ival = "repeat",
+ .oval = DEDUPE_MODE_REPEAT,
+ .help = "repeat previous page",
+ },
+ { .ival = "working_set",
+ .oval = DEDUPE_MODE_WORKING_SET,
+ .help = "choose a page randomly from limited working set defined in dedupe_working_set_percentage",
+ },
+ },
+ },
+ {
+ .name = "dedupe_working_set_percentage",
+ .lname = "Dedupe working set percentage",
+ .help = "Dedupe working set size in percentages from file or device size used to generate dedupe patterns from",
+ .type = FIO_OPT_INT,
+ .off1 = offsetof(struct thread_options, dedupe_working_set_percentage),
+ .parent = "dedupe_percentage",
+ .def = "5",
+ .maxval = 100,
+ .minval = 0,
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_IO_BUF,
+ },
{
.name = "clat_percentiles",
.lname = "Completion latency percentiles",
sprintf(buf, "%llu", mb_memory);
fio_keywords[1].replace = strdup(buf);
- l = cpus_online();
+ l = cpus_configured();
sprintf(buf, "%lu", l);
fio_keywords[2].replace = strdup(buf);
}