#define DEF_WRITE_BW_LOG (0)
#define DEF_WRITE_LAT_LOG (0)
+#define td_var_offset(var) ((size_t) &((struct thread_data *)0)->var)
+
+static int str_rw_cb(void *, char *);
+static int str_ioengine_cb(void *, char *);
+static int str_mem_cb(void *, char *);
+static int str_verify_cb(void *, char *);
+static int str_lockmem_cb(void *, unsigned long *);
+static int str_prio_cb(void *, unsigned int *);
+static int str_prioclass_cb(void *, unsigned int *);
+static int str_exitall_cb(void);
+static int str_cpumask_cb(void *, unsigned int *);
+
+/*
+ * Map of job/command line options
+ */
+static struct fio_option options[] = {
+ {
+ .name = "name",
+ .type = FIO_OPT_STR_STORE,
+ .off1 = td_var_offset(name),
+ },
+ {
+ .name = "directory",
+ .type = FIO_OPT_STR_STORE,
+ .off1 = td_var_offset(directory),
+ },
+ {
+ .name = "filename",
+ .type = FIO_OPT_STR_STORE,
+ .off1 = td_var_offset(filename),
+ },
+ {
+ .name = "rw",
+ .type = FIO_OPT_STR,
+ .cb = str_rw_cb,
+ },
+ {
+ .name = "ioengine",
+ .type = FIO_OPT_STR,
+ .cb = str_ioengine_cb,
+ },
+ {
+ .name = "mem",
+ .type = FIO_OPT_STR,
+ .cb = str_mem_cb,
+ },
+ {
+ .name = "verify",
+ .type = FIO_OPT_STR,
+ .cb = str_verify_cb,
+ },
+ {
+ .name = "write_iolog",
+ .type = FIO_OPT_STR_STORE,
+ .off1 = td_var_offset(write_iolog),
+ },
+ {
+ .name = "iolog",
+ .type = FIO_OPT_STR_STORE,
+ .off1 = td_var_offset(iolog),
+ },
+ {
+ .name = "exec_prerun",
+ .type = FIO_OPT_STR_STORE,
+ .off1 = td_var_offset(exec_prerun),
+ },
+ {
+ .name = "exec_postrun",
+ .type = FIO_OPT_STR_STORE,
+ .off1 = td_var_offset(exec_postrun),
+ },
+#ifdef FIO_HAVE_IOSCHED_SWITCH
+ {
+ .name = "ioscheduler",
+ .type = FIO_OPT_STR_STORE,
+ .off1 = td_var_offset(ioscheduler),
+ },
+#endif
+ {
+ .name = "size",
+ .type = FIO_OPT_STR_VAL,
+ .off1 = td_var_offset(total_file_size),
+ },
+ {
+ .name = "bs",
+ .type = FIO_OPT_STR_VAL,
+ .off1 = td_var_offset(bs),
+ },
+ {
+ .name = "offset",
+ .type = FIO_OPT_STR_VAL,
+ .off1 = td_var_offset(start_offset),
+ },
+ {
+ .name = "zonesize",
+ .type = FIO_OPT_STR_VAL,
+ .off1 = td_var_offset(zone_size),
+ },
+ {
+ .name = "zoneskip",
+ .type = FIO_OPT_STR_VAL,
+ .off1 = td_var_offset(zone_skip),
+ },
+ {
+ .name = "lockmem",
+ .type = FIO_OPT_STR_VAL,
+ .cb = str_lockmem_cb,
+ },
+ {
+ .name = "bsrange",
+ .type = FIO_OPT_RANGE,
+ .off1 = td_var_offset(min_bs),
+ .off2 = td_var_offset(max_bs),
+ },
+ {
+ .name = "nrfiles",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(nr_files),
+ },
+ {
+ .name = "iodepth",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(iodepth),
+ },
+ {
+ .name = "fsync",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(fsync_blocks),
+ },
+ {
+ .name = "rwmixcycle",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(rwmixcycle),
+ },
+ {
+ .name = "rwmixread",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(rwmixread),
+ .max_val= 100,
+ },
+ {
+ .name = "rwmixwrite",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(rwmixwrite),
+ .max_val= 100,
+ },
+ {
+ .name = "nice",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(nice),
+ },
+#ifdef FIO_HAVE_IOPRIO
+ {
+ .name = "prio",
+ .type = FIO_OPT_INT,
+ .cb = str_prio_cb,
+ },
+ {
+ .name = "prioclass",
+ .type = FIO_OPT_INT,
+ .cb = str_prioclass_cb,
+ },
+#endif
+ {
+ .name = "thinktime",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(thinktime)
+ },
+ {
+ .name = "rate",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(rate)
+ },
+ {
+ .name = "ratemin",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(ratemin)
+ },
+ {
+ .name = "ratecycle",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(ratecycle)
+ },
+ {
+ .name = "startdelay",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(start_delay)
+ },
+ {
+ .name = "timeout",
+ .type = FIO_OPT_STR_VAL_TIME,
+ .off1 = td_var_offset(timeout)
+ },
+ {
+ .name = "invalidate",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(invalidate_cache)
+ },
+ {
+ .name = "sync",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(sync_io)
+ },
+ {
+ .name = "bwavgtime",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(bw_avg_time)
+ },
+ {
+ .name = "create_serialize",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(create_serialize)
+ },
+ {
+ .name = "create_fsync",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(create_fsync)
+ },
+ {
+ .name = "loops",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(loops)
+ },
+ {
+ .name = "numjobs",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(numjobs)
+ },
+ {
+ .name = "cpuload",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(cpuload)
+ },
+ {
+ .name = "cpuchunks",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(cpucycle)
+ },
+ {
+ .name = "direct",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(odirect)
+ },
+ {
+ .name = "overwrite",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(overwrite)
+ },
+#ifdef FIO_HAVE_CPU_AFFINITY
+ {
+ .name = "cpumask",
+ .type = FIO_OPT_INT,
+ .cb = str_cpumask_cb,
+ },
+#endif
+ {
+ .name = "end_fsync",
+ .type = FIO_OPT_INT,
+ .off1 = td_var_offset(end_fsync)
+ },
+ {
+ .name = "unlink",
+ .type = FIO_OPT_STR_SET,
+ .off1 = td_var_offset(unlink),
+ },
+ {
+ .name = "exitall",
+ .type = FIO_OPT_STR_SET,
+ .cb = str_exitall_cb,
+ },
+ {
+ .name = "stonewall",
+ .type = FIO_OPT_STR_SET,
+ .off1 = td_var_offset(stonewall),
+ },
+ {
+ .name = "thread",
+ .type = FIO_OPT_STR_SET,
+ .off1 = td_var_offset(thread),
+ },
+ {
+ .name = "write_bw_log",
+ .type = FIO_OPT_STR_SET,
+ .off1 = td_var_offset(write_bw_log),
+ },
+ {
+ .name = "write_lat_log",
+ .type = FIO_OPT_STR_SET,
+ .off1 = td_var_offset(write_lat_log),
+ },
+ {
+ .name = NULL,
+ },
+};
+
static int def_timeout = DEF_TIMEOUT;
static char fio_version_string[] = "fio 1.5";
thread_number--;
}
+static void fixup_options(struct thread_data *td)
+{
+ if (!td->min_bs)
+ td->min_bs = td->bs;
+ if (!td->max_bs)
+ td->max_bs = td->bs;
+
+ if (!td->rwmixread && td->rwmixwrite)
+ td->rwmixread = 100 - td->rwmixwrite;
+}
+
/*
* Adds a job to the list of things todo. Sanitizes the various options
* to make sure we don't have conflicts, and initializes various
}
#endif
+ fixup_options(td);
+
/*
* the def_thread is just for options, it's not a real job
*/
return 1;
}
+static int str_lockmem_cb(void fio_unused *data, unsigned long *val)
+{
+ mlock_size = *val;
+ return 0;
+}
+
+static int str_prioclass_cb(void *data, unsigned int *val)
+{
+ struct thread_data *td = data;
+
+ td->ioprio |= *val << IOPRIO_CLASS_SHIFT;
+ return 0;
+}
+
+static int str_prio_cb(void *data, unsigned int *val)
+{
+ struct thread_data *td = data;
+
+ td->ioprio |= *val;
+ return 0;
+}
+
+static int str_exitall_cb(void)
+{
+ exitall_on_terminate = 1;
+ return 0;
+}
+
+static int str_cpumask_cb(void *data, unsigned int *val)
+{
+ struct thread_data *td = data;
+
+ fill_cpu_mask(td->cpumask, *val);
+ return 0;
+}
+
/*
* This is our [ini] type file parser.
*/
int parse_jobs_ini(char *file, int stonewall_flag)
{
- unsigned int prioclass, prio, cpu, global, il;
- unsigned long long ull;
- unsigned long ul1, ul2;
+ unsigned int global;
struct thread_data *td;
char *string, *name, *tmpbuf;
fpos_t off;
continue;
if (strstr(p, "["))
break;
+
strip_blank_front(&p);
strip_blank_end(p);
- if (!check_int(p, "prio", &prio)) {
-#ifndef FIO_HAVE_IOPRIO
- log_err("io priorities not available\n");
- ret = 1;
- break;
-#endif
- td->ioprio |= prio;
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "prioclass", &prioclass)) {
-#ifndef FIO_HAVE_IOPRIO
- log_err("io priorities not available\n");
- ret = 1;
- break;
-#else
- td->ioprio |= prioclass << IOPRIO_CLASS_SHIFT;
- fgetpos(f, &off);
- continue;
-#endif
- }
- if (!check_int(p, "direct", &il)) {
- td->odirect = il;
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "rand_repeatable", &il)) {
- td->rand_repeatable = il;
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "rate", &td->rate)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "ratemin", &td->ratemin)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "ratecycle", &td->ratecycle)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "cpuload", &td->cpuload)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "cpuchunks", &td->cpucycle)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "thinktime", &td->thinktime)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "cpumask", &cpu)) {
-#ifndef FIO_HAVE_CPU_AFFINITY
- log_err("cpu affinity not available\n");
- ret = 1;
- break;
-#endif
- fill_cpu_mask(td->cpumask, cpu);
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "fsync", &td->fsync_blocks)) {
- fgetpos(f, &off);
- td->end_fsync = 1;
- continue;
- }
- if (!check_int(p, "startdelay", &td->start_delay)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_str_time(p, "timeout", &ull)) {
- td->timeout = ull;
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "invalidate", &il)) {
- td->invalidate_cache = il;
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "iodepth", &td->iodepth)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "sync", &il)) {
- td->sync_io = il;
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "bwavgtime", &td->bw_avg_time)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "create_serialize", &il)) {
- td->create_serialize = il;
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "create_fsync", &il)) {
- td->create_fsync = il;
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "end_fsync", &il)) {
- td->end_fsync = il;
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "loops", &td->loops)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "numjobs", &td->numjobs)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "overwrite", &il)) {
- td->overwrite = il;
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "rwmixcycle", &td->rwmixcycle)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "rwmixread", &il)) {
- if (il > 100)
- il = 100;
- td->rwmixread = il;
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "rwmixwrite", &il)) {
- if (il > 100)
- il = 100;
- td->rwmixread = 100 - il;
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "nice", &td->nice)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_int(p, "nrfiles", &td->nr_files)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_range_bytes(p, "bsrange", &ul1, &ul2)) {
- if (ul1 > ul2) {
- td->max_bs = ul1;
- td->min_bs = ul2;
- } else {
- td->max_bs = ul2;
- td->min_bs = ul1;
- }
- fgetpos(f, &off);
- continue;
- }
- if (!check_str_bytes(p, "bs", &ull)) {
- td->bs = ull;
- fgetpos(f, &off);
- continue;
- }
- if (!check_str_bytes(p, "size", &td->total_file_size)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_str_bytes(p, "offset", &td->start_offset)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_str_bytes(p, "zonesize", &td->zone_size)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_str_bytes(p, "zoneskip", &td->zone_skip)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_str_bytes(p, "lockmem", &mlock_size)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_strstore(p, "directory", tmpbuf)) {
- td->directory = strdup(tmpbuf);
- fgetpos(f, &off);
- continue;
- }
- if (!check_strstore(p, "filename", tmpbuf)) {
- td->filename = strdup(tmpbuf);
- fgetpos(f, &off);
- continue;
- }
- if (!check_strstore(p, "name", tmpbuf)) {
- snprintf(td->name, sizeof(td->name)-1, "%s%d", tmpbuf, td->thread_number);
- fgetpos(f, &off);
- continue;
- }
- if (!check_str(p, "mem", str_mem_cb, td)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_str(p, "verify", str_verify_cb, td)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_str(p, "rw", str_rw_cb, td)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_str(p, "ioengine", str_ioengine_cb, td)) {
- fgetpos(f, &off);
- continue;
- }
- if (!check_strset(p, "exitall")) {
- exitall_on_terminate = 1;
- fgetpos(f, &off);
- continue;
- }
- if (!check_strset(p, "stonewall")) {
- td->stonewall = 1;
- fgetpos(f, &off);
- continue;
- }
- if (!check_strset(p, "thread")) {
- td->use_thread = 1;
- fgetpos(f, &off);
- continue;
- }
- if (!check_strset(p, "unlink")) {
- td->unlink = 1;
- fgetpos(f, &off);
- continue;
- }
- if (!check_strset(p, "write_bw_log")) {
- td->write_bw_log = 1;
- fgetpos(f, &off);
- continue;
- }
- if (!check_strset(p, "write_lat_log")) {
- td->write_lat_log = 1;
- fgetpos(f, &off);
- continue;
- }
- if (!check_strstore(p, "iolog", tmpbuf)) {
- if (td->write_iolog) {
- log_err("fio: read iolog overrides given write_iolog\n");
- free(td->iolog_file);
- td->write_iolog = 0;
- }
- td->iolog_file = strdup(tmpbuf);
- td->read_iolog = 1;
- fgetpos(f, &off);
- continue;
- }
- if (!check_strstore(p, "write_iolog", tmpbuf)) {
- if (!td->read_iolog) {
- td->iolog_file = strdup(tmpbuf);
- td->write_iolog = 1;
- } else
- log_err("fio: read iolog overrides given write_iolog\n");
- fgetpos(f, &off);
- continue;
- }
- if (!check_strstore(p, "exec_prerun", tmpbuf)) {
- td->exec_prerun = strdup(tmpbuf);
- fgetpos(f, &off);
- continue;
- }
- if (!check_strstore(p, "exec_postrun", tmpbuf)) {
- td->exec_postrun = strdup(tmpbuf);
- fgetpos(f, &off);
- continue;
- }
- if (!check_strstore(p, "ioscheduler", tmpbuf)) {
-#ifndef FIO_HAVE_IOSCHED_SWITCH
- log_err("io scheduler switching not available\n");
- ret = 1;
- break;
-#else
- td->ioscheduler = strdup(tmpbuf);
- fgetpos(f, &off);
- continue;
-#endif
- }
+ fgetpos(f, &off);
/*
* Don't break here, continue parsing options so we
* dump all the bad ones. Makes trial/error fixups
* easier on the user.
*/
- printf("Client%d: bad option %s\n",td->thread_number,p);
- ret = 1;
+ ret = parse_option(p, options, td);
}
if (!ret) {
}
/*
- * convert string after '=' into decimal value, noting any size suffix
+ * convert string into decimal value, noting any size suffix
*/
static int str_to_decimal(char *p, unsigned long long *val, int kilo)
{
- char *str;
+ char *str = p;
int len;
- str = strchr(p, '=');
- if (!str)
- return 1;
-
- str++;
len = strlen(str);
*val = strtoul(str, NULL, 10);
return 0;
}
-int check_str_bytes(char *p, char *name, unsigned long long *val)
+static int check_str_bytes(char *p, unsigned long long *val)
{
- if (strncmp(p, name, strlen(name) - 1))
- return 1;
-
return str_to_decimal(p, val, 1);
}
-int check_str_time(char *p, char *name, unsigned long long *val)
+static int check_str_time(char *p, unsigned long long *val)
{
- if (strncmp(p, name, strlen(name) - 1))
- return 1;
-
return str_to_decimal(p, val, 0);
}
*(s + 1) = '\0';
}
-int check_str(char *p, char *name, str_cb_fn *cb, void *data)
-{
- char *s;
-
- if (strncmp(p, name, strlen(name)))
- return 1;
-
- s = strstr(p, name);
- if (!s)
- return 1;
-
- s = strchr(s, '=');
- if (!s)
- return 1;
-
- s++;
- strip_blank_front(&s);
- return cb(data, s);
-}
-
-int check_strstore(char *p, char *name, char *dest)
-{
- char *s;
-
- if (strncmp(p, name, strlen(name)))
- return 1;
-
- s = strstr(p, name);
- if (!s)
- return 1;
-
- s = strchr(p, '=');
- if (!s)
- return 1;
-
- s++;
- strip_blank_front(&s);
-
- strcpy(dest, s);
- return 0;
-}
-
-static int __check_range_bytes(char *str, unsigned long *val)
+static int check_range_bytes(char *str, unsigned long *val)
{
char suffix;
return 1;
}
-int check_range_bytes(char *p, char *name, unsigned long *s, unsigned long *e)
+int check_int(char *p, unsigned int *val)
{
- char option[128];
- char *str, *p1, *p2;
-
- if (strncmp(p, name, strlen(name)))
- return 1;
-
- strcpy(option, p);
- p = option;
-
- str = strstr(p, name);
- if (!str)
- return 1;
+ if (sscanf(p, "%u", val) == 1)
+ return 0;
- p += strlen(name);
+ return 1;
+}
- str = strchr(p, '=');
- if (!str)
- return 1;
+int check_strset(char *p, char *name)
+{
+ return strncmp(p, name, strlen(name));
+}
- /*
- * 'p' now holds whatever is after the '=' sign
- */
- p1 = str + 1;
+static struct fio_option *find_option(struct fio_option *options,
+ const char *opt)
+{
+ struct fio_option *o;
+ int i = 0;
- /*
- * terminate p1 at the '-' sign
- */
- p = strchr(p1, '-');
- if (!p)
- return 1;
+ do {
+ o = &options[i];
+ if (!o->name)
+ break;
- p2 = p + 1;
- *p = '\0';
+ if (!strcmp(o->name, opt))
+ return o;
- if (!__check_range_bytes(p1, s) && !__check_range_bytes(p2, e))
- return 0;
+ i++;
+ } while (1);
- return 1;
+ return NULL;
}
-int check_int(char *p, char *name, unsigned int *val)
+static int handle_option(struct fio_option *o, char *ptr, void *data)
{
- char *str;
-
- if (strncmp(p, name, strlen(name)))
- return 1;
-
- str = strstr(p, name);
- if (!str)
- return 1;
+ unsigned int il, *ilp;
+ unsigned long long ull, *ullp;
+ unsigned long ul1, ul2, *ulp1, *ulp2;
+ char *tmpbuf, **cp;
+ int ret = 0, is_time = 0;
- str = strchr(p, '=');
- if (!str)
- return 1;
+ tmpbuf = malloc(4096);
- str++;
+ switch (o->type) {
+ case FIO_OPT_STR: {
+ fio_opt_str_fn *fn = o->cb;
- if (sscanf(str, "%u", val) == 1)
- return 0;
+ ret = fn(data, ptr);
+ break;
+ }
+ case FIO_OPT_STR_VAL_TIME:
+ is_time = 1;
+ case FIO_OPT_STR_VAL: {
+ fio_opt_str_val_fn *fn = o->cb;
+
+ if (is_time)
+ ret = check_str_time(ptr, &ull);
+ else
+ ret = check_str_bytes(ptr, &ull);
+
+ if (ret)
+ break;
+
+ if (o->max_val && ull > o->max_val)
+ ull = o->max_val;
+
+ if (fn)
+ ret = fn(data, &ull);
+ else {
+ ullp = td_var(data, o->off1);
+ *ullp = ull;
+ }
+ break;
+ }
+ case FIO_OPT_STR_STORE:
+ cp = td_var(data, o->off1);
+ *cp = strdup(ptr);
+ break;
+ case FIO_OPT_RANGE: {
+ char *p1, *p2;
+
+ p1 = strchr(ptr, '-');
+ if (!p1) {
+ ret = 1;
+ break;
+ }
+
+ p2 = p1 + 1;
+ *p1 = '\0';
+
+ ret = 1;
+ if (!check_range_bytes(p1, &ul1) && !check_range_bytes(p2, &ul2)) {
+ ret = 0;
+ ulp1 = td_var(data, o->off1);
+ ulp2 = td_var(data, o->off2);
+ if (ul1 > ul2) {
+ *ulp1 = ul2;
+ *ulp2 = ul1;
+ } else {
+ *ulp2 = ul2;
+ *ulp1 = ul1;
+ }
+ }
+
+ break;
+ }
+ case FIO_OPT_INT: {
+ fio_opt_int_fn *fn = o->cb;
+
+ ret = check_int(ptr, &il);
+ if (ret)
+ break;
+
+ if (o->max_val && il > o->max_val)
+ il = o->max_val;
+
+ if (fn)
+ ret = fn(data, &il);
+ else {
+ ilp = td_var(data, o->off1);
+ *ilp = il;
+ }
+ break;
+ }
+ case FIO_OPT_STR_SET: {
+ fio_opt_str_set_fn *fn = o->cb;
+
+ if (fn)
+ ret = fn(data);
+ else {
+ ilp = td_var(data, o->off1);
+ *ilp = 1;
+ }
+ break;
+ }
+ default:
+ fprintf(stderr, "Bad option type %d\n", o->type);
+ ret = 1;
+ }
- return 1;
+ free(tmpbuf);
+ return ret;
}
-int check_strset(char *p, char *name)
+int parse_option(const char *opt, struct fio_option *options, void *data)
{
- return strncmp(p, name, strlen(name));
-}
+ struct fio_option *o = find_option(options, opt);
+ char *pre, *post;
+ char tmp[64];
+
+ strcpy(tmp, opt);
+
+ pre = strchr(tmp, '=');
+ if (pre) {
+ post = pre;
+ *pre = '\0';
+ pre = tmp;
+ post++;
+ o = find_option(options, pre);
+ } else {
+ o = find_option(options, tmp);
+ post = NULL;
+ }
+ if (!o) {
+ fprintf(stderr, "Bad option %s\n", tmp);
+ return 1;
+ }
+
+ return handle_option(o, post, data);
+}