} else
perc = -1;
- if (str_to_decimal(fname, &val, 1, o)) {
+ if (str_to_decimal(fname, &val, 1, o, 0)) {
log_err("fio: bssplit conversion failed\n");
free(bssplit);
return 1;
} else {
error[i] = atoi(fname);
if (error[i] < 0)
- error[i] = error[i];
+ error[i] = -error[i];
}
if (!error[i]) {
log_err("Unknown error %s, please use number value \n",
td->o.continue_on_error |= 1 << etype;
td->o.ignore_error_nr[etype] = i;
td->o.ignore_error[etype] = error;
- }
+ } else
+ free(error);
+
return 0;
}
{
struct thread_data *td = data;
struct thread_options *o = &td->o;
- char *nr = get_opt_postfix(str);
+ char *nr;
if (parse_dryrun())
return 0;
o->ddir_seq_nr = 1;
o->ddir_seq_add = 0;
+ nr = get_opt_postfix(str);
if (!nr)
return 0;
else {
long long val;
- if (str_to_decimal(nr, &val, 1, o)) {
+ if (str_to_decimal(nr, &val, 1, o, 0)) {
log_err("fio: rw postfix parsing failed\n");
free(nr);
return 1;
}
#ifdef FIO_HAVE_CPU_AFFINITY
+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();
+
+ cpus_in_mask = fio_cpu_count(mask);
+ cpu_index = cpu_index % cpus_in_mask;
+
+ index = 0;
+ for (i = 0; i < max_cpu; i++) {
+ if (!fio_cpu_isset(mask, i))
+ continue;
+
+ if (cpu_index != index)
+ fio_cpu_clear(mask, i);
+
+ index++;
+ }
+
+ return fio_cpu_count(mask);
+}
+
static int str_cpumask_cb(void *data, unsigned long long *val)
{
struct thread_data *td = data;
static int str_numa_cpunodes_cb(void *data, char *input)
{
struct thread_data *td = data;
+ struct bitmask *verify_bitmask;
if (parse_dryrun())
return 0;
* 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) {
+ verify_bitmask = numa_parse_nodestring(input);
+ if (verify_bitmask == NULL) {
log_err("fio: numa_parse_nodestring failed\n");
td_verror(td, 1, "str_numa_cpunodes_cb");
return 1;
}
+ numa_free_nodemask(verify_bitmask);
+ td->o.numa_cpunodes = strdup(input);
td->o.numa_cpumask_set = 1;
return 0;
}
{ "default", "prefer", "bind", "interleave", "local", NULL };
int i;
char *nodelist;
+ struct bitmask *verify_bitmask;
if (parse_dryrun())
return 0;
break;
case MPOL_INTERLEAVE:
case MPOL_BIND:
- td->o.numa_memnodesmask = numa_parse_nodestring(nodelist);
- if (td->o.numa_memnodesmask == NULL) {
+ verify_bitmask = numa_parse_nodestring(nodelist);
+ if (verify_bitmask == NULL) {
log_err("fio: numa_parse_nodestring failed\n");
td_verror(td, 1, "str_numa_memnodes_cb");
return 1;
}
+ td->o.numa_memnodes = strdup(nodelist);
+ numa_free_nodemask(verify_bitmask);
+
break;
case MPOL_LOCAL:
case MPOL_DEFAULT:
}
/*
- * Return next file in the string. Files are separated with ':'. If the ':'
+ * 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
* indicate a new file.
*/
-static char *get_next_file_name(char **ptr)
+static char *get_next_name(char **ptr)
{
char *str = *ptr;
char *p, *start;
return start;
}
+
+static int get_max_name_idx(char *input)
+{
+ unsigned int cur_idx;
+ char *str, *p;
+
+ p = str = strdup(input);
+ for (cur_idx = 0; ; cur_idx++)
+ if (get_next_name(&str) == NULL)
+ break;
+
+ free(p);
+ return cur_idx;
+}
+
+/*
+ * Returns the directory at the index, indexes > entires will be
+ * assigned via modulo division of the index
+ */
+int set_name_idx(char *target, char *input, int index)
+{
+ unsigned int cur_idx;
+ int len;
+ char *fname, *str, *p;
+
+ p = str = strdup(input);
+
+ index %= get_max_name_idx(input);
+ for (cur_idx = 0; cur_idx <= index; cur_idx++)
+ fname = get_next_name(&str);
+
+ len = sprintf(target, "%s/", fname);
+ free(p);
+
+ return len;
+}
+
static int str_filename_cb(void *data, const char *input)
{
struct thread_data *td = data;
if (!td->files_index)
td->o.nr_files = 0;
- while ((fname = get_next_file_name(&str)) != NULL) {
+ while ((fname = get_next_name(&str)) != NULL) {
if (!strlen(fname))
break;
- add_file(td, fname);
- td->o.nr_files++;
+ add_file(td, fname, 0, 1);
}
free(p);
return 0;
}
-static int str_directory_cb(void *data, const char fio_unused *str)
+static int str_directory_cb(void *data, const char fio_unused *unused)
{
struct thread_data *td = data;
struct stat sb;
+ char *dirname, *str, *p;
+ int ret = 0;
if (parse_dryrun())
return 0;
- if (lstat(td->o.directory, &sb) < 0) {
- int ret = errno;
+ p = str = strdup(td->o.directory);
+ while ((dirname = get_next_name(&str)) != NULL) {
+ if (lstat(dirname, &sb) < 0) {
+ ret = errno;
- log_err("fio: %s is not a directory\n", td->o.directory);
- td_verror(td, ret, "lstat");
- return 1;
- }
- if (!S_ISDIR(sb.st_mode)) {
- log_err("fio: %s is not a directory\n", td->o.directory);
- return 1;
+ log_err("fio: %s is not a directory\n", dirname);
+ td_verror(td, ret, "lstat");
+ goto out;
+ }
+ if (!S_ISDIR(sb.st_mode)) {
+ log_err("fio: %s is not a directory\n", dirname);
+ ret = 1;
+ goto out;
+ }
}
- return 0;
+out:
+ free(p);
+ return ret;
}
static int str_lockfile_cb(void *data, const char fio_unused *str)
return ret;
}
+static int str_buffer_compress_cb(void *data, unsigned long long *il)
+{
+ struct thread_data *td = data;
+
+ td->flags |= TD_F_COMPRESS;
+ td->o.compress_percentage = *il;
+ return 0;
+}
+
static int str_verify_pattern_cb(void *data, const char *input)
{
struct thread_data *td = data;
.help = "fallocate() file based engine",
},
#endif
+#ifdef CONFIG_GFAPI
+ { .ival = "gfapi",
+ .help = "Glusterfs libgfapi(sync) based engine"
+ },
+ { .ival = "gfapi_async",
+ .help = "Glusterfs libgfapi(async) based engine"
+ },
+#endif
+
{ .ival = "external",
.help = "Load external engine (append name)",
},
.category = FIO_OPT_C_IO,
.group = FIO_OPT_G_INVALID,
},
+ {
+ .name = "io_limit",
+ .lname = "IO Limit",
+ .type = FIO_OPT_STR_VAL,
+ .off1 = td_var_offset(io_limit),
+ .interval = 1024 * 1024,
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_INVALID,
+ },
{
.name = "fill_device",
.lname = "Fill device",
.category = FIO_OPT_C_FILE,
.group = FIO_OPT_G_INVALID,
},
+ {
+ .name = "file_append",
+ .lname = "File append",
+ .type = FIO_OPT_BOOL,
+ .off1 = td_var_offset(file_append),
+ .help = "IO will start at the end of the file(s)",
+ .def = "0",
+ .category = FIO_OPT_C_FILE,
+ .group = FIO_OPT_G_INVALID,
+ },
{
.name = "offset",
.lname = "IO offset",
.off1 = td_var_offset(allrand_repeatable),
.help = "Use repeatable random numbers for everything",
.def = "0",
+ .category = FIO_OPT_C_IO,
+ .group = FIO_OPT_G_RANDOM,
},
{
.name = "nrfiles",
.lname = "Start delay",
.type = FIO_OPT_STR_VAL_TIME,
.off1 = td_var_offset(start_delay),
+ .off2 = td_var_offset(start_delay_high),
.help = "Only start job when this period has passed",
.def = "0",
+ .is_seconds = 1,
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_RUNTIME,
},
.off1 = td_var_offset(timeout),
.help = "Stop workload when this amount of time has passed",
.def = "0",
+ .is_seconds = 1,
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_RUNTIME,
},
.type = FIO_OPT_STR_VAL_TIME,
.off1 = td_var_offset(ramp_time),
.help = "Ramp up time before measuring performance",
+ .is_seconds = 1,
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_RUNTIME,
},
.oval = MEM_MALLOC,
.help = "Use malloc(3) for IO buffers",
},
+#ifndef CONFIG_NO_SHM
{ .ival = "shm",
.oval = MEM_SHM,
.help = "Use shared memory segments for IO buffers",
.oval = MEM_SHMHUGE,
.help = "Like shm, but use huge pages",
},
+#endif
#endif
{ .ival = "mmap",
.oval = MEM_MMAP,
.oval = VERIFY_SHA512,
.help = "Use sha512 checksums for verification",
},
+ { .ival = "xxhash",
+ .oval = VERIFY_XXHASH,
+ .help = "Use xxhash checksums for verification",
+ },
{ .ival = "meta",
.oval = VERIFY_META,
.help = "Use io information",
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_CRED,
},
+ {
+ .name = "cpus_allowed_policy",
+ .lname = "CPUs allowed distribution policy",
+ .type = FIO_OPT_STR,
+ .off1 = td_var_offset(cpus_allowed_policy),
+ .help = "Distribution policy for cpus_allowed",
+ .parent = "cpus_allowed",
+ .prio = 1,
+ .posval = {
+ { .ival = "shared",
+ .oval = FIO_CPUS_SHARED,
+ .help = "Mask shared between threads",
+ },
+ { .ival = "split",
+ .oval = FIO_CPUS_SPLIT,
+ .help = "Mask split between threads",
+ },
+ },
+ .category = FIO_OPT_C_GENERAL,
+ .group = FIO_OPT_G_CRED,
+ },
#endif
#ifdef CONFIG_LIBNUMA
{
.type = FIO_OPT_STR_SET,
.off1 = td_var_offset(use_thread),
.help = "Use threads instead of processes",
+#ifdef CONFIG_NO_SHM
+ .def = "1",
+ .no_warn_def = 1,
+#endif
.category = FIO_OPT_C_GENERAL,
.group = FIO_OPT_G_PROCESS,
},
.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,
+ },
{
.name = "bwavgtime",
.lname = "Bandwidth average time",
.name = "buffer_compress_percentage",
.lname = "Buffer compression percentage",
.type = FIO_OPT_INT,
- .off1 = td_var_offset(compress_percentage),
+ .cb = str_buffer_compress_cb,
.maxval = 100,
- .minval = 1,
+ .minval = 0,
.help = "How compressible the buffer is (approximately)",
.interval = 5,
.category = FIO_OPT_C_IO,
return NULL;
ret = fread(&buf[tmp - str], 1, 128 - (tmp - str), f);
- if (ret <= 0)
+ if (ret <= 0) {
+ pclose(f);
return NULL;
+ }
pclose(f);
buf[(tmp - str) + ret - 1] = '\0';
void fio_fill_default_options(struct thread_data *td)
{
+ td->o.magic = OPT_MAGIC;
fill_default_options(td, fio_options);
}
struct thread_options *o = data;
unsigned int kb_base = 0;
- if (o)
+ /*
+ * This is a hack... For private options, *data is not holding
+ * a pointer to the thread_options, but to private data. This means
+ * we can't safely dereference it, but magic is first so mem wise
+ * it is valid. But this also means that if the job first sets
+ * kb_base and expects that to be honored by private options,
+ * it will be disappointed. We will return the global default
+ * for this.
+ */
+ if (o && o->magic == OPT_MAGIC)
kb_base = o->kb_base;
if (!kb_base)
kb_base = 1024;