.has_arg = optional_argument,
.val = 'c' | FIO_CLIENT_FLAG,
},
+ {
+ .name = (char *) "enghelp",
+ .has_arg = optional_argument,
+ .val = 'i' | FIO_CLIENT_FLAG,
+ },
{
.name = (char *) "showcmd",
.has_arg = no_argument,
shm_id = shmget(0, size, IPC_CREAT | 0600);
if (shm_id != -1)
break;
- if (errno != EINVAL) {
+ if (errno != EINVAL && errno != ENOMEM) {
perror("shmget");
break;
}
/*
* Return a free job structure.
*/
-static struct thread_data *get_new_job(int global, struct thread_data *parent)
+static struct thread_data *get_new_job(int global, struct thread_data *parent,
+ int preserve_eo)
{
struct thread_data *td;
td = &threads[thread_number++];
*td = *parent;
+ td->io_ops = NULL;
+ if (!preserve_eo)
+ td->eo = NULL;
+
td->o.uid = td->o.gid = -1U;
dup_files(td, parent);
- options_mem_dupe(td);
+ fio_options_mem_dupe(td);
profile_add_hooks(td);
log_info("fio: %s\n", td->verror);
fio_options_free(td);
+ if (td->io_ops)
+ free_ioengine(td);
memset(&threads[td->thread_number - 1], 0, sizeof(*td));
thread_number--;
}
/*
- * only really works for sequential io for now, and with 1 file
+ * only really works with 1 file
*/
- if (o->zone_size && td_random(td) && o->open_files == 1)
+ if (o->zone_size && o->open_files == 1)
o->zone_size = 0;
+ /*
+ * If zone_range isn't specified, backward compatibility dictates it
+ * should be made equal to zone_size.
+ */
+ if (o->zone_size && !o->zone_range)
+ o->zone_range = o->zone_size;
+
/*
* Reads can do overwrites, we always need to pre-create the file
*/
return 0;
}
+/*
+ * Initializes the ioengine configured for a job, if it has not been done so
+ * already.
+ */
+int ioengine_load(struct thread_data *td)
+{
+ const char *engine;
+
+ /*
+ * Engine has already been loaded.
+ */
+ if (td->io_ops)
+ return 0;
+
+ engine = get_engine_name(td->o.ioengine);
+ td->io_ops = load_ioengine(td, engine);
+ if (!td->io_ops) {
+ log_err("fio: failed to load engine %s\n", engine);
+ return 1;
+ }
+
+ if (td->io_ops->option_struct_size && td->io_ops->options) {
+ /*
+ * In cases where td->eo is set, clone it for a child thread.
+ * This requires that the parent thread has the same ioengine,
+ * but that requirement must be enforced by the code which
+ * cloned the thread.
+ */
+ void *origeo = td->eo;
+ /*
+ * Otherwise use the default thread options.
+ */
+ if (!origeo && td != &def_thread && def_thread.eo &&
+ def_thread.io_ops->options == td->io_ops->options)
+ origeo = def_thread.eo;
+
+ options_init(td->io_ops->options);
+ td->eo = malloc(td->io_ops->option_struct_size);
+ /*
+ * Use the default thread as an option template if this uses the
+ * same options structure and there are non-default options
+ * used.
+ */
+ if (origeo) {
+ memcpy(td->eo, origeo, td->io_ops->option_struct_size);
+ options_mem_dupe(td->eo, td->io_ops->options);
+ } else {
+ memset(td->eo, 0, td->io_ops->option_struct_size);
+ fill_default_options(td->eo, td->io_ops->options);
+ }
+ *(struct thread_data **)td->eo = td;
+ }
+
+ return 0;
+}
+
/*
* Adds a job to the list of things todo. Sanitizes the various options
* to make sure we don't have conflicts, and initializes various
const char *ddir_str[] = { NULL, "read", "write", "rw", NULL,
"randread", "randwrite", "randrw" };
unsigned int i;
- const char *engine;
char fname[PATH_MAX];
int numjobs, file_alloced;
if (profile_td_init(td))
goto err;
- engine = get_engine_name(td->o.ioengine);
- td->io_ops = load_ioengine(td, engine);
- if (!td->io_ops) {
- log_err("fio: failed to load engine %s\n", engine);
+ if (ioengine_load(td))
goto err;
- }
if (td->o.use_thread)
nr_thread++;
if (fixup_options(td))
goto err;
+ /*
+ * IO engines only need this for option callbacks, and the address may
+ * change in subprocesses.
+ */
+ if (td->eo)
+ *(struct thread_data **)td->eo = NULL;
+
if (td->io_ops->flags & FIO_DISKLESSIO) {
struct fio_file *f;
goto err;
if (td->o.write_lat_log) {
- setup_log(&td->lat_log);
- setup_log(&td->slat_log);
- setup_log(&td->clat_log);
+ setup_log(&td->lat_log, td->o.log_avg_msec);
+ setup_log(&td->slat_log, td->o.log_avg_msec);
+ setup_log(&td->clat_log, td->o.log_avg_msec);
}
if (td->o.write_bw_log)
- setup_log(&td->bw_log);
+ setup_log(&td->bw_log, td->o.log_avg_msec);
if (td->o.write_iops_log)
- setup_log(&td->iops_log);
+ setup_log(&td->iops_log, td->o.log_avg_msec);
if (!td->o.name)
td->o.name = strdup(jobname);
*/
numjobs = td->o.numjobs;
while (--numjobs) {
- struct thread_data *td_new = get_new_job(0, td);
+ struct thread_data *td_new = get_new_job(0, td, 1);
if (!td_new)
goto err;
sprintf(jobname, "%s", o[i] + 5);
}
if (in_global && !td_parent)
- td_parent = get_new_job(1, &def_thread);
+ td_parent = get_new_job(1, &def_thread, 0);
else if (!in_global && !td) {
if (!td_parent)
td_parent = &def_thread;
- td = get_new_job(0, td_parent);
+ td = get_new_job(0, td_parent, 0);
}
if (in_global)
fio_options_parse(td_parent, (char **) &o[i], 1);
first_sect = 0;
}
- td = get_new_job(global, &def_thread);
+ td = get_new_job(global, &def_thread, 0);
if (!td) {
ret = 1;
break;
printf(" --help\t\tPrint this page\n");
printf(" --cmdhelp=cmd\t\tPrint command help, \"all\" for all of"
" them\n");
+ printf(" --enghelp=engine\tPrint ioengine help, or list"
+ " available ioengines\n");
+ printf(" --enghelp=engine,cmd\tPrint help for an ioengine"
+ " cmd\n");
printf(" --showcmd\t\tTurn a job file into command line options\n");
printf(" --eta=when\t\tWhen ETA estimate should be printed\n");
printf(" \t\tMay be \"always\", \"never\" or \"auto\"\n");
do_exit++;
}
break;
+ case 'i':
+ if (!cur_client) {
+ fio_show_ioengine_help(optarg);
+ do_exit++;
+ }
+ break;
case 's':
dump_cmdline = 1;
break;
if (is_section && skip_this_section(val))
continue;
- td = get_new_job(global, &def_thread);
- if (!td)
+ td = get_new_job(global, &def_thread, 1);
+ if (!td || ioengine_load(td))
return 0;
+ fio_options_set_ioengine_opts(l_opts, td);
}
ret = fio_cmd_option_parse(td, opt, val);
+
+ if (!ret && !strcmp(opt, "ioengine")) {
+ free_ioengine(td);
+ if (ioengine_load(td))
+ return 0;
+ fio_options_set_ioengine_opts(l_opts, td);
+ }
+ break;
+ }
+ case FIO_GETOPT_IOENGINE: {
+ const char *opt = l_opts[lidx].name;
+ char *val = optarg;
+ opt = l_opts[lidx].name;
+ val = optarg;
+ ret = fio_cmd_ioengine_option_parse(td, opt, val);
break;
}
case 'w':