if (str_to_decimal(fname, &val, 1, o)) {
log_err("fio: bssplit conversion failed\n");
- free(o->bssplit);
+ free(bssplit);
return 1;
}
static int str_bssplit_cb(void *data, const char *input)
{
struct thread_data *td = data;
- char *str, *p, *odir;
+ char *str, *p, *odir, *ddir;
int ret = 0;
+ if (parse_dryrun())
+ return 0;
+
p = str = strdup(input);
strip_blank_front(&str);
odir = strchr(str, ',');
if (odir) {
- ret = bssplit_ddir(&td->o, DDIR_WRITE, odir + 1);
+ ddir = strchr(odir + 1, ',');
+ if (ddir) {
+ ret = bssplit_ddir(&td->o, DDIR_TRIM, ddir + 1);
+ if (!ret)
+ *ddir = '\0';
+ } else {
+ char *op;
+
+ op = strdup(odir + 1);
+ ret = bssplit_ddir(&td->o, DDIR_TRIM, op);
+
+ free(op);
+ }
+ if (!ret)
+ ret = bssplit_ddir(&td->o, DDIR_WRITE, odir + 1);
if (!ret) {
*odir = '\0';
ret = bssplit_ddir(&td->o, DDIR_READ, str);
char *op;
op = strdup(str);
+ ret = bssplit_ddir(&td->o, DDIR_WRITE, op);
+ free(op);
+ if (!ret) {
+ op = strdup(str);
+ 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_WRITE, op);
-
- free(op);
}
free(p);
return ret;
}
+static int str2error(char *str)
+{
+ const char *err[] = { "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO",
+ "ENXIO", "E2BIG", "ENOEXEC", "EBADF",
+ "ECHILD", "EAGAIN", "ENOMEM", "EACCES",
+ "EFAULT", "ENOTBLK", "EBUSY", "EEXIST",
+ "EXDEV", "ENODEV", "ENOTDIR", "EISDIR",
+ "EINVAL", "ENFILE", "EMFILE", "ENOTTY",
+ "ETXTBSY","EFBIG", "ENOSPC", "ESPIPE",
+ "EROFS","EMLINK", "EPIPE", "EDOM", "ERANGE" };
+ int i = 0, num = sizeof(err) / sizeof(void *);
+
+ while (i < num) {
+ if (!strcmp(err[i], str))
+ return i + 1;
+ i++;
+ }
+ return 0;
+}
+
+static int ignore_error_type(struct thread_data *td, int etype, char *str)
+{
+ unsigned int i;
+ int *error;
+ char *fname;
+
+ if (etype >= ERROR_TYPE_CNT) {
+ log_err("Illegal error type\n");
+ return 1;
+ }
+
+ td->o.ignore_error_nr[etype] = 4;
+ error = malloc(4 * sizeof(struct bssplit));
+
+ i = 0;
+ while ((fname = strsep(&str, ":")) != NULL) {
+
+ if (!strlen(fname))
+ break;
+
+ /*
+ * grow struct buffer, if needed
+ */
+ if (i == td->o.ignore_error_nr[etype]) {
+ td->o.ignore_error_nr[etype] <<= 1;
+ error = realloc(error, td->o.ignore_error_nr[etype]
+ * sizeof(int));
+ }
+ if (fname[0] == 'E') {
+ error[i] = str2error(fname);
+ } else {
+ error[i] = atoi(fname);
+ if (error[i] < 0)
+ error[i] = error[i];
+ }
+ if (!error[i]) {
+ log_err("Unknown error %s, please use number value \n",
+ fname);
+ free(error);
+ return 1;
+ }
+ i++;
+ }
+ if (i) {
+ td->o.continue_on_error |= 1 << etype;
+ td->o.ignore_error_nr[etype] = i;
+ td->o.ignore_error[etype] = error;
+ }
+ return 0;
+
+}
+
+static int str_ignore_error_cb(void *data, const char *input)
+{
+ struct thread_data *td = data;
+ char *str, *p, *n;
+ int type = 0, ret = 1;
+
+ 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';
+ ret = ignore_error_type(td, type, p);
+ if (ret)
+ break;
+ p = n;
+ type++;
+ }
+ free(str);
+ return ret;
+}
+
static int str_rw_cb(void *data, const char *str)
{
struct thread_data *td = data;
struct thread_options *o = &td->o;
char *nr = get_opt_postfix(str);
+ if (parse_dryrun())
+ return 0;
+
o->ddir_seq_nr = 1;
o->ddir_seq_add = 0;
static int str_mem_cb(void *data, const char *mem)
{
struct thread_data *td = data;
- struct thread_options *o = &td->o;
- if (o->mem_type == MEM_MMAPHUGE || o->mem_type == MEM_MMAP) {
- o->mmapfile = get_opt_postfix(mem);
- if (o->mem_type == MEM_MMAPHUGE && !o->mmapfile) {
- log_err("fio: mmaphuge:/path/to/file\n");
- return 1;
- }
- }
+ if (td->o.mem_type == MEM_MMAPHUGE || td->o.mem_type == MEM_MMAP)
+ td->o.mmapfile = get_opt_postfix(mem);
return 0;
}
struct thread_data *td = data;
fio_clock_source = td->o.clocksource;
- fio_time_init();
+ fio_clock_source_set = 1;
+ fio_clock_init();
return 0;
}
long max_cpu;
int ret;
+ if (parse_dryrun())
+ return 0;
+
ret = fio_cpuset_init(&td->o.cpumask);
if (ret < 0) {
log_err("fio: cpuset_init failed\n");
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;
}
#endif
+#ifdef CONFIG_LIBNUMA
+static int str_numa_cpunodes_cb(void *data, char *input)
+{
+ struct thread_data *td = data;
+
+ if (parse_dryrun())
+ return 0;
+
+ /* numa_parse_nodestring() parses a character string list
+ * of nodes into a bit mask. The bit mask is allocated by
+ * numa_allocate_nodemask(), so it should be freed by
+ * numa_free_nodemask().
+ */
+ td->o.numa_cpunodesmask = numa_parse_nodestring(input);
+ if (td->o.numa_cpunodesmask == NULL) {
+ log_err("fio: numa_parse_nodestring failed\n");
+ td_verror(td, 1, "str_numa_cpunodes_cb");
+ return 1;
+ }
+
+ td->o.numa_cpumask_set = 1;
+ return 0;
+}
+
+static int str_numa_mpol_cb(void *data, char *input)
+{
+ struct thread_data *td = data;
+ const char * const policy_types[] =
+ { "default", "prefer", "bind", "interleave", "local", NULL };
+ int i;
+ char *nodelist;
+
+ if (parse_dryrun())
+ return 0;
+
+ nodelist = strchr(input, ':');
+ if (nodelist) {
+ /* NUL-terminate mode */
+ *nodelist++ = '\0';
+ }
+
+ for (i = 0; i <= MPOL_LOCAL; i++) {
+ if (!strcmp(input, policy_types[i])) {
+ td->o.numa_mem_mode = i;
+ break;
+ }
+ }
+ if (i > MPOL_LOCAL) {
+ log_err("fio: memory policy should be: default, prefer, bind, interleave, local\n");
+ goto out;
+ }
+
+ switch (td->o.numa_mem_mode) {
+ case MPOL_PREFERRED:
+ /*
+ * Insist on a nodelist of one node only
+ */
+ if (nodelist) {
+ char *rest = nodelist;
+ while (isdigit(*rest))
+ rest++;
+ if (*rest) {
+ log_err("fio: one node only for \'prefer\'\n");
+ goto out;
+ }
+ } else {
+ log_err("fio: one node is needed for \'prefer\'\n");
+ goto out;
+ }
+ break;
+ case MPOL_INTERLEAVE:
+ /*
+ * Default to online nodes with memory if no nodelist
+ */
+ if (!nodelist)
+ nodelist = strdup("all");
+ break;
+ case MPOL_LOCAL:
+ case MPOL_DEFAULT:
+ /*
+ * Don't allow a nodelist
+ */
+ if (nodelist) {
+ log_err("fio: NO nodelist for \'local\'\n");
+ goto out;
+ }
+ break;
+ case MPOL_BIND:
+ /*
+ * Insist on a nodelist
+ */
+ if (!nodelist) {
+ log_err("fio: a nodelist is needed for \'bind\'\n");
+ goto out;
+ }
+ break;
+ }
+
+
+ /* numa_parse_nodestring() parses a character string list
+ * of nodes into a bit mask. The bit mask is allocated by
+ * numa_allocate_nodemask(), so it should be freed by
+ * numa_free_nodemask().
+ */
+ switch (td->o.numa_mem_mode) {
+ case MPOL_PREFERRED:
+ td->o.numa_mem_prefer_node = atoi(nodelist);
+ break;
+ case MPOL_INTERLEAVE:
+ case MPOL_BIND:
+ td->o.numa_memnodesmask = numa_parse_nodestring(nodelist);
+ if (td->o.numa_memnodesmask == NULL) {
+ log_err("fio: numa_parse_nodestring failed\n");
+ td_verror(td, 1, "str_numa_memnodes_cb");
+ return 1;
+ }
+ break;
+ case MPOL_LOCAL:
+ case MPOL_DEFAULT:
+ default:
+ break;
+ }
+
+ td->o.numa_memmask_set = 1;
+ return 0;
+
+out:
+ return 1;
+}
+#endif
+
static int str_fst_cb(void *data, const char *str)
{
struct thread_data *td = data;
return 0;
}
-#ifdef FIO_HAVE_SYNC_FILE_RANGE
+#ifdef CONFIG_SYNC_FILE_RANGE
static int str_sfr_cb(void *data, const char *str)
{
struct thread_data *td = data;
}
#endif
+static int str_random_distribution_cb(void *data, const char *str)
+{
+ struct thread_data *td = data;
+ double val;
+ char *nr;
+
+ if (parse_dryrun())
+ return 0;
+
+ if (td->o.random_distribution == FIO_RAND_DIST_ZIPF)
+ val = 1.1;
+ else if (td->o.random_distribution == FIO_RAND_DIST_PARETO)
+ val = 0.2;
+ else
+ return 0;
+
+ nr = get_opt_postfix(str);
+ if (nr && !str_to_float(nr, &val)) {
+ log_err("fio: random postfix parsing failed\n");
+ free(nr);
+ return 1;
+ }
+
+ free(nr);
+
+ if (td->o.random_distribution == FIO_RAND_DIST_ZIPF) {
+ if (val == 1.00) {
+ log_err("fio: zipf theta must different than 1.0\n");
+ return 1;
+ }
+ td->o.zipf_theta.u.f = val;
+ } else {
+ 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;
+ }
+
+ return 0;
+}
+
/*
* Return next file in the string. Files are separated with ':'. If the ':'
* is escaped with a '\', then that ':' is part of the filename and does not
struct thread_data *td = data;
struct stat sb;
+ if (parse_dryrun())
+ return 0;
+
if (lstat(td->o.directory, &sb) < 0) {
int ret = errno;
{
struct thread_data *td = data;
+ if (parse_dryrun())
+ return 0;
+
if (!td->files_index)
td->o.nr_files = 0;
{
struct thread_data *td = data;
long off;
- int i = 0, j = 0, len, k, base = 10;
+ int i = 0, j = 0, len, k, base = 10, pattern_length;
char *loc1, *loc2;
loc1 = strstr(input, "0x");
* 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.
*/
+ pattern_length = i;
while (i > 1 && i * 2 <= MAX_PATTERN_SIZE) {
memcpy(&td->o.verify_pattern[i], &td->o.verify_pattern[0], i);
i *= 2;
}
+
+ /*
+ * Fill remainder, if the pattern multiple ends up not being
+ * MAX_PATTERN_SIZE.
+ */
+ while (i > 1 && i < MAX_PATTERN_SIZE) {
+ unsigned int b = min(pattern_length, MAX_PATTERN_SIZE - i);
+
+ memcpy(&td->o.verify_pattern[i], &td->o.verify_pattern[0], b);
+ i += b;
+ }
+
if (i == 1) {
/*
* The code in verify_io_u_pattern assumes a single byte pattern
return 0;
}
-static int str_lockfile_cb(void *data, const char *str)
-{
- struct thread_data *td = data;
- char *nr = get_opt_postfix(str);
-
- td->o.lockfile_batch = 1;
- if (nr) {
- td->o.lockfile_batch = atoi(nr);
- free(nr);
- }
-
- return 0;
-}
-
static int str_gtod_reduce_cb(void *data, int *il)
{
struct thread_data *td = data;
return 0;
}
-static int kb_base_verify(struct fio_option *o, void *data)
-{
- struct thread_data *td = data;
-
- if (td->o.kb_base != 1024 && td->o.kb_base != 1000) {
- log_err("fio: kb_base set to nonsensical value: %u\n",
- td->o.kb_base);
- return 1;
- }
-
- return 0;
-}
-
/*
* Option grouping
*/
.category = FIO_OPT_C_FILE,
.group = FIO_OPT_G_FILENAME,
},
+ {
+ .name = "filename_format",
+ .type = FIO_OPT_STR_STORE,
+ .off1 = td_var_offset(filename_format),
+ .prio = -1, /* must come after "directory" */
+ .help = "Override default $jobname.$jobnum.$filenum naming",
+ .def = "$jobname.$jobnum.$filenum",
+ .category = FIO_OPT_C_FILE,
+ .group = FIO_OPT_G_FILENAME,
+ },
{
.name = "lockfile",
.lname = "Lockfile",
.type = FIO_OPT_STR,
- .cb = str_lockfile_cb,
.off1 = td_var_offset(file_lock_mode),
.help = "Lock file when doing IO to it",
.parent = "filename",
.oval = TD_DDIR_WRITE,
.help = "Sequential write",
},
+ { .ival = "trim",
+ .oval = TD_DDIR_TRIM,
+ .help = "Sequential trim",
+ },
{ .ival = "randread",
.oval = TD_DDIR_RANDREAD,
.help = "Random read",
.oval = TD_DDIR_RANDWRITE,
.help = "Random write",
},
+ { .ival = "randtrim",
+ .oval = TD_DDIR_RANDTRIM,
+ .help = "Random trim",
+ },
{ .ival = "rw",
.oval = TD_DDIR_RW,
.help = "Sequential read and write mix",
{ .ival = "vsync",
.help = "Use readv/writev",
},
-#ifdef FIO_HAVE_LIBAIO
+#ifdef CONFIG_PWRITEV
+ { .ival = "pvsync",
+ .help = "Use preadv/pwritev",
+ },
+#endif
+#ifdef CONFIG_LIBAIO
{ .ival = "libaio",
.help = "Linux native asynchronous IO",
},
#endif
-#ifdef FIO_HAVE_POSIXAIO
+#ifdef CONFIG_POSIXAIO
{ .ival = "posixaio",
.help = "POSIX asynchronous IO",
},
#endif
-#ifdef FIO_HAVE_SOLARISAIO
+#ifdef CONFIG_SOLARISAIO
{ .ival = "solarisaio",
.help = "Solaris native asynchronous IO",
},
#endif
-#ifdef FIO_HAVE_WINDOWSAIO
+#ifdef CONFIG_WINDOWSAIO
{ .ival = "windowsaio",
.help = "Windows native asynchronous IO"
},
{ .ival = "mmap",
.help = "Memory mapped IO"
},
-#ifdef FIO_HAVE_SPLICE
+#ifdef CONFIG_LINUX_SPLICE
{ .ival = "splice",
.help = "splice/vmsplice based IO",
},
{ .ival = "net",
.help = "Network IO",
},
-#ifdef FIO_HAVE_SYSLET
- { .ival = "syslet-rw",
- .help = "syslet enabled async pread/pwrite IO",
- },
-#endif
{ .ival = "cpuio",
.help = "CPU cycle burner engine",
},
-#ifdef FIO_HAVE_GUASI
+#ifdef CONFIG_GUASI
{ .ival = "guasi",
.help = "GUASI IO engine",
},
.help = "binject direct inject block engine",
},
#endif
-#ifdef FIO_HAVE_RDMA
+#ifdef CONFIG_RDMA
{ .ival = "rdma",
.help = "RDMA IO engine",
},
+#endif
+#ifdef CONFIG_FUSION_AW
+ { .ival = "fusion-aw-sync",
+ .help = "Fusion-io atomic write engine",
+ },
+#endif
+#ifdef CONFIG_LINUX_EXT4_MOVE_EXTENT
+ { .ival = "e4defrag",
+ .help = "ext4 defrag engine",
+ },
+#endif
+#ifdef CONFIG_LINUX_FALLOCATE
+ { .ival = "falloc",
+ .help = "fallocate() file based engine",
+ },
#endif
{ .ival = "external",
.help = "Load external engine (append name)",
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_INVALID,
},
+ {
+ .name = "number_ios",
+ .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",
+ .def = "0",
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_INVALID,
+ },
{
.name = "bs",
.lname = "Block size",
.type = FIO_OPT_INT,
.off1 = td_var_offset(bs[DDIR_READ]),
.off2 = td_var_offset(bs[DDIR_WRITE]),
+ .off3 = td_var_offset(bs[DDIR_TRIM]),
.minval = 1,
.help = "Block size unit",
.def = "4k",
.type = FIO_OPT_INT,
.off1 = td_var_offset(ba[DDIR_READ]),
.off2 = td_var_offset(ba[DDIR_WRITE]),
+ .off3 = td_var_offset(ba[DDIR_TRIM]),
.minval = 1,
.help = "IO block offset alignment",
.parent = "rw",
.off2 = td_var_offset(max_bs[DDIR_READ]),
.off3 = td_var_offset(min_bs[DDIR_WRITE]),
.off4 = td_var_offset(max_bs[DDIR_WRITE]),
+ .off5 = td_var_offset(min_bs[DDIR_TRIM]),
+ .off6 = td_var_offset(max_bs[DDIR_TRIM]),
.minval = 1,
.help = "Set block size range (in more detail than bs)",
.parent = "rw",
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_INVALID,
},
+ {
+ .name = "bs_is_seq_rand",
+ .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",
+ .def = "0",
+ .parent = "blocksize",
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_INVALID,
+ },
{
.name = "randrepeat",
.lname = "Random repeatable",
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_RANDOM,
},
+ {
+ .name = "random_generator",
+ .type = FIO_OPT_STR,
+ .off1 = td_var_offset(random_generator),
+ .help = "Type of random number generator to use",
+ .def = "tausworthe",
+ .posval = {
+ { .ival = "tausworthe",
+ .oval = FIO_RAND_GEN_TAUSWORTHE,
+ .help = "Strong Tausworthe generator",
+ },
+ { .ival = "lfsr",
+ .oval = FIO_RAND_GEN_LFSR,
+ .help = "Variable length LFSR",
+ },
+ },
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_RANDOM,
+ },
+ {
+ .name = "random_distribution",
+ .type = FIO_OPT_STR,
+ .off1 = td_var_offset(random_distribution),
+ .cb = str_random_distribution_cb,
+ .help = "Random offset distribution generator",
+ .def = "random",
+ .posval = {
+ { .ival = "random",
+ .oval = FIO_RAND_DIST_RANDOM,
+ .help = "Completely random",
+ },
+ { .ival = "zipf",
+ .oval = FIO_RAND_DIST_ZIPF,
+ .help = "Zipf distribution",
+ },
+ { .ival = "pareto",
+ .oval = FIO_RAND_DIST_PARETO,
+ .help = "Pareto distribution",
+ },
+ },
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_RANDOM,
+ },
+ {
+ .name = "percentage_random",
+ .lname = "Percentage Random",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(perc_rand[DDIR_READ]),
+ .off2 = td_var_offset(perc_rand[DDIR_WRITE]),
+ .off3 = td_var_offset(perc_rand[DDIR_TRIM]),
+ .maxval = 100,
+ .help = "Percentage of seq/random mix that should be random",
+ .def = "100,100,100",
+ .interval = 5,
+ .inverse = "percentage_sequential",
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_RANDOM,
+ },
+ {
+ .name = "percentage_sequential",
+ .lname = "Percentage Sequential",
+ .type = FIO_OPT_DEPRECATED,
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_RANDOM,
+ },
{
.name = "nrfiles",
.lname = "Number of files",
.parent = "nrfiles",
.hide = 1,
},
-#ifdef FIO_HAVE_FALLOCATE
+#ifdef CONFIG_POSIX_FALLOCATE
{
.name = "fallocate",
.lname = "Fallocate",
.oval = FIO_FALLOCATE_POSIX,
.help = "Use posix_fallocate()",
},
-#ifdef FIO_HAVE_LINUX_FALLOCATE
+#ifdef CONFIG_LINUX_FALLOCATE
{ .ival = "keep",
.oval = FIO_FALLOCATE_KEEP_SIZE,
.help = "Use fallocate(..., FALLOC_FL_KEEP_SIZE, ...)",
},
},
},
-#endif /* FIO_HAVE_FALLOCATE */
+#endif /* CONFIG_POSIX_FALLOCATE */
{
.name = "fadvise_hint",
.lname = "Fadvise hint",
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_INVALID,
},
-#ifdef FIO_HAVE_SYNC_FILE_RANGE
+#ifdef CONFIG_SYNC_FILE_RANGE
{
.name = "sync_file_range",
.lname = "Sync file range",
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_CLOCK,
.posval = {
+#ifdef CONFIG_GETTIMEOFDAY
{ .ival = "gettimeofday",
.oval = CS_GTOD,
.help = "Use gettimeofday(2) for timing",
},
+#endif
+#ifdef CONFIG_CLOCK_GETTIME
{ .ival = "clock_gettime",
.oval = CS_CGETTIME,
.help = "Use clock_gettime(2) for timing",
},
+#endif
#ifdef ARCH_HAVE_CPU_CLOCK
{ .ival = "cpu",
.oval = CS_CPUCLOCK,
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_VERIFY,
},
+ {
+ .name = "verifysort_nr",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(verifysort_nr),
+ .help = "Pre-load and sort verify blocks for a read workload",
+ .minval = 0,
+ .maxval = 131072,
+ .def = "1024",
+ .parent = "verify",
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_VERIFY,
+ },
{
.name = "verify_interval",
.lname = "Verify interval",
.group = FIO_OPT_G_VERIFY,
},
#endif
+ {
+ .name = "experimental_verify",
+ .off1 = td_var_offset(experimental_verify),
+ .type = FIO_OPT_BOOL,
+ .help = "Enable experimental verification",
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_VERIFY,
+ },
#ifdef FIO_HAVE_TRIM
{
.name = "trim_percentage",
.lname = "Lock memory",
.type = FIO_OPT_STR_VAL,
.off1 = td_var_offset(lockmem),
- .help = "Lock down this amount of memory",
+ .help = "Lock down this amount of memory (per worker)",
.def = "0",
.interval = 1024 * 1024,
.category = FIO_OPT_C_GENERAL,
.name = "rate",
.lname = "I/O rate",
.type = FIO_OPT_INT,
- .off1 = td_var_offset(rate[0]),
- .off2 = td_var_offset(rate[1]),
+ .off1 = td_var_offset(rate[DDIR_READ]),
+ .off2 = td_var_offset(rate[DDIR_WRITE]),
+ .off3 = td_var_offset(rate[DDIR_TRIM]),
.help = "Set bandwidth rate",
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_RATE,
.name = "ratemin",
.lname = "I/O min rate",
.type = FIO_OPT_INT,
- .off1 = td_var_offset(ratemin[0]),
- .off2 = td_var_offset(ratemin[1]),
+ .off1 = td_var_offset(ratemin[DDIR_READ]),
+ .off2 = td_var_offset(ratemin[DDIR_WRITE]),
+ .off3 = td_var_offset(ratemin[DDIR_TRIM]),
.help = "Job must meet this rate or it will be shutdown",
.parent = "rate",
.hide = 1,
.name = "rate_iops",
.lname = "I/O rate IOPS",
.type = FIO_OPT_INT,
- .off1 = td_var_offset(rate_iops[0]),
- .off2 = td_var_offset(rate_iops[1]),
+ .off1 = td_var_offset(rate_iops[DDIR_READ]),
+ .off2 = td_var_offset(rate_iops[DDIR_WRITE]),
+ .off3 = td_var_offset(rate_iops[DDIR_TRIM]),
.help = "Limit IO used to this number of IO operations/sec",
.hide = 1,
.category = FIO_OPT_C_IO,
.name = "rate_iops_min",
.lname = "I/O min rate IOPS",
.type = FIO_OPT_INT,
- .off1 = td_var_offset(rate_iops_min[0]),
- .off2 = td_var_offset(rate_iops_min[1]),
+ .off1 = td_var_offset(rate_iops_min[DDIR_READ]),
+ .off2 = td_var_offset(rate_iops_min[DDIR_WRITE]),
+ .off3 = td_var_offset(rate_iops_min[DDIR_TRIM]),
.help = "Job must meet this rate or it will be shut down",
.parent = "rate_iops",
.hide = 1,
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_RATE,
},
+ {
+ .name = "max_latency",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(max_latency),
+ .help = "Maximum tolerated IO latency (usec)",
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_RATE,
+ },
{
.name = "invalidate",
.lname = "Cache invalidate",
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_CRED,
},
+#endif
+#ifdef CONFIG_LIBNUMA
+ {
+ .name = "numa_cpu_nodes",
+ .type = FIO_OPT_STR,
+ .cb = str_numa_cpunodes_cb,
+ .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,
+ .help = "NUMA memory policy setup",
+ .category = FIO_OPT_C_GENERAL,
+ .group = FIO_OPT_G_INVALID,
+ },
#endif
{
.name = "end_fsync",
{
.name = "write_iops_log",
.lname = "Write IOPS log",
- .type = FIO_OPT_STR,
+ .type = FIO_OPT_STR_STORE,
.off1 = td_var_offset(iops_log_file),
.help = "Write log of IOPS during run",
.category = FIO_OPT_C_LOG,
{
.name = "group_reporting",
.lname = "Group reporting",
- .type = FIO_OPT_BOOL,
+ .type = FIO_OPT_STR_SET,
.off1 = td_var_offset(group_reporting),
.help = "Do reporting on a per-group basis",
- .def = "1",
.category = FIO_OPT_C_STAT,
.group = FIO_OPT_G_INVALID,
},
.lname = "Completion latency percentile list",
.type = FIO_OPT_FLOAT_LIST,
.off1 = td_var_offset(percentile_list),
- .off2 = td_var_offset(overwrite_plist),
+ .off2 = td_var_offset(percentile_precision),
.help = "Specify a custom list of percentiles to report",
+ .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,
.maxfp = 100.0,
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_CLOCK,
},
+ {
+ .name = "unified_rw_reporting",
+ .type = FIO_OPT_BOOL,
+ .off1 = td_var_offset(unified_rw_rep),
+ .help = "Unify reporting across data direction",
+ .def = "0",
+ .category = FIO_OPT_C_GENERAL,
+ .group = FIO_OPT_G_INVALID,
+ },
{
.name = "continue_on_error",
.lname = "Continue on error",
.help = "Continue on non-fatal errors during IO",
.def = "none",
.category = FIO_OPT_C_GENERAL,
- .group = FIO_OPT_G_INVALID,
+ .group = FIO_OPT_G_ERR,
.posval = {
{ .ival = "none",
.oval = ERROR_TYPE_NONE,
},
},
},
+ {
+ .name = "ignore_error",
+ .type = FIO_OPT_STR,
+ .cb = str_ignore_error_cb,
+ .help = "Set a specific list of errors to ignore",
+ .parent = "rw",
+ .category = FIO_OPT_C_GENERAL,
+ .group = FIO_OPT_G_ERR,
+ },
+ {
+ .name = "error_dump",
+ .type = FIO_OPT_BOOL,
+ .off1 = td_var_offset(error_dump),
+ .def = "0",
+ .help = "Dump info on each error",
+ .category = FIO_OPT_C_GENERAL,
+ .group = FIO_OPT_G_ERR,
+ },
{
.name = "profile",
.lname = "Profile",
.lname = "KB Base",
.type = FIO_OPT_INT,
.off1 = td_var_offset(kb_base),
- .verify = kb_base_verify,
.prio = 1,
.def = "1024",
+ .posval = {
+ { .ival = "1024",
+ .oval = 1024,
+ .help = "Use 1024 as the K base",
+ },
+ { .ival = "1000",
+ .oval = 1000,
+ .help = "Use 1000 as the K base",
+ },
+ },
.help = "How many bytes per KB for reporting (1000 or 1024)",
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_INVALID,
},
+ {
+ .name = "unit_base",
+ .lname = "Base unit for reporting (Bits or Bytes)",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(unit_base),
+ .prio = 1,
+ .posval = {
+ { .ival = "0",
+ .oval = 0,
+ .help = "Auto-detect",
+ },
+ { .ival = "8",
+ .oval = 8,
+ .help = "Normal (byte based)",
+ },
+ { .ival = "1",
+ .oval = 1,
+ .help = "Bit based",
+ },
+ },
+ .help = "Bit multiple of result summary data (8 for byte, 1 for bit)",
+ .category = FIO_OPT_C_GENERAL,
+ .group = FIO_OPT_G_INVALID,
+ },
{
.name = "hugepage-size",
.lname = "Hugepage size",
char buf[128];
long l;
- sprintf(buf, "%lu", page_size);
+ sprintf(buf, "%lu", (unsigned long) page_size);
fio_keywords[0].replace = strdup(buf);
mb_memory = os_phys_mem() / (1024 * 1024);
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);
+ return parse_cmd_option(opt, val, td->io_ops->options, td->eo);
}
void fio_fill_default_options(struct thread_data *td)