if (perc > 100)
perc = 100;
else if (!perc)
- perc = -1;
+ perc = -1U;
} else
- perc = -1;
+ perc = -1U;
- if (str_to_decimal(fname, &val, 1, o, 0)) {
+ if (str_to_decimal(fname, &val, 1, o, 0, 0)) {
log_err("fio: bssplit conversion failed\n");
free(bssplit);
return 1;
for (i = 0; i < o->bssplit_nr[ddir]; i++) {
struct bssplit *bsp = &bssplit[i];
- if (bsp->perc == (unsigned char) -1)
+ if (bsp->perc == -1U)
perc_missing++;
else
perc += bsp->perc;
}
- if (perc > 100) {
+ if (perc > 100 && perc_missing > 1) {
log_err("fio: bssplit percentages add to more than 100%%\n");
free(bssplit);
return 1;
}
+
/*
* If values didn't have a percentage set, divide the remains between
* them.
*/
if (perc_missing) {
+ if (perc_missing == 1 && o->bssplit_nr[ddir] == 1)
+ perc = 100;
for (i = 0; i < o->bssplit_nr[ddir]; i++) {
struct bssplit *bsp = &bssplit[i];
- if (bsp->perc == (unsigned char) -1)
+ if (bsp->perc == -1U)
bsp->perc = (100 - perc) / perc_missing;
}
}
else {
long long val;
- if (str_to_decimal(nr, &val, 1, o, 0)) {
+ if (str_to_decimal(nr, &val, 1, o, 0, 0)) {
log_err("fio: rw postfix parsing failed\n");
free(nr);
return 1;
}
}
- td->o.cpumask_set = 1;
return 0;
}
}
free(p);
- if (!ret)
- td->o.cpumask_set = 1;
return ret;
}
static int str_cpus_allowed_cb(void *data, const char *input)
{
struct thread_data *td = data;
- int ret;
if (parse_dryrun())
return 0;
- ret = set_cpus_allowed(td, &td->o.cpumask, input);
- if (!ret)
- td->o.cpumask_set = 1;
-
- return ret;
+ return set_cpus_allowed(td, &td->o.cpumask, input);
}
static int str_verify_cpus_allowed_cb(void *data, const char *input)
{
struct thread_data *td = data;
- int ret;
-
- ret = set_cpus_allowed(td, &td->o.verify_cpumask, input);
- if (!ret)
- td->o.verify_cpumask_set = 1;
- return ret;
+ return set_cpus_allowed(td, &td->o.verify_cpumask, input);
}
#endif
numa_free_nodemask(verify_bitmask);
td->o.numa_cpunodes = strdup(input);
- td->o.numa_cpumask_set = 1;
return 0;
}
}
td->o.numa_memnodes = strdup(nodelist);
numa_free_nodemask(verify_bitmask);
-
+
break;
case MPOL_LOCAL:
case MPOL_DEFAULT:
break;
}
- td->o.numa_memmask_set = 1;
return 0;
-
out:
return 1;
}
return 0;
nr = get_opt_postfix(str);
- if (nr && !str_to_float(nr, &val)) {
+ if (nr && !str_to_float(nr, &val, 0)) {
log_err("fio: random postfix parsing failed\n");
free(nr);
return 1;
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)
* 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);
if (i == 1) {
/*
- * The code in verify_io_u_pattern assumes a single byte pattern
- * fills the whole verify pattern buffer.
+ * The code in verify_io_u_pattern assumes a single byte
+ * pattern fills the whole verify pattern buffer.
*/
memset(pattern, pattern[0], max_size);
}
ret = pattern_cb(td->o.buffer_pattern, MAX_PATTERN_SIZE, input,
&td->o.buffer_pattern_bytes);
- if (!ret) {
- td->o.refill_buffers = 0;
+ 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;
}
return ret;
return 0;
}
+static int str_dedupe_cb(void *data, unsigned long long *il)
+{
+ struct thread_data *td = data;
+
+ td->flags |= TD_F_COMPRESS;
+ td->o.dedupe_percentage = *il;
+ td->o.refill_buffers = 1;
+ return 0;
+}
+
static int str_verify_pattern_cb(void *data, const char *input)
{
struct thread_data *td = data;
.help = "Glusterfs libgfapi(async) based engine"
},
#endif
-
+#ifdef CONFIG_LIBHDFS
+ { .ival = "libhdfs",
+ .help = "Hadoop Distributed Filesystem (HDFS) engine"
+ },
+#endif
{ .ival = "external",
.help = "Load external engine (append name)",
},
.lname = "Size",
.type = FIO_OPT_STR_VAL,
.cb = str_size_cb,
+ .off1 = td_var_offset(size),
.help = "Total size of device or files",
.interval = 1024 * 1024,
.category = FIO_OPT_C_IO,
.lname = "Number of IOs to perform",
.type = FIO_OPT_STR_VAL,
.off1 = td_var_offset(number_ios),
- .help = "Force job completion of this number of IOs",
+ .help = "Force job completion after this number of IOs",
.def = "0",
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_INVALID,
.lname = "Block size split",
.type = FIO_OPT_STR,
.cb = str_bssplit_cb,
+ .off1 = td_var_offset(bssplit),
.help = "Set a specific mix of block sizes",
.parent = "rw",
.hide = 1,
.lname = "Block size division is seq/random (not read/write)",
.type = FIO_OPT_BOOL,
.off1 = td_var_offset(bs_is_seq_rand),
- .help = "Consider any blocksize setting to be sequential,ramdom",
+ .help = "Consider any blocksize setting to be sequential,random",
.def = "0",
.parent = "blocksize",
.category = FIO_OPT_C_IO,
{
.name = "use_os_rand",
.lname = "Use OS random",
- .type = FIO_OPT_BOOL,
- .off1 = td_var_offset(use_os_rand),
- .help = "Set to use OS random generator",
- .def = "0",
- .parent = "rw",
- .hide = 1,
+ .type = FIO_OPT_DEPRECATED,
+ .off1 = td_var_offset(dep_use_os_rand),
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_RANDOM,
},
.help = "Only start job when this period has passed",
.def = "0",
.is_seconds = 1,
+ .is_time = 1,
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_RUNTIME,
},
.help = "Stop workload when this amount of time has passed",
.def = "0",
.is_seconds = 1,
+ .is_time = 1,
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_RUNTIME,
},
.off1 = td_var_offset(ramp_time),
.help = "Ramp up time before measuring performance",
.is_seconds = 1,
+ .is_time = 1,
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_RUNTIME,
},
.lname = "Verify pattern",
.type = FIO_OPT_STR,
.cb = str_verify_pattern_cb,
+ .off1 = td_var_offset(verify_pattern),
.help = "Fill pattern for IO buffers",
.parent = "verify",
.hide = 1,
.lname = "Async verify CPUs",
.type = FIO_OPT_STR,
.cb = str_verify_cpus_allowed_cb,
+ .off1 = td_var_offset(verify_cpumask),
.help = "Set CPUs allowed for async verify threads",
.parent = "verify_async",
.hide = 1,
.off1 = td_var_offset(experimental_verify),
.type = FIO_OPT_BOOL,
.help = "Enable experimental verification",
+ .parent = "verify",
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_VERIFY,
+ },
+ {
+ .name = "verify_state_load",
+ .lname = "Load verify state",
+ .off1 = td_var_offset(verify_state),
+ .type = FIO_OPT_BOOL,
+ .help = "Load verify termination state",
+ .parent = "verify",
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_VERIFY,
+ },
+ {
+ .name = "verify_state_save",
+ .lname = "Save verify state",
+ .off1 = td_var_offset(verify_state_save),
+ .type = FIO_OPT_BOOL,
+ .def = "1",
+ .help = "Save verify state on termination",
+ .parent = "verify",
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_VERIFY,
},
.lname = "Read/write mix read",
.type = FIO_OPT_INT,
.cb = str_rwmix_read_cb,
+ .off1 = td_var_offset(rwmix[DDIR_READ]),
.maxval = 100,
.help = "Percentage of mixed workload that is reads",
.def = "50",
.lname = "Read/write mix write",
.type = FIO_OPT_INT,
.cb = str_rwmix_write_cb,
+ .off1 = td_var_offset(rwmix[DDIR_WRITE]),
.maxval = 100,
.help = "Percentage of mixed workload that is writes",
.def = "50",
.off1 = td_var_offset(thinktime),
.help = "Idle time between IO buffers (usec)",
.def = "0",
+ .is_time = 1,
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_THINKTIME,
},
.off1 = td_var_offset(thinktime_spin),
.help = "Start think time by spinning this amount (usec)",
.def = "0",
+ .is_time = 1,
.parent = "thinktime",
.hide = 1,
.category = FIO_OPT_C_IO,
.type = FIO_OPT_INT,
.off1 = td_var_offset(max_latency),
.help = "Maximum tolerated IO latency (usec)",
+ .is_time = 1,
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_LATPROF,
},
.type = FIO_OPT_STR_VAL_TIME,
.off1 = td_var_offset(latency_target),
.help = "Ramp to max queue depth supporting this latency",
+ .is_time = 1,
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_LATPROF,
},
.type = FIO_OPT_STR_VAL_TIME,
.off1 = td_var_offset(latency_window),
.help = "Time to sustain latency_target",
+ .is_time = 1,
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_LATPROF,
},
.lname = "CPU mask",
.type = FIO_OPT_INT,
.cb = str_cpumask_cb,
+ .off1 = td_var_offset(cpumask),
.help = "CPU affinity mask",
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_CRED,
.lname = "CPUs allowed",
.type = FIO_OPT_STR,
.cb = str_cpus_allowed_cb,
+ .off1 = td_var_offset(cpumask),
.help = "Set CPUs allowed",
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_CRED,
.name = "numa_cpu_nodes",
.type = FIO_OPT_STR,
.cb = str_numa_cpunodes_cb,
+ .off1 = td_var_offset(numa_cpunodes),
.help = "NUMA CPU nodes bind",
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_INVALID,
.name = "numa_mem_policy",
.type = FIO_OPT_STR,
.cb = str_numa_mpol_cb,
+ .off1 = td_var_offset(numa_memnodes),
.help = "NUMA memory policy setup",
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_INVALID,
.category = FIO_OPT_C_LOG,
.group = FIO_OPT_G_INVALID,
},
+ {
+ .name = "log_offset",
+ .lname = "Log offset of IO",
+ .type = FIO_OPT_BOOL,
+ .off1 = td_var_offset(log_offset),
+ .help = "Include offset of IO for each log entry",
+ .def = "0",
+ .category = FIO_OPT_C_LOG,
+ .group = FIO_OPT_G_INVALID,
+ },
+#ifdef CONFIG_ZLIB
+ {
+ .name = "log_compression",
+ .lname = "Log compression",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(log_gz),
+ .help = "Log in compressed chunks of this size",
+ .minval = 32 * 1024 * 1024ULL,
+ .maxval = 512 * 1024 * 1024ULL,
+ .category = FIO_OPT_C_LOG,
+ .group = FIO_OPT_G_INVALID,
+ },
+ {
+ .name = "log_store_compressed",
+ .lname = "Log store compressed",
+ .type = FIO_OPT_BOOL,
+ .off1 = td_var_offset(log_gz_store),
+ .help = "Store logs in a compressed format",
+ .category = FIO_OPT_C_LOG,
+ .group = FIO_OPT_G_INVALID,
+ },
+#endif
{
.name = "bwavgtime",
.lname = "Bandwidth average time",
.lname = "Buffer pattern",
.type = FIO_OPT_STR,
.cb = str_buffer_pattern_cb,
+ .off1 = td_var_offset(buffer_pattern),
.help = "Fill pattern for IO buffers",
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_IO_BUF,
.lname = "Buffer compression percentage",
.type = FIO_OPT_INT,
.cb = str_buffer_compress_cb,
+ .off1 = td_var_offset(compress_percentage),
.maxval = 100,
.minval = 0,
.help = "How compressible the buffer is (approximately)",
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_IO_BUF,
},
+ {
+ .name = "dedupe_percentage",
+ .lname = "Dedupe percentage",
+ .type = FIO_OPT_INT,
+ .cb = str_dedupe_cb,
+ .off1 = td_var_offset(dedupe_percentage),
+ .maxval = 100,
+ .minval = 0,
+ .help = "Percentage of buffers that are dedupable",
+ .interval = 1,
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_IO_BUF,
+ },
{
.name = "clat_percentiles",
.lname = "Completion latency percentiles",
.name = "ignore_error",
.type = FIO_OPT_STR,
.cb = str_ignore_error_cb,
+ .off1 = td_var_offset(ignore_error_nr),
.help = "Set a specific list of errors to ignore",
.parent = "rw",
.category = FIO_OPT_C_GENERAL,
return opts_copy;
}
+static void show_closest_option(const char *opt)
+{
+ int best_option, best_distance;
+ int i, distance;
+ char *name;
+
+ if (!strlen(opt))
+ return;
+
+ name = strdup(opt);
+ i = 0;
+ while (name[i] != '\0' && name[i] != '=')
+ i++;
+ name[i] = '\0';
+
+ best_option = -1;
+ best_distance = INT_MAX;
+ i = 0;
+ while (fio_options[i].name) {
+ distance = string_distance(name, fio_options[i].name);
+ if (distance < best_distance) {
+ best_distance = distance;
+ best_option = i;
+ }
+ i++;
+ }
+
+ if (best_option != -1)
+ 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 newret = parse_option(opts_copy[i], opts[i], fio_options,
&o, td, dump_cmdline);
+ if (!newret && o)
+ fio_option_mark_set(&td->o, o);
+
if (opts_copy[i]) {
if (newret && !o) {
unknown++;
for (i = 0; i < num_opts; i++) {
struct fio_option *o = NULL;
int newret = 1;
+
if (!opts_copy[i])
continue;
td->eo, dump_cmdline);
ret |= newret;
- if (!o)
+ if (!o) {
log_err("Bad option <%s>\n", opts[i]);
-
+ show_closest_option(opts[i]);
+ }
free(opts_copy[i]);
opts_copy[i] = NULL;
}
int fio_cmd_option_parse(struct thread_data *td, const char *opt, char *val)
{
- return parse_cmd_option(opt, val, fio_options, td);
+ int ret;
+
+ ret = parse_cmd_option(opt, val, fio_options, td);
+ if (!ret) {
+ struct fio_option *o;
+
+ o = find_option(fio_options, opt);
+ if (o)
+ fio_option_mark_set(&td->o, o);
+ }
+
+ return ret;
}
int fio_cmd_ioengine_option_parse(struct thread_data *td, const char *opt,
return find_option(fio_options, name);
}
+int __fio_option_is_set(struct thread_options *o, unsigned int off1)
+{
+ unsigned int opt_off, index, offset;
+ struct fio_option *opt = NULL;
+ int i;
+
+ for (i = 0; fio_options[i].name; i++) {
+ if (off1 == fio_options[i].off1) {
+ opt = &fio_options[i];
+ break;
+ }
+ }
+
+ if (!opt) {
+ log_err("fio: no option found at offset %u\n", off1);
+ return 0;
+ }
+
+ 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;
+}
+
+void fio_option_mark_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);
+ o->set_options[index] |= 1UL << offset;
+}