return 0;
}
-#ifdef FIO_HAVE_LIBAIO
-static int str_libaio_cb(void *data, const char *str)
-{
- struct thread_data *td = data;
-
- if (!strcmp(str, "userspace_reap")) {
- td->o.userspace_libaio_reap = 1;
- return 0;
- }
-
- log_err("fio: bad libaio sub-option: %s\n", str);
- return 1;
-}
-#endif
-
static int str_mem_cb(void *data, const char *mem)
{
struct thread_data *td = data;
{
struct thread_data *td = data;
- if (td->o.verify != VERIFY_CRC32C_INTEL)
- return 0;
-
- if (!crc32c_intel_works()) {
- log_info("fio: System does not support hw accelerated crc32c. Falling back to sw crc32c.\n");
- td->o.verify = VERIFY_CRC32C;
+ if (td->o.verify == VERIFY_CRC32C_INTEL ||
+ td->o.verify == VERIFY_CRC32C) {
+ crc32c_intel_probe();
}
return 0;
return start;
}
-static int str_hostname_cb(void *data, const char *input)
-{
- struct thread_data *td = data;
-
- td->o.filename = strdup(input);
- return 0;
-}
-
static int str_filename_cb(void *data, const char *input)
{
struct thread_data *td = data;
}
}
}
+
+ /*
+ * 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.
+ */
+ while (i > 1 && i * 2 <= MAX_PATTERN_SIZE) {
+ memcpy(&td->o.verify_pattern[i], &td->o.verify_pattern[0], i);
+ i *= 2;
+ }
+ if (i == 1) {
+ /*
+ * The code in verify_io_u_pattern assumes a single byte pattern
+ * fills the whole verify pattern buffer.
+ */
+ memset(td->o.verify_pattern, td->o.verify_pattern[0],
+ MAX_PATTERN_SIZE);
+ }
+
td->o.verify_pattern_bytes = i;
+
/*
* VERIFY_META could already be set
*/
if (td->o.verify == VERIFY_NONE)
td->o.verify = VERIFY_PATTERN;
+
return 0;
}
.prio = -1, /* must come after "directory" */
.help = "File(s) to use for the workload",
},
- {
- .name = "hostname",
- .type = FIO_OPT_STR_STORE,
- .cb = str_hostname_cb,
- .help = "Hostname for net IO engine",
- },
{
.name = "kb_base",
.type = FIO_OPT_INT,
.oval = TD_DDIR_RW,
.help = "Sequential read and write mix",
},
+ { .ival = "readwrite",
+ .oval = TD_DDIR_RW,
+ .help = "Sequential read and write mix",
+ },
{ .ival = "randrw",
.oval = TD_DDIR_RANDRW,
.help = "Random read and write mix"
#ifdef FIO_HAVE_LIBAIO
{ .ival = "libaio",
.help = "Linux native asynchronous IO",
- .cb = str_libaio_cb,
},
#endif
#ifdef FIO_HAVE_POSIXAIO
#ifdef FIO_HAVE_WINDOWSAIO
{ .ival = "windowsaio",
.help = "Windows native asynchronous IO"
- },
+ },
#endif
{ .ival = "mmap",
.help = "Memory mapped IO"
.help = "Start IO from this offset",
.def = "0",
},
+ {
+ .name = "offset_increment",
+ .type = FIO_OPT_STR_VAL,
+ .off1 = td_var_offset(offset_increment),
+ .help = "What is the increment from one offset to the next",
+ .parent = "offset",
+ .def = "0",
+ },
{
.name = "bs",
.alias = "blocksize",
.help = "Use crc32 checksums for verification",
},
{ .ival = "crc32c-intel",
- .oval = VERIFY_CRC32C_INTEL,
- .help = "Use hw crc32c checksums for verification",
+ .oval = VERIFY_CRC32C,
+ .help = "Use crc32c checksums for verification (hw assisted, if available)",
},
{ .ival = "crc32c",
.oval = VERIFY_CRC32C,
- .help = "Use crc32c checksums for verification",
+ .help = "Use crc32c checksums for verification (hw assisted, if available)",
},
{ .ival = "crc16",
.oval = VERIFY_CRC16,
.name = "zonesize",
.type = FIO_OPT_STR_VAL,
.off1 = td_var_offset(zone_size),
+ .help = "Amount of data to read per zone",
+ .def = "0",
+ },
+ {
+ .name = "zonerange",
+ .type = FIO_OPT_STR_VAL,
+ .off1 = td_var_offset(zone_range),
.help = "Give size of an IO zone",
.def = "0",
},
.cb = str_write_iops_log_cb,
.help = "Write log of IOPS during run",
},
+ {
+ .name = "log_avg_msec",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(log_avg_msec),
+ .help = "Average bw/iops/lat logs over this period of time",
+ .def = "0",
+ },
{
.name = "hugepage-size",
.type = FIO_OPT_INT,
.help = "Slightly scramble buffers on every IO submit",
.def = "1",
},
+ {
+ .name = "buffer_compress_percentage",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(compress_percentage),
+ .maxval = 100,
+ .minval = 1,
+ .help = "How compressible the buffer is (approximately)",
+ },
+ {
+ .name = "buffer_compress_chunk",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(compress_chunk),
+ .parent = "buffer_compress_percentage",
+ .help = "Size of compressible region in buffer",
+ },
{
.name = "clat_percentiles",
.type = FIO_OPT_BOOL,
},
{
.name = "continue_on_error",
- .type = FIO_OPT_BOOL,
+ .type = FIO_OPT_STR,
.off1 = td_var_offset(continue_on_error),
.help = "Continue on non-fatal errors during IO",
- .def = "0",
+ .def = "none",
+ .posval = {
+ { .ival = "none",
+ .oval = ERROR_TYPE_NONE,
+ .help = "Exit when an error is encountered",
+ },
+ { .ival = "read",
+ .oval = ERROR_TYPE_READ,
+ .help = "Continue on read errors only",
+ },
+ { .ival = "write",
+ .oval = ERROR_TYPE_WRITE,
+ .help = "Continue on write errors only",
+ },
+ { .ival = "io",
+ .oval = ERROR_TYPE_READ | ERROR_TYPE_WRITE,
+ .help = "Continue on any IO errors",
+ },
+ { .ival = "verify",
+ .oval = ERROR_TYPE_VERIFY,
+ .help = "Continue on verify errors only",
+ },
+ { .ival = "all",
+ .oval = ERROR_TYPE_ANY,
+ .help = "Continue on all io and verify errors",
+ },
+ { .ival = "0",
+ .oval = ERROR_TYPE_NONE,
+ .help = "Alias for 'none'",
+ },
+ { .ival = "1",
+ .oval = ERROR_TYPE_ANY,
+ .help = "Alias for 'all'",
+ },
+ },
},
{
.name = "profile",
.off1 = td_var_offset(gid),
.help = "Run job with this group ID",
},
+ {
+ .name = "flow_id",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(flow_id),
+ .help = "The flow index ID to use",
+ .def = "0",
+ },
+ {
+ .name = "flow",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(flow),
+ .help = "Weight for flow control of this job",
+ .parent = "flow_id",
+ .def = "0",
+ },
+ {
+ .name = "flow_watermark",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(flow_watermark),
+ .help = "High watermark for flow control. This option"
+ " should be set to the same value for all threads"
+ " with non-zero flow.",
+ .parent = "flow_id",
+ .def = "1024",
+ },
+ {
+ .name = "flow_sleep",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(flow_sleep),
+ .help = "How many microseconds to sleep after being held"
+ " back by the flow control mechanism",
+ .parent = "flow_id",
+ .def = "0",
+ },
{
.name = NULL,
},
};
static void add_to_lopt(struct option *lopt, struct fio_option *o,
- const char *name)
+ const char *name, int val)
{
lopt->name = (char *) name;
- lopt->val = FIO_GETOPT_JOB;
+ lopt->val = val;
if (o->type == FIO_OPT_STR_SET)
lopt->has_arg = no_argument;
else
lopt->has_arg = required_argument;
}
-void fio_options_dup_and_init(struct option *long_options)
+static void options_to_lopts(struct fio_option *opts,
+ struct option *long_options,
+ int i, int option_type)
{
- struct fio_option *o;
- unsigned int i;
-
- options_init(options);
-
- i = 0;
- while (long_options[i].name)
- i++;
-
- o = &options[0];
+ struct fio_option *o = &opts[0];
while (o->name) {
- add_to_lopt(&long_options[i], o, o->name);
+ add_to_lopt(&long_options[i], o, o->name, option_type);
if (o->alias) {
i++;
- add_to_lopt(&long_options[i], o, o->alias);
+ add_to_lopt(&long_options[i], o, o->alias, option_type);
}
i++;
}
}
+void fio_options_set_ioengine_opts(struct option *long_options,
+ struct thread_data *td)
+{
+ unsigned int i;
+
+ i = 0;
+ while (long_options[i].name) {
+ if (long_options[i].val == FIO_GETOPT_IOENGINE) {
+ memset(&long_options[i], 0, sizeof(*long_options));
+ break;
+ }
+ i++;
+ }
+
+ /*
+ * Just clear out the prior ioengine options.
+ */
+ if (!td || !td->eo)
+ return;
+
+ options_to_lopts(td->io_ops->options, long_options, i,
+ FIO_GETOPT_IOENGINE);
+}
+
+void fio_options_dup_and_init(struct option *long_options)
+{
+ unsigned int i;
+
+ options_init(options);
+
+ i = 0;
+ while (long_options[i].name)
+ i++;
+
+ options_to_lopts(options, long_options, i, FIO_GETOPT_JOB);
+}
+
struct fio_keyword {
const char *word;
const char *desc;
int fio_options_parse(struct thread_data *td, char **opts, int num_opts)
{
- int i, ret;
+ int i, ret, unknown;
char **opts_copy;
sort_options(opts, options, num_opts);
opts_copy = dup_and_sub_options(opts, num_opts);
- for (ret = 0, i = 0; i < num_opts; i++) {
- ret |= parse_option(opts_copy[i], opts[i], options, td);
+ for (ret = 0, i = 0, unknown = 0; i < num_opts; i++) {
+ struct fio_option *o;
+ int newret = parse_option(opts_copy[i], opts[i], options, &o,
+ td);
+
+ if (opts_copy[i]) {
+ if (newret && !o) {
+ unknown++;
+ continue;
+ }
+ free(opts_copy[i]);
+ opts_copy[i] = NULL;
+ }
+
+ ret |= newret;
+ }
+
+ if (unknown) {
+ ret |= ioengine_load(td);
+ if (td->eo) {
+ sort_options(opts_copy, td->io_ops->options, num_opts);
+ opts = opts_copy;
+ }
+ for (i = 0; i < num_opts; i++) {
+ struct fio_option *o = NULL;
+ int newret = 1;
+ if (!opts_copy[i])
+ continue;
+
+ if (td->eo)
+ newret = parse_option(opts_copy[i], opts[i],
+ td->io_ops->options, &o,
+ td->eo);
+
+ ret |= newret;
+ if (!o)
+ log_err("Bad option <%s>\n", opts[i]);
- if (opts_copy[i])
free(opts_copy[i]);
- opts_copy[i] = NULL;
+ opts_copy[i] = NULL;
+ }
}
free(opts_copy);
return parse_cmd_option(opt, val, options, td);
}
+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);
+}
+
void fio_fill_default_options(struct thread_data *td)
{
fill_default_options(td, options);
return show_cmd_help(options, opt);
}
-/*
- * dupe FIO_OPT_STR_STORE options
- */
-void options_mem_dupe(struct thread_data *td)
+void options_mem_dupe(void *data, struct fio_option *options)
{
- struct thread_options *o = &td->o;
- struct fio_option *opt;
+ struct fio_option *o;
char **ptr;
- int i;
- for (i = 0, opt = &options[0]; opt->name; i++, opt = &options[i]) {
- if (opt->type != FIO_OPT_STR_STORE)
+ for (o = &options[0]; o->name; o++) {
+ if (o->type != FIO_OPT_STR_STORE)
continue;
- ptr = (void *) o + opt->off1;
- if (!*ptr)
- ptr = td_var(o, opt->off1);
+ ptr = td_var(data, 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, options);
+
+ 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);
+ }
+}
+
unsigned int fio_get_kb_base(void *data)
{
struct thread_data *td = data;
void fio_options_free(struct thread_data *td)
{
options_free(options, td);
+ if (td->eo && td->io_ops && td->io_ops->options) {
+ options_free(td->io_ops->options, td->eo);
+ free(td->eo);
+ td->eo = NULL;
+ }
}