X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=options.c;h=713112f6de5d6cc7c5a8b59e3f151e49db740fee;hp=062abb4e163251b540b3dddcbdf91bcba1d2302c;hb=53b5693d04b0de440ce3eb1fa22834ef46d0a4e3;hpb=4205998f8be13752f9857ef6e28b6c7f86a3c769 diff --git a/options.c b/options.c index 062abb4e..713112f6 100644 --- a/options.c +++ b/options.c @@ -20,7 +20,9 @@ char client_sockaddr_str[INET6_ADDRSTRLEN] = { 0 }; -struct pattern_fmt_desc fmt_desc[] = { +#define cb_data_to_td(data) container_of(data, struct thread_data, o) + +static struct pattern_fmt_desc fmt_desc[] = { { .fmt = "%o", .len = FIELD_SIZE(struct io_u *, offset), @@ -223,7 +225,7 @@ static int str_split_parse(struct thread_data *td, char *str, split_parse_fn *fn static int str_bssplit_cb(void *data, const char *input) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); char *str, *p; int ret = 0; @@ -258,7 +260,7 @@ static int str2error(char *str) "EINVAL", "ENFILE", "EMFILE", "ENOTTY", "ETXTBSY","EFBIG", "ENOSPC", "ESPIPE", "EROFS","EMLINK", "EPIPE", "EDOM", "ERANGE" }; - int i = 0, num = sizeof(err) / sizeof(void *); + int i = 0, num = sizeof(err) / sizeof(char *); while (i < num) { if (!strcmp(err[i], str)) @@ -324,7 +326,7 @@ static int ignore_error_type(struct thread_data *td, int etype, char *str) static int str_ignore_error_cb(void *data, const char *input) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); char *str, *p, *n; int type = 0, ret = 1; @@ -352,7 +354,7 @@ static int str_ignore_error_cb(void *data, const char *input) static int str_rw_cb(void *data, const char *str) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); struct thread_options *o = &td->o; char *nr; @@ -386,7 +388,7 @@ static int str_rw_cb(void *data, const char *str) static int str_mem_cb(void *data, const char *mem) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); if (td->o.mem_type == MEM_MMAPHUGE || td->o.mem_type == MEM_MMAP || td->o.mem_type == MEM_MMAPSHARED) @@ -397,7 +399,7 @@ static int str_mem_cb(void *data, const char *mem) static int fio_clock_source_cb(void *data, const char *str) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); fio_clock_source = td->o.clocksource; fio_clock_source_set = 1; @@ -407,7 +409,7 @@ static int fio_clock_source_cb(void *data, const char *str) static int str_rwmix_read_cb(void *data, unsigned long long *val) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); td->o.rwmix[DDIR_READ] = *val; td->o.rwmix[DDIR_WRITE] = 100 - *val; @@ -416,7 +418,7 @@ static int str_rwmix_read_cb(void *data, unsigned long long *val) static int str_rwmix_write_cb(void *data, unsigned long long *val) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); td->o.rwmix[DDIR_WRITE] = *val; td->o.rwmix[DDIR_READ] = 100 - *val; @@ -454,7 +456,7 @@ int fio_cpus_split(os_cpu_mask_t *mask, unsigned int cpu_index) static int str_cpumask_cb(void *data, unsigned long long *val) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); unsigned int i; long max_cpu; int ret; @@ -554,7 +556,7 @@ static int set_cpus_allowed(struct thread_data *td, os_cpu_mask_t *mask, static int str_cpus_allowed_cb(void *data, const char *input) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); if (parse_dryrun()) return 0; @@ -564,7 +566,7 @@ static int str_cpus_allowed_cb(void *data, const char *input) static int str_verify_cpus_allowed_cb(void *data, const char *input) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); if (parse_dryrun()) return 0; @@ -575,7 +577,7 @@ static int str_verify_cpus_allowed_cb(void *data, const char *input) #ifdef CONFIG_ZLIB static int str_log_cpus_allowed_cb(void *data, const char *input) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); if (parse_dryrun()) return 0; @@ -589,7 +591,7 @@ static int str_log_cpus_allowed_cb(void *data, const char *input) #ifdef CONFIG_LIBNUMA static int str_numa_cpunodes_cb(void *data, char *input) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); struct bitmask *verify_bitmask; if (parse_dryrun()) @@ -614,7 +616,7 @@ static int str_numa_cpunodes_cb(void *data, char *input) static int str_numa_mpol_cb(void *data, char *input) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); const char * const policy_types[] = { "default", "prefer", "bind", "interleave", "local", NULL }; int i; @@ -723,13 +725,78 @@ out: static int str_fst_cb(void *data, const char *str) { - struct thread_data *td = data; - char *nr = get_opt_postfix(str); + struct thread_data *td = cb_data_to_td(data); + double val; + bool done = false; + char *nr; td->file_service_nr = 1; - if (nr) { - td->file_service_nr = atoi(nr); + + switch (td->o.file_service_type) { + case FIO_FSERVICE_RANDOM: + case FIO_FSERVICE_RR: + case FIO_FSERVICE_SEQ: + nr = get_opt_postfix(str); + if (nr) { + td->file_service_nr = atoi(nr); + free(nr); + } + done = true; + break; + case FIO_FSERVICE_ZIPF: + val = FIO_DEF_ZIPF; + break; + case FIO_FSERVICE_PARETO: + val = FIO_DEF_PARETO; + break; + case FIO_FSERVICE_GAUSS: + val = 0.0; + break; + default: + log_err("fio: bad file service type: %d\n", td->o.file_service_type); + return 1; + } + + if (done) + return 0; + + nr = get_opt_postfix(str); + if (nr && !str_to_float(nr, &val, 0)) { + log_err("fio: file service type random postfix parsing failed\n"); free(nr); + return 1; + } + + free(nr); + + switch (td->o.file_service_type) { + case FIO_FSERVICE_ZIPF: + if (val == 1.00) { + log_err("fio: zipf theta must be different than 1.0\n"); + return 1; + } + if (parse_dryrun()) + return 0; + td->zipf_theta = val; + break; + case FIO_FSERVICE_PARETO: + if (val <= 0.00 || val >= 1.00) { + log_err("fio: pareto input out of range (0 < input < 1.0)\n"); + return 1; + } + if (parse_dryrun()) + return 0; + td->pareto_h = val; + break; + case FIO_FSERVICE_GAUSS: + if (val < 0.00 || val >= 100.00) { + log_err("fio: normal deviation out of range (0 <= input < 100.0)\n"); + return 1; + } + if (parse_dryrun()) + return 0; + td->gauss_dev = val; + break; } return 0; @@ -738,7 +805,7 @@ static int str_fst_cb(void *data, const char *str) #ifdef CONFIG_SYNC_FILE_RANGE static int str_sfr_cb(void *data, const char *str) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); char *nr = get_opt_postfix(str); td->sync_file_range_nr = 1; @@ -941,7 +1008,7 @@ static int parse_zoned_distribution(struct thread_data *td, const char *input) static int str_random_distribution_cb(void *data, const char *str) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); double val; char *nr; @@ -982,8 +1049,8 @@ static int str_random_distribution_cb(void *data, const char *str) return 0; td->o.pareto_h.u.f = val; } else { - if (val <= 0.00 || val >= 100.0) { - log_err("fio: normal deviation out of range (0 < input < 100.0)\n"); + if (val < 0.00 || val >= 100.0) { + log_err("fio: normal deviation out of range (0 <= input < 100.0)\n"); return 1; } if (parse_dryrun()) @@ -994,6 +1061,78 @@ static int str_random_distribution_cb(void *data, const char *str) return 0; } +static int str_steadystate_cb(void *data, const char *str) +{ + struct thread_data *td = cb_data_to_td(data); + double val; + char *nr; + char *pct; + long long ll; + + if (td->o.ss_state != FIO_SS_IOPS && td->o.ss_state != FIO_SS_IOPS_SLOPE && + td->o.ss_state != FIO_SS_BW && td->o.ss_state != FIO_SS_BW_SLOPE) { + /* should be impossible to get here */ + log_err("fio: unknown steady state criterion\n"); + return 1; + } + + nr = get_opt_postfix(str); + if (!nr) { + log_err("fio: steadystate threshold must be specified in addition to criterion\n"); + free(nr); + return 1; + } + + /* ENHANCEMENT Allow fio to understand size=10.2% and use here */ + pct = strstr(nr, "%"); + if (pct) { + *pct = '\0'; + strip_blank_end(nr); + if (!str_to_float(nr, &val, 0)) { + log_err("fio: could not parse steadystate threshold percentage\n"); + free(nr); + return 1; + } + + dprint(FD_PARSE, "set steady state threshold to %f%%\n", val); + free(nr); + if (parse_dryrun()) + return 0; + + td->o.ss_state |= __FIO_SS_PCT; + td->o.ss_limit.u.f = val; + } else if (td->o.ss_state & __FIO_SS_IOPS) { + if (!str_to_float(nr, &val, 0)) { + log_err("fio: steadystate IOPS threshold postfix parsing failed\n"); + free(nr); + return 1; + } + + dprint(FD_PARSE, "set steady state IOPS threshold to %f\n", val); + free(nr); + if (parse_dryrun()) + return 0; + + td->o.ss_limit.u.f = val; + } else { /* bandwidth criterion */ + if (str_to_decimal(nr, &ll, 1, td, 0, 0)) { + log_err("fio: steadystate BW threshold postfix parsing failed\n"); + free(nr); + return 1; + } + + dprint(FD_PARSE, "set steady state BW threshold to %lld\n", ll); + free(nr); + if (parse_dryrun()) + return 0; + + td->o.ss_limit.u.f = (double) ll; + } + + td->ss.state = td->o.ss_state; + return 0; +} + /* * 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 @@ -1059,7 +1198,8 @@ static int get_max_name_idx(char *input) * Returns the directory at the index, indexes > entires will be * assigned via modulo division of the index */ -int set_name_idx(char *target, size_t tlen, char *input, int index) +int set_name_idx(char *target, size_t tlen, char *input, int index, + bool unique_filename) { unsigned int cur_idx; int len; @@ -1071,7 +1211,7 @@ int set_name_idx(char *target, size_t tlen, char *input, int index) for (cur_idx = 0; cur_idx <= index; cur_idx++) fname = get_next_name(&str); - if (client_sockaddr_str[0]) { + if (client_sockaddr_str[0] && unique_filename) { len = snprintf(target, tlen, "%s/%s.", fname, client_sockaddr_str); } else @@ -1085,7 +1225,7 @@ int set_name_idx(char *target, size_t tlen, char *input, int index) static int str_filename_cb(void *data, const char *input) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); char *fname, *str, *p; p = str = strdup(input); @@ -1108,7 +1248,7 @@ static int str_filename_cb(void *data, const char *input) static int str_directory_cb(void *data, const char fio_unused *unused) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); struct stat sb; char *dirname, *str, *p; int ret = 0; @@ -1139,7 +1279,7 @@ out: static int str_opendir_cb(void *data, const char fio_unused *str) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); if (parse_dryrun()) return 0; @@ -1152,7 +1292,7 @@ static int str_opendir_cb(void *data, const char fio_unused *str) static int str_buffer_pattern_cb(void *data, const char *input) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); int ret; /* FIXME: for now buffer pattern does not support formats */ @@ -1173,7 +1313,7 @@ static int str_buffer_pattern_cb(void *data, const char *input) static int str_buffer_compress_cb(void *data, unsigned long long *il) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); td->flags |= TD_F_COMPRESS; td->o.compress_percentage = *il; @@ -1182,7 +1322,7 @@ static int str_buffer_compress_cb(void *data, unsigned long long *il) static int str_dedupe_cb(void *data, unsigned long long *il) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); td->flags |= TD_F_COMPRESS; td->o.dedupe_percentage = *il; @@ -1192,7 +1332,7 @@ static int str_dedupe_cb(void *data, unsigned long long *il) static int str_verify_pattern_cb(void *data, const char *input) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); int ret; td->o.verify_fmt_sz = ARRAY_SIZE(td->o.verify_fmt); @@ -1215,7 +1355,7 @@ static int str_verify_pattern_cb(void *data, const char *input) static int str_gtod_reduce_cb(void *data, int *il) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); int val = *il; td->o.disable_lat = !!val; @@ -1231,7 +1371,7 @@ static int str_gtod_reduce_cb(void *data, int *il) static int str_size_cb(void *data, unsigned long long *__val) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); unsigned long long v = *__val; if (parse_is_percent(v)) { @@ -1243,9 +1383,53 @@ static int str_size_cb(void *data, unsigned long long *__val) return 0; } +static int str_write_bw_log_cb(void *data, const char *str) +{ + struct thread_data *td = cb_data_to_td(data); + + if (str) + td->o.bw_log_file = strdup(str); + + td->o.write_bw_log = 1; + return 0; +} + +static int str_write_lat_log_cb(void *data, const char *str) +{ + struct thread_data *td = cb_data_to_td(data); + + if (str) + td->o.lat_log_file = strdup(str); + + td->o.write_lat_log = 1; + return 0; +} + +static int str_write_iops_log_cb(void *data, const char *str) +{ + struct thread_data *td = cb_data_to_td(data); + + if (str) + td->o.iops_log_file = strdup(str); + + td->o.write_iops_log = 1; + return 0; +} + +static int str_write_hist_log_cb(void *data, const char *str) +{ + struct thread_data *td = cb_data_to_td(data); + + if (str) + td->o.hist_log_file = strdup(str); + + td->o.write_hist_log = 1; + return 0; +} + static int rw_verify(struct fio_option *o, void *data) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); if (read_only && td_write(td)) { log_err("fio: job <%s> has write bit set, but fio is in" @@ -1259,7 +1443,7 @@ static int rw_verify(struct fio_option *o, void *data) static int gtod_cpu_verify(struct fio_option *o, void *data) { #ifndef FIO_HAVE_CPU_AFFINITY - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); if (td->o.gtod_cpu) { log_err("fio: platform must support CPU affinity for" @@ -1279,7 +1463,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "description", .lname = "Description of job", .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(description), + .off1 = offsetof(struct thread_options, description), .help = "Text job description", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_DESC, @@ -1288,7 +1472,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "name", .lname = "Job name", .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(name), + .off1 = offsetof(struct thread_options, name), .help = "Name of this job", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_DESC, @@ -1297,7 +1481,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "wait_for", .lname = "Waitee name", .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(wait_for), + .off1 = offsetof(struct thread_options, wait_for), .help = "Name of the job this one wants to wait for before starting", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_DESC, @@ -1306,7 +1490,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "filename", .lname = "Filename(s)", .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(filename), + .off1 = offsetof(struct thread_options, filename), .cb = str_filename_cb, .prio = -1, /* must come after "directory" */ .help = "File(s) to use for the workload", @@ -1317,7 +1501,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "directory", .lname = "Directory", .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(directory), + .off1 = offsetof(struct thread_options, directory), .cb = str_directory_cb, .help = "Directory to store files in", .category = FIO_OPT_C_FILE, @@ -1325,19 +1509,30 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { }, { .name = "filename_format", + .lname = "Filename Format", .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(filename_format), + .off1 = offsetof(struct thread_options, 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 = "unique_filename", + .lname = "Unique Filename", + .type = FIO_OPT_BOOL, + .off1 = offsetof(struct thread_options, unique_filename), + .help = "For network clients, prefix file with source IP", + .def = "1", + .category = FIO_OPT_C_FILE, + .group = FIO_OPT_G_FILENAME, + }, { .name = "lockfile", .lname = "Lockfile", .type = FIO_OPT_STR, - .off1 = td_var_offset(file_lock_mode), + .off1 = offsetof(struct thread_options, file_lock_mode), .help = "Lock file when doing IO to it", .prio = 1, .parent = "filename", @@ -1365,7 +1560,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "opendir", .lname = "Open directory", .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(opendir), + .off1 = offsetof(struct thread_options, opendir), .cb = str_opendir_cb, .help = "Recursively add files from this directory and down", .category = FIO_OPT_C_FILE, @@ -1377,7 +1572,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .alias = "readwrite", .type = FIO_OPT_STR, .cb = str_rw_cb, - .off1 = td_var_offset(td_ddir), + .off1 = offsetof(struct thread_options, td_ddir), .help = "IO direction", .def = "read", .verify = rw_verify, @@ -1430,7 +1625,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "rw_sequencer", .lname = "RW Sequencer", .type = FIO_OPT_STR, - .off1 = td_var_offset(rw_seq), + .off1 = offsetof(struct thread_options, rw_seq), .help = "IO offset generator modifier", .def = "sequential", .category = FIO_OPT_C_IO, @@ -1451,7 +1646,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "ioengine", .lname = "IO Engine", .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(ioengine), + .off1 = offsetof(struct thread_options, ioengine), .help = "IO engine to use", .def = FIO_PREFERRED_ENGINE, .category = FIO_OPT_C_IO, @@ -1471,7 +1666,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .help = "Use preadv/pwritev", }, #endif -#ifdef CONFIG_PWRITEV +#ifdef FIO_HAVE_PWRITEV2 { .ival = "pvsync2", .help = "Use preadv2/pwritev2", }, @@ -1568,6 +1763,17 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { { .ival = "libhdfs", .help = "Hadoop Distributed Filesystem (HDFS) engine" }, +#endif +#ifdef CONFIG_PMEMBLK + { .ival = "pmemblk", + .help = "NVML libpmemblk based IO engine", + }, + +#endif +#ifdef CONFIG_LINUX_DEVDAX + { .ival = "dev-dax", + .help = "DAX Device based IO engine", + }, #endif { .ival = "external", .help = "Load external engine (append name)", @@ -1578,7 +1784,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "iodepth", .lname = "IO Depth", .type = FIO_OPT_INT, - .off1 = td_var_offset(iodepth), + .off1 = offsetof(struct thread_options, iodepth), .help = "Number of IO buffers to keep in flight", .minval = 1, .interval = 1, @@ -1591,11 +1797,10 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "IO Depth batch", .alias = "iodepth_batch_submit", .type = FIO_OPT_INT, - .off1 = td_var_offset(iodepth_batch), + .off1 = offsetof(struct thread_options, iodepth_batch), .help = "Number of IO buffers to submit in one go", .parent = "iodepth", .hide = 1, - .minval = 1, .interval = 1, .def = "1", .category = FIO_OPT_C_IO, @@ -1606,7 +1811,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Min IO depth batch complete", .alias = "iodepth_batch_complete", .type = FIO_OPT_INT, - .off1 = td_var_offset(iodepth_batch_complete_min), + .off1 = offsetof(struct thread_options, iodepth_batch_complete_min), .help = "Min number of IO buffers to retrieve in one go", .parent = "iodepth", .hide = 1, @@ -1620,7 +1825,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "iodepth_batch_complete_max", .lname = "Max IO depth batch complete", .type = FIO_OPT_INT, - .off1 = td_var_offset(iodepth_batch_complete_max), + .off1 = offsetof(struct thread_options, iodepth_batch_complete_max), .help = "Max number of IO buffers to retrieve in one go", .parent = "iodepth", .hide = 1, @@ -1633,7 +1838,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "iodepth_low", .lname = "IO Depth batch low", .type = FIO_OPT_INT, - .off1 = td_var_offset(iodepth_low), + .off1 = offsetof(struct thread_options, iodepth_low), .help = "Low water mark for queuing depth", .parent = "iodepth", .hide = 1, @@ -1645,7 +1850,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "io_submit_mode", .lname = "IO submit mode", .type = FIO_OPT_STR, - .off1 = td_var_offset(io_submit_mode), + .off1 = offsetof(struct thread_options, io_submit_mode), .help = "How IO submissions and completions are done", .def = "inline", .category = FIO_OPT_C_IO, @@ -1666,7 +1871,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Size", .type = FIO_OPT_STR_VAL, .cb = str_size_cb, - .off1 = td_var_offset(size), + .off1 = offsetof(struct thread_options, size), .help = "Total size of device or files", .interval = 1024 * 1024, .category = FIO_OPT_C_IO, @@ -1677,7 +1882,8 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .alias = "io_limit", .lname = "IO Size", .type = FIO_OPT_STR_VAL, - .off1 = td_var_offset(io_limit), + .off1 = offsetof(struct thread_options, io_limit), + .help = "Total size of I/O to be performed", .interval = 1024 * 1024, .category = FIO_OPT_C_IO, .group = FIO_OPT_G_INVALID, @@ -1687,7 +1893,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Fill device", .alias = "fill_fs", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(fill_device), + .off1 = offsetof(struct thread_options, fill_device), .help = "Write until an ENOSPC error occurs", .def = "0", .category = FIO_OPT_C_FILE, @@ -1697,8 +1903,8 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "filesize", .lname = "File size", .type = FIO_OPT_STR_VAL, - .off1 = td_var_offset(file_size_low), - .off2 = td_var_offset(file_size_high), + .off1 = offsetof(struct thread_options, file_size_low), + .off2 = offsetof(struct thread_options, file_size_high), .minval = 1, .help = "Size of individual files", .interval = 1024 * 1024, @@ -1709,7 +1915,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "file_append", .lname = "File append", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(file_append), + .off1 = offsetof(struct thread_options, file_append), .help = "IO will start at the end of the file(s)", .def = "0", .category = FIO_OPT_C_FILE, @@ -1720,7 +1926,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "IO offset", .alias = "fileoffset", .type = FIO_OPT_STR_VAL, - .off1 = td_var_offset(start_offset), + .off1 = offsetof(struct thread_options, start_offset), .help = "Start IO from this offset", .def = "0", .interval = 1024 * 1024, @@ -1731,7 +1937,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "offset_increment", .lname = "IO offset increment", .type = FIO_OPT_STR_VAL, - .off1 = td_var_offset(offset_increment), + .off1 = offsetof(struct thread_options, offset_increment), .help = "What is the increment from one offset to the next", .parent = "offset", .hide = 1, @@ -1744,7 +1950,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "number_ios", .lname = "Number of IOs to perform", .type = FIO_OPT_STR_VAL, - .off1 = td_var_offset(number_ios), + .off1 = offsetof(struct thread_options, number_ios), .help = "Force job completion after this number of IOs", .def = "0", .category = FIO_OPT_C_IO, @@ -1755,12 +1961,12 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Block size", .alias = "blocksize", .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]), + .off1 = offsetof(struct thread_options, bs[DDIR_READ]), + .off2 = offsetof(struct thread_options, bs[DDIR_WRITE]), + .off3 = offsetof(struct thread_options, bs[DDIR_TRIM]), .minval = 1, .help = "Block size unit", - .def = "4k", + .def = "4096", .parent = "rw", .hide = 1, .interval = 512, @@ -1772,9 +1978,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Block size align", .alias = "blockalign", .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]), + .off1 = offsetof(struct thread_options, ba[DDIR_READ]), + .off2 = offsetof(struct thread_options, ba[DDIR_WRITE]), + .off3 = offsetof(struct thread_options, ba[DDIR_TRIM]), .minval = 1, .help = "IO block offset alignment", .parent = "rw", @@ -1788,12 +1994,12 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Block size range", .alias = "blocksize_range", .type = FIO_OPT_RANGE, - .off1 = td_var_offset(min_bs[DDIR_READ]), - .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]), + .off1 = offsetof(struct thread_options, min_bs[DDIR_READ]), + .off2 = offsetof(struct thread_options, max_bs[DDIR_READ]), + .off3 = offsetof(struct thread_options, min_bs[DDIR_WRITE]), + .off4 = offsetof(struct thread_options, max_bs[DDIR_WRITE]), + .off5 = offsetof(struct thread_options, min_bs[DDIR_TRIM]), + .off6 = offsetof(struct thread_options, max_bs[DDIR_TRIM]), .minval = 1, .help = "Set block size range (in more detail than bs)", .parent = "rw", @@ -1807,7 +2013,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Block size split", .type = FIO_OPT_STR, .cb = str_bssplit_cb, - .off1 = td_var_offset(bssplit), + .off1 = offsetof(struct thread_options, bssplit), .help = "Set a specific mix of block sizes", .parent = "rw", .hide = 1, @@ -1819,7 +2025,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Block size unaligned", .alias = "blocksize_unaligned", .type = FIO_OPT_STR_SET, - .off1 = td_var_offset(bs_unaligned), + .off1 = offsetof(struct thread_options, bs_unaligned), .help = "Don't sector align IO buffer sizes", .parent = "rw", .hide = 1, @@ -1830,7 +2036,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .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), + .off1 = offsetof(struct thread_options, bs_is_seq_rand), .help = "Consider any blocksize setting to be sequential,random", .def = "0", .parent = "blocksize", @@ -1841,7 +2047,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "randrepeat", .lname = "Random repeatable", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(rand_repeatable), + .off1 = offsetof(struct thread_options, rand_repeatable), .help = "Use repeatable random IO pattern", .def = "1", .parent = "rw", @@ -1853,7 +2059,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "randseed", .lname = "The random generator seed", .type = FIO_OPT_STR_VAL, - .off1 = td_var_offset(rand_seed), + .off1 = offsetof(struct thread_options, rand_seed), .help = "Set the random generator seed value", .def = "0x89", .parent = "rw", @@ -1864,7 +2070,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "use_os_rand", .lname = "Use OS random", .type = FIO_OPT_DEPRECATED, - .off1 = td_var_offset(dep_use_os_rand), + .off1 = offsetof(struct thread_options, dep_use_os_rand), .category = FIO_OPT_C_IO, .group = FIO_OPT_G_RANDOM, }, @@ -1872,7 +2078,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "norandommap", .lname = "No randommap", .type = FIO_OPT_STR_SET, - .off1 = td_var_offset(norandommap), + .off1 = offsetof(struct thread_options, norandommap), .help = "Accept potential duplicate random blocks", .parent = "rw", .hide = 1, @@ -1884,7 +2090,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "softrandommap", .lname = "Soft randommap", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(softrandommap), + .off1 = offsetof(struct thread_options, softrandommap), .help = "Set norandommap if randommap allocation fails", .parent = "norandommap", .hide = 1, @@ -1894,8 +2100,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { }, { .name = "random_generator", + .lname = "Random Generator", .type = FIO_OPT_STR, - .off1 = td_var_offset(random_generator), + .off1 = offsetof(struct thread_options, random_generator), .help = "Type of random number generator to use", .def = "tausworthe", .posval = { @@ -1918,8 +2125,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { }, { .name = "random_distribution", + .lname = "Random Distribution", .type = FIO_OPT_STR, - .off1 = td_var_offset(random_distribution), + .off1 = offsetof(struct thread_options, random_distribution), .cb = str_random_distribution_cb, .help = "Random offset distribution generator", .def = "random", @@ -1938,7 +2146,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { }, { .ival = "normal", .oval = FIO_RAND_DIST_GAUSS, - .help = "Normal (gaussian) distribution", + .help = "Normal (Gaussian) distribution", }, { .ival = "zoned", .oval = FIO_RAND_DIST_ZONED, @@ -1953,9 +2161,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .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]), + .off1 = offsetof(struct thread_options, perc_rand[DDIR_READ]), + .off2 = offsetof(struct thread_options, perc_rand[DDIR_WRITE]), + .off3 = offsetof(struct thread_options, perc_rand[DDIR_TRIM]), .maxval = 100, .help = "Percentage of seq/random mix that should be random", .def = "100,100,100", @@ -1973,8 +2181,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { }, { .name = "allrandrepeat", + .lname = "All Random Repeat", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(allrand_repeatable), + .off1 = offsetof(struct thread_options, allrand_repeatable), .help = "Use repeatable random numbers for everything", .def = "0", .category = FIO_OPT_C_IO, @@ -1985,7 +2194,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Number of files", .alias = "nr_files", .type = FIO_OPT_INT, - .off1 = td_var_offset(nr_files), + .off1 = offsetof(struct thread_options, nr_files), .help = "Split job workload between this number of files", .def = "1", .interval = 1, @@ -1996,7 +2205,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "openfiles", .lname = "Number of open files", .type = FIO_OPT_INT, - .off1 = td_var_offset(open_files), + .off1 = offsetof(struct thread_options, open_files), .help = "Number of files to keep open at the same time", .category = FIO_OPT_C_FILE, .group = FIO_OPT_G_INVALID, @@ -2006,7 +2215,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "File service type", .type = FIO_OPT_STR, .cb = str_fst_cb, - .off1 = td_var_offset(file_service_type), + .off1 = offsetof(struct thread_options, file_service_type), .help = "How to select which file to service next", .def = "roundrobin", .category = FIO_OPT_C_FILE, @@ -2014,7 +2223,19 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .posval = { { .ival = "random", .oval = FIO_FSERVICE_RANDOM, - .help = "Choose a file at random", + .help = "Choose a file at random (uniform)", + }, + { .ival = "zipf", + .oval = FIO_FSERVICE_ZIPF, + .help = "Zipf randomized", + }, + { .ival = "pareto", + .oval = FIO_FSERVICE_PARETO, + .help = "Pareto randomized", + }, + { .ival = "gauss", + .oval = FIO_FSERVICE_GAUSS, + .help = "Normal (gaussian) distribution", }, { .ival = "roundrobin", .oval = FIO_FSERVICE_RR, @@ -2033,7 +2254,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "fallocate", .lname = "Fallocate", .type = FIO_OPT_STR, - .off1 = td_var_offset(fallocate_mode), + .off1 = offsetof(struct thread_options, fallocate_mode), .help = "Whether pre-allocation is performed when laying out files", .def = "posix", .category = FIO_OPT_C_FILE, @@ -2064,12 +2285,37 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { }, }, }, -#endif /* CONFIG_POSIX_FALLOCATE */ +#else /* CONFIG_POSIX_FALLOCATE */ + { + .name = "fallocate", + .lname = "Fallocate", + .type = FIO_OPT_UNSUPPORTED, + .help = "Your platform does not support fallocate", + }, +#endif /* CONFIG_POSIX_FALLOCATE */ { .name = "fadvise_hint", .lname = "Fadvise hint", - .type = FIO_OPT_BOOL, - .off1 = td_var_offset(fadvise_hint), + .type = FIO_OPT_STR, + .off1 = offsetof(struct thread_options, fadvise_hint), + .posval = { + { .ival = "0", + .oval = F_ADV_NONE, + .help = "Don't issue fadvise", + }, + { .ival = "1", + .oval = F_ADV_TYPE, + .help = "Advise using fio IO pattern", + }, + { .ival = "random", + .oval = F_ADV_RANDOM, + .help = "Advise using FADV_RANDOM", + }, + { .ival = "sequential", + .oval = F_ADV_SEQUENTIAL, + .help = "Advise using FADV_SEQUENTIAL", + }, + }, .help = "Use fadvise() to advise the kernel on IO pattern", .def = "1", .category = FIO_OPT_C_FILE, @@ -2080,17 +2326,24 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "fadvise_stream", .lname = "Fadvise stream", .type = FIO_OPT_INT, - .off1 = td_var_offset(fadvise_stream), + .off1 = offsetof(struct thread_options, fadvise_stream), .help = "Use fadvise() to set stream ID", .category = FIO_OPT_C_FILE, .group = FIO_OPT_G_INVALID, }, +#else + { + .name = "fadvise_stream", + .lname = "Fadvise stream", + .type = FIO_OPT_UNSUPPORTED, + .help = "Your platform does not support fadvise stream ID", + }, #endif { .name = "fsync", .lname = "Fsync", .type = FIO_OPT_INT, - .off1 = td_var_offset(fsync_blocks), + .off1 = offsetof(struct thread_options, fsync_blocks), .help = "Issue fsync for writes every given number of blocks", .def = "0", .interval = 1, @@ -2101,7 +2354,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "fdatasync", .lname = "Fdatasync", .type = FIO_OPT_INT, - .off1 = td_var_offset(fdatasync_blocks), + .off1 = offsetof(struct thread_options, fdatasync_blocks), .help = "Issue fdatasync for writes every given number of blocks", .def = "0", .interval = 1, @@ -2112,7 +2365,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "write_barrier", .lname = "Write barrier", .type = FIO_OPT_INT, - .off1 = td_var_offset(barrier_blocks), + .off1 = offsetof(struct thread_options, barrier_blocks), .help = "Make every Nth write a barrier write", .def = "0", .interval = 1, @@ -2143,17 +2396,24 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { }, .type = FIO_OPT_STR_MULTI, .cb = str_sfr_cb, - .off1 = td_var_offset(sync_file_range), + .off1 = offsetof(struct thread_options, sync_file_range), .help = "Use sync_file_range()", .category = FIO_OPT_C_FILE, .group = FIO_OPT_G_INVALID, }, +#else + { + .name = "sync_file_range", + .lname = "Sync file range", + .type = FIO_OPT_UNSUPPORTED, + .help = "Your platform does not support sync_file_range", + }, #endif { .name = "direct", .lname = "Direct I/O", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(odirect), + .off1 = offsetof(struct thread_options, odirect), .help = "Use O_DIRECT IO (negates buffered)", .def = "0", .inverse = "buffered", @@ -2164,7 +2424,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "atomic", .lname = "Atomic I/O", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(oatomic), + .off1 = offsetof(struct thread_options, oatomic), .help = "Use Atomic IO with O_DIRECT (implies O_DIRECT)", .def = "0", .category = FIO_OPT_C_IO, @@ -2174,7 +2434,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "buffered", .lname = "Buffered I/O", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(odirect), + .off1 = offsetof(struct thread_options, odirect), .neg = 1, .help = "Use buffered IO (negates direct)", .def = "1", @@ -2186,7 +2446,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "overwrite", .lname = "Overwrite", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(overwrite), + .off1 = offsetof(struct thread_options, overwrite), .help = "When writing, set whether to overwrite current data", .def = "0", .category = FIO_OPT_C_FILE, @@ -2196,7 +2456,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "loops", .lname = "Loops", .type = FIO_OPT_INT, - .off1 = td_var_offset(loops), + .off1 = offsetof(struct thread_options, loops), .help = "Number of times to run the job", .def = "1", .interval = 1, @@ -2207,7 +2467,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "numjobs", .lname = "Number of jobs", .type = FIO_OPT_INT, - .off1 = td_var_offset(numjobs), + .off1 = offsetof(struct thread_options, numjobs), .help = "Duplicate this job this many times", .def = "1", .interval = 1, @@ -2218,8 +2478,8 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "startdelay", .lname = "Start delay", .type = FIO_OPT_STR_VAL_TIME, - .off1 = td_var_offset(start_delay), - .off2 = td_var_offset(start_delay_high), + .off1 = offsetof(struct thread_options, start_delay), + .off2 = offsetof(struct thread_options, start_delay_high), .help = "Only start job when this period has passed", .def = "0", .is_seconds = 1, @@ -2232,7 +2492,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Runtime", .alias = "timeout", .type = FIO_OPT_STR_VAL_TIME, - .off1 = td_var_offset(timeout), + .off1 = offsetof(struct thread_options, timeout), .help = "Stop workload when this amount of time has passed", .def = "0", .is_seconds = 1, @@ -2244,7 +2504,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "time_based", .lname = "Time based", .type = FIO_OPT_STR_SET, - .off1 = td_var_offset(time_based), + .off1 = offsetof(struct thread_options, time_based), .help = "Keep running until runtime/timeout is met", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_RUNTIME, @@ -2253,7 +2513,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "verify_only", .lname = "Verify only", .type = FIO_OPT_STR_SET, - .off1 = td_var_offset(verify_only), + .off1 = offsetof(struct thread_options, verify_only), .help = "Verifies previously written data is still valid", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_RUNTIME, @@ -2262,7 +2522,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "ramp_time", .lname = "Ramp time", .type = FIO_OPT_STR_VAL_TIME, - .off1 = td_var_offset(ramp_time), + .off1 = offsetof(struct thread_options, ramp_time), .help = "Ramp up time before measuring performance", .is_seconds = 1, .is_time = 1, @@ -2274,7 +2534,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Clock source", .type = FIO_OPT_STR, .cb = fio_clock_source_cb, - .off1 = td_var_offset(clocksource), + .off1 = offsetof(struct thread_options, clocksource), .help = "What type of timing source to use", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_CLOCK, @@ -2305,7 +2565,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "I/O Memory", .type = FIO_OPT_STR, .cb = str_mem_cb, - .off1 = td_var_offset(mem_type), + .off1 = offsetof(struct thread_options, mem_type), .help = "Backing type for IO buffers", .def = "malloc", .category = FIO_OPT_C_IO, @@ -2348,7 +2608,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .alias = "mem_align", .lname = "I/O memory alignment", .type = FIO_OPT_INT, - .off1 = td_var_offset(mem_align), + .off1 = offsetof(struct thread_options, mem_align), .minval = 0, .help = "IO memory buffer offset alignment", .def = "0", @@ -2361,7 +2621,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "verify", .lname = "Verify", .type = FIO_OPT_STR, - .off1 = td_var_offset(verify), + .off1 = offsetof(struct thread_options, verify), .help = "Verify data written", .def = "0", .category = FIO_OPT_C_IO, @@ -2438,7 +2698,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "do_verify", .lname = "Perform verify step", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(do_verify), + .off1 = offsetof(struct thread_options, do_verify), .help = "Run verification stage after write", .def = "1", .parent = "verify", @@ -2450,7 +2710,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "verifysort", .lname = "Verify sort", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(verifysort), + .off1 = offsetof(struct thread_options, verifysort), .help = "Sort written verify blocks for read back", .def = "1", .parent = "verify", @@ -2460,8 +2720,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { }, { .name = "verifysort_nr", + .lname = "Verify Sort Nr", .type = FIO_OPT_INT, - .off1 = td_var_offset(verifysort_nr), + .off1 = offsetof(struct thread_options, verifysort_nr), .help = "Pre-load and sort verify blocks for a read workload", .minval = 0, .maxval = 131072, @@ -2474,7 +2735,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "verify_interval", .lname = "Verify interval", .type = FIO_OPT_INT, - .off1 = td_var_offset(verify_interval), + .off1 = offsetof(struct thread_options, verify_interval), .minval = 2 * sizeof(struct verify_header), .help = "Store verify buffer header every N bytes", .parent = "verify", @@ -2488,7 +2749,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Verify offset", .type = FIO_OPT_INT, .help = "Offset verify header location by N bytes", - .off1 = td_var_offset(verify_offset), + .off1 = offsetof(struct thread_options, verify_offset), .minval = sizeof(struct verify_header), .parent = "verify", .hide = 1, @@ -2500,7 +2761,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Verify pattern", .type = FIO_OPT_STR, .cb = str_verify_pattern_cb, - .off1 = td_var_offset(verify_pattern), + .off1 = offsetof(struct thread_options, verify_pattern), .help = "Fill pattern for IO buffers", .parent = "verify", .hide = 1, @@ -2511,7 +2772,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "verify_fatal", .lname = "Verify fatal", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(verify_fatal), + .off1 = offsetof(struct thread_options, verify_fatal), .def = "0", .help = "Exit on a single verify failure, don't continue", .parent = "verify", @@ -2523,7 +2784,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "verify_dump", .lname = "Verify dump", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(verify_dump), + .off1 = offsetof(struct thread_options, verify_dump), .def = "0", .help = "Dump contents of good and bad blocks on failure", .parent = "verify", @@ -2535,7 +2796,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "verify_async", .lname = "Verify asynchronously", .type = FIO_OPT_INT, - .off1 = td_var_offset(verify_async), + .off1 = offsetof(struct thread_options, verify_async), .def = "0", .help = "Number of async verifier threads to use", .parent = "verify", @@ -2547,7 +2808,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "verify_backlog", .lname = "Verify backlog", .type = FIO_OPT_STR_VAL, - .off1 = td_var_offset(verify_backlog), + .off1 = offsetof(struct thread_options, verify_backlog), .help = "Verify after this number of blocks are written", .parent = "verify", .hide = 1, @@ -2558,7 +2819,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "verify_backlog_batch", .lname = "Verify backlog batch", .type = FIO_OPT_INT, - .off1 = td_var_offset(verify_batch), + .off1 = offsetof(struct thread_options, verify_batch), .help = "Verify this number of IO blocks", .parent = "verify", .hide = 1, @@ -2571,17 +2832,25 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Async verify CPUs", .type = FIO_OPT_STR, .cb = str_verify_cpus_allowed_cb, - .off1 = td_var_offset(verify_cpumask), + .off1 = offsetof(struct thread_options, verify_cpumask), .help = "Set CPUs allowed for async verify threads", .parent = "verify_async", .hide = 1, .category = FIO_OPT_C_IO, .group = FIO_OPT_G_VERIFY, }, +#else + { + .name = "verify_async_cpus", + .lname = "Async verify CPUs", + .type = FIO_OPT_UNSUPPORTED, + .help = "Your platform does not support CPU affinities", + }, #endif { .name = "experimental_verify", - .off1 = td_var_offset(experimental_verify), + .lname = "Experimental Verify", + .off1 = offsetof(struct thread_options, experimental_verify), .type = FIO_OPT_BOOL, .help = "Enable experimental verification", .parent = "verify", @@ -2591,7 +2860,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { { .name = "verify_state_load", .lname = "Load verify state", - .off1 = td_var_offset(verify_state), + .off1 = offsetof(struct thread_options, verify_state), .type = FIO_OPT_BOOL, .help = "Load verify termination state", .parent = "verify", @@ -2601,7 +2870,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { { .name = "verify_state_save", .lname = "Save verify state", - .off1 = td_var_offset(verify_state_save), + .off1 = offsetof(struct thread_options, verify_state_save), .type = FIO_OPT_BOOL, .def = "1", .help = "Save verify state on termination", @@ -2614,10 +2883,10 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "trim_percentage", .lname = "Trim percentage", .type = FIO_OPT_INT, - .off1 = td_var_offset(trim_percentage), + .off1 = offsetof(struct thread_options, trim_percentage), .minval = 0, .maxval = 100, - .help = "Number of verify blocks to discard/trim", + .help = "Number of verify blocks to trim (i.e., discard)", .parent = "verify", .def = "0", .interval = 1, @@ -2629,8 +2898,8 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "trim_verify_zero", .lname = "Verify trim zero", .type = FIO_OPT_BOOL, - .help = "Verify that trim/discarded blocks are returned as zeroes", - .off1 = td_var_offset(trim_zero), + .help = "Verify that trimmed (i.e., discarded) blocks are returned as zeroes", + .off1 = offsetof(struct thread_options, trim_zero), .parent = "trim_percentage", .hide = 1, .def = "1", @@ -2641,7 +2910,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "trim_backlog", .lname = "Trim backlog", .type = FIO_OPT_STR_VAL, - .off1 = td_var_offset(trim_backlog), + .off1 = offsetof(struct thread_options, trim_backlog), .help = "Trim after this number of blocks are written", .parent = "trim_percentage", .hide = 1, @@ -2653,7 +2922,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "trim_backlog_batch", .lname = "Trim backlog batch", .type = FIO_OPT_INT, - .off1 = td_var_offset(trim_batch), + .off1 = offsetof(struct thread_options, trim_batch), .help = "Trim this number of IO blocks", .parent = "trim_percentage", .hide = 1, @@ -2661,12 +2930,37 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .category = FIO_OPT_C_IO, .group = FIO_OPT_G_TRIM, }, +#else + { + .name = "trim_percentage", + .lname = "Trim percentage", + .type = FIO_OPT_UNSUPPORTED, + .help = "Fio does not support TRIM on your platform", + }, + { + .name = "trim_verify_zero", + .lname = "Verify trim zero", + .type = FIO_OPT_UNSUPPORTED, + .help = "Fio does not support TRIM on your platform", + }, + { + .name = "trim_backlog", + .lname = "Trim backlog", + .type = FIO_OPT_UNSUPPORTED, + .help = "Fio does not support TRIM on your platform", + }, + { + .name = "trim_backlog_batch", + .lname = "Trim backlog batch", + .type = FIO_OPT_UNSUPPORTED, + .help = "Fio does not support TRIM on your platform", + }, #endif { .name = "write_iolog", .lname = "Write I/O log", .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(write_iolog_file), + .off1 = offsetof(struct thread_options, write_iolog_file), .help = "Store IO pattern to file", .category = FIO_OPT_C_IO, .group = FIO_OPT_G_IOLOG, @@ -2675,7 +2969,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "read_iolog", .lname = "Read I/O log", .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(read_iolog_file), + .off1 = offsetof(struct thread_options, read_iolog_file), .help = "Playback IO pattern from file", .category = FIO_OPT_C_IO, .group = FIO_OPT_G_IOLOG, @@ -2684,7 +2978,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "replay_no_stall", .lname = "Don't stall on replay", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(no_stall), + .off1 = offsetof(struct thread_options, no_stall), .def = "0", .parent = "read_iolog", .hide = 1, @@ -2696,7 +2990,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "replay_redirect", .lname = "Redirect device for replay", .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(replay_redirect), + .off1 = offsetof(struct thread_options, replay_redirect), .parent = "read_iolog", .hide = 1, .help = "Replay all I/O onto this device, regardless of trace device", @@ -2707,7 +3001,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "replay_scale", .lname = "Replace offset scale factor", .type = FIO_OPT_INT, - .off1 = td_var_offset(replay_scale), + .off1 = offsetof(struct thread_options, replay_scale), .parent = "read_iolog", .def = "1", .help = "Align offsets to this blocksize", @@ -2718,7 +3012,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "replay_align", .lname = "Replace alignment", .type = FIO_OPT_INT, - .off1 = td_var_offset(replay_align), + .off1 = offsetof(struct thread_options, replay_align), .parent = "read_iolog", .help = "Scale offset down by this factor", .category = FIO_OPT_C_IO, @@ -2729,7 +3023,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "exec_prerun", .lname = "Pre-execute runnable", .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(exec_prerun), + .off1 = offsetof(struct thread_options, exec_prerun), .help = "Execute this file prior to running job", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_INVALID, @@ -2738,7 +3032,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "exec_postrun", .lname = "Post-execute runnable", .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(exec_postrun), + .off1 = offsetof(struct thread_options, exec_postrun), .help = "Execute this file after running job", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_INVALID, @@ -2748,17 +3042,24 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "ioscheduler", .lname = "I/O scheduler", .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(ioscheduler), + .off1 = offsetof(struct thread_options, ioscheduler), .help = "Use this IO scheduler on the backing device", .category = FIO_OPT_C_FILE, .group = FIO_OPT_G_INVALID, }, +#else + { + .name = "ioscheduler", + .lname = "I/O scheduler", + .type = FIO_OPT_UNSUPPORTED, + .help = "Your platform does not support IO scheduler switching", + }, #endif { .name = "zonesize", .lname = "Zone size", .type = FIO_OPT_STR_VAL, - .off1 = td_var_offset(zone_size), + .off1 = offsetof(struct thread_options, zone_size), .help = "Amount of data to read per zone", .def = "0", .interval = 1024 * 1024, @@ -2769,7 +3070,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "zonerange", .lname = "Zone range", .type = FIO_OPT_STR_VAL, - .off1 = td_var_offset(zone_range), + .off1 = offsetof(struct thread_options, zone_range), .help = "Give size of an IO zone", .def = "0", .interval = 1024 * 1024, @@ -2780,7 +3081,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "zoneskip", .lname = "Zone skip", .type = FIO_OPT_STR_VAL, - .off1 = td_var_offset(zone_skip), + .off1 = offsetof(struct thread_options, zone_skip), .help = "Space between IO zones", .def = "0", .interval = 1024 * 1024, @@ -2791,7 +3092,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "lockmem", .lname = "Lock memory", .type = FIO_OPT_STR_VAL, - .off1 = td_var_offset(lockmem), + .off1 = offsetof(struct thread_options, lockmem), .help = "Lock down this amount of memory (per worker)", .def = "0", .interval = 1024 * 1024, @@ -2803,7 +3104,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Read/write mix read", .type = FIO_OPT_INT, .cb = str_rwmix_read_cb, - .off1 = td_var_offset(rwmix[DDIR_READ]), + .off1 = offsetof(struct thread_options, rwmix[DDIR_READ]), .maxval = 100, .help = "Percentage of mixed workload that is reads", .def = "50", @@ -2817,7 +3118,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Read/write mix write", .type = FIO_OPT_INT, .cb = str_rwmix_write_cb, - .off1 = td_var_offset(rwmix[DDIR_WRITE]), + .off1 = offsetof(struct thread_options, rwmix[DDIR_WRITE]), .maxval = 100, .help = "Percentage of mixed workload that is writes", .def = "50", @@ -2837,7 +3138,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "nice", .lname = "Nice", .type = FIO_OPT_INT, - .off1 = td_var_offset(nice), + .off1 = offsetof(struct thread_options, nice), .help = "Set job CPU nice value", .minval = -19, .maxval = 20, @@ -2851,32 +3152,51 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "prio", .lname = "I/O nice priority", .type = FIO_OPT_INT, - .off1 = td_var_offset(ioprio), + .off1 = offsetof(struct thread_options, ioprio), .help = "Set job IO priority value", - .minval = 0, - .maxval = 7, + .minval = IOPRIO_MIN_PRIO, + .maxval = IOPRIO_MAX_PRIO, .interval = 1, .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_CRED, }, +#else + { + .name = "prio", + .lname = "I/O nice priority", + .type = FIO_OPT_UNSUPPORTED, + .help = "Your platform does not support IO priorities", + }, +#endif +#ifdef FIO_HAVE_IOPRIO_CLASS +#ifndef FIO_HAVE_IOPRIO +#error "FIO_HAVE_IOPRIO_CLASS requires FIO_HAVE_IOPRIO" +#endif { .name = "prioclass", .lname = "I/O nice priority class", .type = FIO_OPT_INT, - .off1 = td_var_offset(ioprio_class), + .off1 = offsetof(struct thread_options, ioprio_class), .help = "Set job IO priority class", - .minval = 0, - .maxval = 3, + .minval = IOPRIO_MIN_PRIO_CLASS, + .maxval = IOPRIO_MAX_PRIO_CLASS, .interval = 1, .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_CRED, }, +#else + { + .name = "prioclass", + .lname = "I/O nice priority class", + .type = FIO_OPT_UNSUPPORTED, + .help = "Your platform does not support IO priority classes", + }, #endif { .name = "thinktime", .lname = "Thinktime", .type = FIO_OPT_INT, - .off1 = td_var_offset(thinktime), + .off1 = offsetof(struct thread_options, thinktime), .help = "Idle time between IO buffers (usec)", .def = "0", .is_time = 1, @@ -2887,7 +3207,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "thinktime_spin", .lname = "Thinktime spin", .type = FIO_OPT_INT, - .off1 = td_var_offset(thinktime_spin), + .off1 = offsetof(struct thread_options, thinktime_spin), .help = "Start think time by spinning this amount (usec)", .def = "0", .is_time = 1, @@ -2900,7 +3220,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "thinktime_blocks", .lname = "Thinktime blocks", .type = FIO_OPT_INT, - .off1 = td_var_offset(thinktime_blocks), + .off1 = offsetof(struct thread_options, thinktime_blocks), .help = "IO buffer period between 'thinktime'", .def = "1", .parent = "thinktime", @@ -2912,9 +3232,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "rate", .lname = "I/O rate", .type = FIO_OPT_INT, - .off1 = td_var_offset(rate[DDIR_READ]), - .off2 = td_var_offset(rate[DDIR_WRITE]), - .off3 = td_var_offset(rate[DDIR_TRIM]), + .off1 = offsetof(struct thread_options, rate[DDIR_READ]), + .off2 = offsetof(struct thread_options, rate[DDIR_WRITE]), + .off3 = offsetof(struct thread_options, rate[DDIR_TRIM]), .help = "Set bandwidth rate", .category = FIO_OPT_C_IO, .group = FIO_OPT_G_RATE, @@ -2924,9 +3244,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .alias = "ratemin", .lname = "I/O min rate", .type = FIO_OPT_INT, - .off1 = td_var_offset(ratemin[DDIR_READ]), - .off2 = td_var_offset(ratemin[DDIR_WRITE]), - .off3 = td_var_offset(ratemin[DDIR_TRIM]), + .off1 = offsetof(struct thread_options, ratemin[DDIR_READ]), + .off2 = offsetof(struct thread_options, ratemin[DDIR_WRITE]), + .off3 = offsetof(struct thread_options, ratemin[DDIR_TRIM]), .help = "Job must meet this rate or it will be shutdown", .parent = "rate", .hide = 1, @@ -2937,9 +3257,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "rate_iops", .lname = "I/O rate IOPS", .type = FIO_OPT_INT, - .off1 = td_var_offset(rate_iops[DDIR_READ]), - .off2 = td_var_offset(rate_iops[DDIR_WRITE]), - .off3 = td_var_offset(rate_iops[DDIR_TRIM]), + .off1 = offsetof(struct thread_options, rate_iops[DDIR_READ]), + .off2 = offsetof(struct thread_options, rate_iops[DDIR_WRITE]), + .off3 = offsetof(struct thread_options, rate_iops[DDIR_TRIM]), .help = "Limit IO used to this number of IO operations/sec", .hide = 1, .category = FIO_OPT_C_IO, @@ -2949,9 +3269,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "rate_iops_min", .lname = "I/O min rate IOPS", .type = FIO_OPT_INT, - .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]), + .off1 = offsetof(struct thread_options, rate_iops_min[DDIR_READ]), + .off2 = offsetof(struct thread_options, rate_iops_min[DDIR_WRITE]), + .off3 = offsetof(struct thread_options, rate_iops_min[DDIR_TRIM]), .help = "Job must meet this rate or it will be shut down", .parent = "rate_iops", .hide = 1, @@ -2962,7 +3282,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "rate_process", .lname = "Rate Process", .type = FIO_OPT_STR, - .off1 = td_var_offset(rate_process), + .off1 = offsetof(struct thread_options, rate_process), .help = "What process controls how rated IO is managed", .def = "linear", .category = FIO_OPT_C_IO, @@ -2985,7 +3305,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .alias = "ratecycle", .lname = "I/O rate cycle", .type = FIO_OPT_INT, - .off1 = td_var_offset(ratecycle), + .off1 = offsetof(struct thread_options, ratecycle), .help = "Window average for rate limits (msec)", .def = "1000", .parent = "rate", @@ -2995,8 +3315,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { }, { .name = "max_latency", + .lname = "Max Latency", .type = FIO_OPT_INT, - .off1 = td_var_offset(max_latency), + .off1 = offsetof(struct thread_options, max_latency), .help = "Maximum tolerated IO latency (usec)", .is_time = 1, .category = FIO_OPT_C_IO, @@ -3006,7 +3327,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "latency_target", .lname = "Latency Target (usec)", .type = FIO_OPT_STR_VAL_TIME, - .off1 = td_var_offset(latency_target), + .off1 = offsetof(struct thread_options, latency_target), .help = "Ramp to max queue depth supporting this latency", .is_time = 1, .category = FIO_OPT_C_IO, @@ -3016,7 +3337,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "latency_window", .lname = "Latency Window (usec)", .type = FIO_OPT_STR_VAL_TIME, - .off1 = td_var_offset(latency_window), + .off1 = offsetof(struct thread_options, latency_window), .help = "Time to sustain latency_target", .is_time = 1, .category = FIO_OPT_C_IO, @@ -3026,7 +3347,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "latency_percentile", .lname = "Latency Percentile", .type = FIO_OPT_FLOAT_LIST, - .off1 = td_var_offset(latency_percentile), + .off1 = offsetof(struct thread_options, latency_percentile), .help = "Percentile of IOs must be below latency_target", .def = "100", .maxlen = 1, @@ -3039,7 +3360,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "invalidate", .lname = "Cache invalidate", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(invalidate_cache), + .off1 = offsetof(struct thread_options, invalidate_cache), .help = "Invalidate buffer/page cache prior to running job", .def = "1", .category = FIO_OPT_C_IO, @@ -3049,7 +3370,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "sync", .lname = "Synchronous I/O", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(sync_io), + .off1 = offsetof(struct thread_options, sync_io), .help = "Use O_SYNC for buffered writes", .def = "0", .parent = "buffered", @@ -3061,8 +3382,8 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "create_serialize", .lname = "Create serialize", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(create_serialize), - .help = "Serialize creating of job files", + .off1 = offsetof(struct thread_options, create_serialize), + .help = "Serialize creation of job files", .def = "1", .category = FIO_OPT_C_FILE, .group = FIO_OPT_G_INVALID, @@ -3071,7 +3392,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "create_fsync", .lname = "Create fsync", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(create_fsync), + .off1 = offsetof(struct thread_options, create_fsync), .help = "fsync file after creation", .def = "1", .category = FIO_OPT_C_FILE, @@ -3081,7 +3402,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "create_on_open", .lname = "Create on open", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(create_on_open), + .off1 = offsetof(struct thread_options, create_on_open), .help = "Create files when they are opened for IO", .def = "0", .category = FIO_OPT_C_FILE, @@ -3089,8 +3410,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { }, { .name = "create_only", + .lname = "Create Only", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(create_only), + .off1 = offsetof(struct thread_options, create_only), .help = "Only perform file creation phase", .category = FIO_OPT_C_FILE, .def = "0", @@ -3099,7 +3421,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "allow_file_create", .lname = "Allow file create", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(allow_create), + .off1 = offsetof(struct thread_options, allow_create), .help = "Permit fio to create files, if they don't exist", .def = "1", .category = FIO_OPT_C_FILE, @@ -3109,7 +3431,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "allow_mounted_write", .lname = "Allow mounted write", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(allow_mounted_write), + .off1 = offsetof(struct thread_options, allow_mounted_write), .help = "Allow writes to a mounted partition", .def = "0", .category = FIO_OPT_C_FILE, @@ -3119,7 +3441,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "pre_read", .lname = "Pre-read files", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(pre_read), + .off1 = offsetof(struct thread_options, pre_read), .help = "Pre-read files before starting official testing", .def = "0", .category = FIO_OPT_C_FILE, @@ -3131,7 +3453,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "CPU mask", .type = FIO_OPT_INT, .cb = str_cpumask_cb, - .off1 = td_var_offset(cpumask), + .off1 = offsetof(struct thread_options, cpumask), .help = "CPU affinity mask", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_CRED, @@ -3141,7 +3463,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "CPUs allowed", .type = FIO_OPT_STR, .cb = str_cpus_allowed_cb, - .off1 = td_var_offset(cpumask), + .off1 = offsetof(struct thread_options, cpumask), .help = "Set CPUs allowed", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_CRED, @@ -3150,7 +3472,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "cpus_allowed_policy", .lname = "CPUs allowed distribution policy", .type = FIO_OPT_STR, - .off1 = td_var_offset(cpus_allowed_policy), + .off1 = offsetof(struct thread_options, cpus_allowed_policy), .help = "Distribution policy for cpus_allowed", .parent = "cpus_allowed", .prio = 1, @@ -3167,32 +3489,66 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_CRED, }, +#else + { + .name = "cpumask", + .lname = "CPU mask", + .type = FIO_OPT_UNSUPPORTED, + .help = "Your platform does not support CPU affinities", + }, + { + .name = "cpus_allowed", + .lname = "CPUs allowed", + .type = FIO_OPT_UNSUPPORTED, + .help = "Your platform does not support CPU affinities", + }, + { + .name = "cpus_allowed_policy", + .lname = "CPUs allowed distribution policy", + .type = FIO_OPT_UNSUPPORTED, + .help = "Your platform does not support CPU affinities", + }, #endif #ifdef CONFIG_LIBNUMA { .name = "numa_cpu_nodes", + .lname = "NUMA CPU Nodes", .type = FIO_OPT_STR, .cb = str_numa_cpunodes_cb, - .off1 = td_var_offset(numa_cpunodes), + .off1 = offsetof(struct thread_options, numa_cpunodes), .help = "NUMA CPU nodes bind", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_INVALID, }, { .name = "numa_mem_policy", + .lname = "NUMA Memory Policy", .type = FIO_OPT_STR, .cb = str_numa_mpol_cb, - .off1 = td_var_offset(numa_memnodes), + .off1 = offsetof(struct thread_options, numa_memnodes), .help = "NUMA memory policy setup", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_INVALID, }, +#else + { + .name = "numa_cpu_nodes", + .lname = "NUMA CPU Nodes", + .type = FIO_OPT_UNSUPPORTED, + .help = "Build fio with libnuma-dev(el) to enable this option", + }, + { + .name = "numa_mem_policy", + .lname = "NUMA Memory Policy", + .type = FIO_OPT_UNSUPPORTED, + .help = "Build fio with libnuma-dev(el) to enable this option", + }, #endif { .name = "end_fsync", .lname = "End fsync", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(end_fsync), + .off1 = offsetof(struct thread_options, end_fsync), .help = "Include fsync at the end of job", .def = "0", .category = FIO_OPT_C_FILE, @@ -3202,7 +3558,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "fsync_on_close", .lname = "Fsync on close", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(fsync_on_close), + .off1 = offsetof(struct thread_options, fsync_on_close), .help = "fsync files on close", .def = "0", .category = FIO_OPT_C_FILE, @@ -3212,12 +3568,22 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "unlink", .lname = "Unlink file", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(unlink), + .off1 = offsetof(struct thread_options, unlink), .help = "Unlink created files after job has completed", .def = "0", .category = FIO_OPT_C_FILE, .group = FIO_OPT_G_INVALID, }, + { + .name = "unlink_each_loop", + .lname = "Unlink file after each loop of a job", + .type = FIO_OPT_BOOL, + .off1 = offsetof(struct thread_options, unlink_each_loop), + .help = "Unlink created files after each loop in a job has completed", + .def = "0", + .category = FIO_OPT_C_FILE, + .group = FIO_OPT_G_INVALID, + }, { .name = "exitall", .lname = "Exit-all on terminate", @@ -3230,8 +3596,8 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { { .name = "exitall_on_error", .lname = "Exit-all on terminate in error", - .type = FIO_OPT_BOOL, - .off1 = td_var_offset(unlink), + .type = FIO_OPT_STR_SET, + .off1 = offsetof(struct thread_options, exitall_error), .help = "Terminate all jobs when one exits in error", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_PROCESS, @@ -3241,7 +3607,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Wait for previous", .alias = "wait_for_previous", .type = FIO_OPT_STR_SET, - .off1 = td_var_offset(stonewall), + .off1 = offsetof(struct thread_options, stonewall), .help = "Insert a hard barrier between this job and previous", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_PROCESS, @@ -3250,7 +3616,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "new_group", .lname = "New group", .type = FIO_OPT_STR_SET, - .off1 = td_var_offset(new_group), + .off1 = offsetof(struct thread_options, new_group), .help = "Mark the start of a new group (for reporting)", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_PROCESS, @@ -3259,7 +3625,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "thread", .lname = "Thread", .type = FIO_OPT_STR_SET, - .off1 = td_var_offset(use_thread), + .off1 = offsetof(struct thread_options, use_thread), .help = "Use threads instead of processes", #ifdef CONFIG_NO_SHM .def = "1", @@ -3270,8 +3636,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { }, { .name = "per_job_logs", + .lname = "Per Job Logs", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(per_job_logs), + .off1 = offsetof(struct thread_options, per_job_logs), .help = "Include job number in generated log files or not", .def = "1", .category = FIO_OPT_C_LOG, @@ -3280,8 +3647,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { { .name = "write_bw_log", .lname = "Write bandwidth log", - .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(bw_log_file), + .type = FIO_OPT_STR, + .off1 = offsetof(struct thread_options, bw_log_file), + .cb = str_write_bw_log_cb, .help = "Write log of bandwidth during run", .category = FIO_OPT_C_LOG, .group = FIO_OPT_G_INVALID, @@ -3289,8 +3657,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { { .name = "write_lat_log", .lname = "Write latency log", - .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(lat_log_file), + .type = FIO_OPT_STR, + .off1 = offsetof(struct thread_options, lat_log_file), + .cb = str_write_lat_log_cb, .help = "Write log of latency during run", .category = FIO_OPT_C_LOG, .group = FIO_OPT_G_INVALID, @@ -3298,8 +3667,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { { .name = "write_iops_log", .lname = "Write IOPS log", - .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(iops_log_file), + .type = FIO_OPT_STR, + .off1 = offsetof(struct thread_options, iops_log_file), + .cb = str_write_iops_log_cb, .help = "Write log of IOPS during run", .category = FIO_OPT_C_LOG, .group = FIO_OPT_G_INVALID, @@ -3308,17 +3678,49 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "log_avg_msec", .lname = "Log averaging (msec)", .type = FIO_OPT_INT, - .off1 = td_var_offset(log_avg_msec), + .off1 = offsetof(struct thread_options, log_avg_msec), .help = "Average bw/iops/lat logs over this period of time", .def = "0", .category = FIO_OPT_C_LOG, .group = FIO_OPT_G_INVALID, }, + { + .name = "log_hist_msec", + .lname = "Log histograms (msec)", + .type = FIO_OPT_INT, + .off1 = offsetof(struct thread_options, log_hist_msec), + .help = "Dump completion latency histograms at frequency of this time value", + .def = "0", + .category = FIO_OPT_C_LOG, + .group = FIO_OPT_G_INVALID, + }, + { + .name = "log_hist_coarseness", + .lname = "Histogram logs coarseness", + .type = FIO_OPT_INT, + .off1 = offsetof(struct thread_options, log_hist_coarseness), + .help = "Integer in range [0,6]. Higher coarseness outputs" + " fewer histogram bins per sample. The number of bins for" + " these are [1216, 608, 304, 152, 76, 38, 19] respectively.", + .def = "0", + .category = FIO_OPT_C_LOG, + .group = FIO_OPT_G_INVALID, + }, + { + .name = "write_hist_log", + .lname = "Write latency histogram logs", + .type = FIO_OPT_STR, + .off1 = offsetof(struct thread_options, hist_log_file), + .cb = str_write_hist_log_cb, + .help = "Write log of latency histograms during run", + .category = FIO_OPT_C_LOG, + .group = FIO_OPT_G_INVALID, + }, { .name = "log_max_value", .lname = "Log maximum instead of average", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(log_max), + .off1 = offsetof(struct thread_options, log_max), .help = "Log max sample in a window instead of average", .def = "0", .category = FIO_OPT_C_LOG, @@ -3328,7 +3730,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "log_offset", .lname = "Log offset of IO", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(log_offset), + .off1 = offsetof(struct thread_options, log_offset), .help = "Include offset of IO for each log entry", .def = "0", .category = FIO_OPT_C_LOG, @@ -3339,7 +3741,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "log_compression", .lname = "Log compression", .type = FIO_OPT_INT, - .off1 = td_var_offset(log_gz), + .off1 = offsetof(struct thread_options, log_gz), .help = "Log in compressed chunks of this size", .minval = 1024ULL, .maxval = 512 * 1024 * 1024ULL, @@ -3352,28 +3754,57 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Log Compression CPUs", .type = FIO_OPT_STR, .cb = str_log_cpus_allowed_cb, - .off1 = td_var_offset(log_gz_cpumask), + .off1 = offsetof(struct thread_options, log_gz_cpumask), .parent = "log_compression", .help = "Limit log compression to these CPUs", .category = FIO_OPT_C_LOG, .group = FIO_OPT_G_INVALID, }, +#else + { + .name = "log_compression_cpus", + .lname = "Log Compression CPUs", + .type = FIO_OPT_UNSUPPORTED, + .help = "Your platform does not support CPU affinities", + }, #endif { .name = "log_store_compressed", .lname = "Log store compressed", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(log_gz_store), + .off1 = offsetof(struct thread_options, log_gz_store), .help = "Store logs in a compressed format", .category = FIO_OPT_C_LOG, .group = FIO_OPT_G_INVALID, }, +#else + { + .name = "log_compression", + .lname = "Log compression", + .type = FIO_OPT_UNSUPPORTED, + .help = "Install libz-dev(el) to get compression support", + }, + { + .name = "log_store_compressed", + .lname = "Log store compressed", + .type = FIO_OPT_UNSUPPORTED, + .help = "Install libz-dev(el) to get compression support", + }, #endif + { + .name = "log_unix_epoch", + .lname = "Log epoch unix", + .type = FIO_OPT_BOOL, + .off1 = offsetof(struct thread_options, log_unix_epoch), + .help = "Use Unix time in log files", + .category = FIO_OPT_C_LOG, + .group = FIO_OPT_G_INVALID, + }, { .name = "block_error_percentiles", .lname = "Block error percentiles", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(block_error_hist), + .off1 = offsetof(struct thread_options, block_error_hist), .help = "Record trim block errors and make a histogram", .def = "0", .category = FIO_OPT_C_LOG, @@ -3383,7 +3814,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "bwavgtime", .lname = "Bandwidth average time", .type = FIO_OPT_INT, - .off1 = td_var_offset(bw_avg_time), + .off1 = offsetof(struct thread_options, bw_avg_time), .help = "Time window over which to calculate bandwidth" " (msec)", .def = "500", @@ -3397,7 +3828,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "iopsavgtime", .lname = "IOPS average time", .type = FIO_OPT_INT, - .off1 = td_var_offset(iops_avg_time), + .off1 = offsetof(struct thread_options, iops_avg_time), .help = "Time window over which to calculate IOPS (msec)", .def = "500", .parent = "write_iops_log", @@ -3410,7 +3841,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "group_reporting", .lname = "Group reporting", .type = FIO_OPT_STR_SET, - .off1 = td_var_offset(group_reporting), + .off1 = offsetof(struct thread_options, group_reporting), .help = "Do reporting on a per-group basis", .category = FIO_OPT_C_STAT, .group = FIO_OPT_G_INVALID, @@ -3419,7 +3850,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "zero_buffers", .lname = "Zero I/O buffers", .type = FIO_OPT_STR_SET, - .off1 = td_var_offset(zero_buffers), + .off1 = offsetof(struct thread_options, zero_buffers), .help = "Init IO buffers to all zeroes", .category = FIO_OPT_C_IO, .group = FIO_OPT_G_IO_BUF, @@ -3428,7 +3859,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "refill_buffers", .lname = "Refill I/O buffers", .type = FIO_OPT_STR_SET, - .off1 = td_var_offset(refill_buffers), + .off1 = offsetof(struct thread_options, refill_buffers), .help = "Refill IO buffers on every IO submit", .category = FIO_OPT_C_IO, .group = FIO_OPT_G_IO_BUF, @@ -3437,7 +3868,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "scramble_buffers", .lname = "Scramble I/O buffers", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(scramble_buffers), + .off1 = offsetof(struct thread_options, scramble_buffers), .help = "Slightly scramble buffers on every IO submit", .def = "1", .category = FIO_OPT_C_IO, @@ -3448,7 +3879,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Buffer pattern", .type = FIO_OPT_STR, .cb = str_buffer_pattern_cb, - .off1 = td_var_offset(buffer_pattern), + .off1 = offsetof(struct thread_options, buffer_pattern), .help = "Fill pattern for IO buffers", .category = FIO_OPT_C_IO, .group = FIO_OPT_G_IO_BUF, @@ -3458,7 +3889,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Buffer compression percentage", .type = FIO_OPT_INT, .cb = str_buffer_compress_cb, - .off1 = td_var_offset(compress_percentage), + .off1 = offsetof(struct thread_options, compress_percentage), .maxval = 100, .minval = 0, .help = "How compressible the buffer is (approximately)", @@ -3470,7 +3901,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "buffer_compress_chunk", .lname = "Buffer compression chunk size", .type = FIO_OPT_INT, - .off1 = td_var_offset(compress_chunk), + .off1 = offsetof(struct thread_options, compress_chunk), .parent = "buffer_compress_percentage", .hide = 1, .help = "Size of compressible region in buffer", @@ -3483,7 +3914,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Dedupe percentage", .type = FIO_OPT_INT, .cb = str_dedupe_cb, - .off1 = td_var_offset(dedupe_percentage), + .off1 = offsetof(struct thread_options, dedupe_percentage), .maxval = 100, .minval = 0, .help = "Percentage of buffers that are dedupable", @@ -3495,7 +3926,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "clat_percentiles", .lname = "Completion latency percentiles", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(clat_percentiles), + .off1 = offsetof(struct thread_options, clat_percentiles), .help = "Enable the reporting of completion latency percentiles", .def = "1", .category = FIO_OPT_C_STAT, @@ -3505,8 +3936,8 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "percentile_list", .lname = "Percentile list", .type = FIO_OPT_FLOAT_LIST, - .off1 = td_var_offset(percentile_list), - .off2 = td_var_offset(percentile_precision), + .off1 = offsetof(struct thread_options, percentile_list), + .off2 = offsetof(struct thread_options, percentile_precision), .help = "Specify a custom list of percentiles to report for " "completion latency and block errors", .def = "1:5:10:20:30:40:50:60:70:80:90:95:99:99.5:99.9:99.95:99.99", @@ -3522,12 +3953,19 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "disk_util", .lname = "Disk utilization", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(do_disk_util), + .off1 = offsetof(struct thread_options, do_disk_util), .help = "Log disk utilization statistics", .def = "1", .category = FIO_OPT_C_STAT, .group = FIO_OPT_G_INVALID, }, +#else + { + .name = "disk_util", + .lname = "Disk utilization", + .type = FIO_OPT_UNSUPPORTED, + .help = "Your platform does not support disk utilization", + }, #endif { .name = "gtod_reduce", @@ -3544,7 +3982,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "disable_lat", .lname = "Disable all latency stats", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(disable_lat), + .off1 = offsetof(struct thread_options, disable_lat), .help = "Disable latency numbers", .parent = "gtod_reduce", .hide = 1, @@ -3556,7 +3994,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "disable_clat", .lname = "Disable completion latency stats", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(disable_clat), + .off1 = offsetof(struct thread_options, disable_clat), .help = "Disable completion latency numbers", .parent = "gtod_reduce", .hide = 1, @@ -3568,7 +4006,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "disable_slat", .lname = "Disable submission latency stats", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(disable_slat), + .off1 = offsetof(struct thread_options, disable_slat), .help = "Disable submission latency numbers", .parent = "gtod_reduce", .hide = 1, @@ -3578,9 +4016,10 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { }, { .name = "disable_bw_measurement", + .alias = "disable_bw", .lname = "Disable bandwidth stats", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(disable_bw), + .off1 = offsetof(struct thread_options, disable_bw), .help = "Disable bandwidth logging", .parent = "gtod_reduce", .hide = 1, @@ -3592,7 +4031,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "gtod_cpu", .lname = "Dedicated gettimeofday() CPU", .type = FIO_OPT_INT, - .off1 = td_var_offset(gtod_cpu), + .off1 = offsetof(struct thread_options, gtod_cpu), .help = "Set up dedicated gettimeofday() thread on this CPU", .verify = gtod_cpu_verify, .category = FIO_OPT_C_GENERAL, @@ -3600,8 +4039,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { }, { .name = "unified_rw_reporting", + .lname = "Unified RW Reporting", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(unified_rw_rep), + .off1 = offsetof(struct thread_options, unified_rw_rep), .help = "Unify reporting across data direction", .def = "0", .category = FIO_OPT_C_GENERAL, @@ -3611,7 +4051,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "continue_on_error", .lname = "Continue on error", .type = FIO_OPT_STR, - .off1 = td_var_offset(continue_on_error), + .off1 = offsetof(struct thread_options, continue_on_error), .help = "Continue on non-fatal errors during IO", .def = "none", .category = FIO_OPT_C_GENERAL, @@ -3653,9 +4093,10 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { }, { .name = "ignore_error", + .lname = "Ignore Error", .type = FIO_OPT_STR, .cb = str_ignore_error_cb, - .off1 = td_var_offset(ignore_error_nr), + .off1 = offsetof(struct thread_options, ignore_error_nr), .help = "Set a specific list of errors to ignore", .parent = "rw", .category = FIO_OPT_C_GENERAL, @@ -3663,8 +4104,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { }, { .name = "error_dump", + .lname = "Error Dump", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(error_dump), + .off1 = offsetof(struct thread_options, error_dump), .def = "0", .help = "Dump info on each error", .category = FIO_OPT_C_GENERAL, @@ -3674,7 +4116,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "profile", .lname = "Profile", .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(profile), + .off1 = offsetof(struct thread_options, profile), .help = "Select a specific builtin performance test", .category = FIO_OPT_C_PROFILE, .group = FIO_OPT_G_INVALID, @@ -3683,7 +4125,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "cgroup", .lname = "Cgroup", .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(cgroup), + .off1 = offsetof(struct thread_options, cgroup), .help = "Add job to cgroup of this name", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_CGROUP, @@ -3692,7 +4134,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "cgroup_nodelete", .lname = "Cgroup no-delete", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(cgroup_nodelete), + .off1 = offsetof(struct thread_options, cgroup_nodelete), .help = "Do not delete cgroups after job completion", .def = "0", .parent = "cgroup", @@ -3703,7 +4145,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "cgroup_weight", .lname = "Cgroup weight", .type = FIO_OPT_INT, - .off1 = td_var_offset(cgroup_weight), + .off1 = offsetof(struct thread_options, cgroup_weight), .help = "Use given weight for cgroup", .minval = 100, .maxval = 1000, @@ -3715,7 +4157,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "uid", .lname = "User ID", .type = FIO_OPT_INT, - .off1 = td_var_offset(uid), + .off1 = offsetof(struct thread_options, uid), .help = "Run job with this user ID", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_CRED, @@ -3724,7 +4166,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "gid", .lname = "Group ID", .type = FIO_OPT_INT, - .off1 = td_var_offset(gid), + .off1 = offsetof(struct thread_options, gid), .help = "Run job with this group ID", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_CRED, @@ -3733,28 +4175,28 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "kb_base", .lname = "KB Base", .type = FIO_OPT_INT, - .off1 = td_var_offset(kb_base), + .off1 = offsetof(struct thread_options, kb_base), .prio = 1, .def = "1024", .posval = { { .ival = "1024", .oval = 1024, - .help = "Use 1024 as the K base", + .help = "Inputs invert IEC and SI prefixes (for compatibility); outputs prefer binary", }, { .ival = "1000", .oval = 1000, - .help = "Use 1000 as the K base", + .help = "Inputs use IEC and SI prefixes; outputs prefer SI", }, }, - .help = "How many bytes per KB for reporting (1000 or 1024)", + .help = "Unit prefix interpretation for quantities of data (IEC and SI)", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_INVALID, }, { .name = "unit_base", - .lname = "Base unit for reporting (Bits or Bytes)", + .lname = "Unit for quantities of data (Bits or Bytes)", .type = FIO_OPT_INT, - .off1 = td_var_offset(unit_base), + .off1 = offsetof(struct thread_options, unit_base), .prio = 1, .posval = { { .ival = "0", @@ -3778,7 +4220,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "hugepage-size", .lname = "Hugepage size", .type = FIO_OPT_INT, - .off1 = td_var_offset(hugepage_size), + .off1 = offsetof(struct thread_options, hugepage_size), .help = "When using hugepages, specify size of each page", .def = __fio_stringify(FIO_HUGE_PAGE), .interval = 1024 * 1024, @@ -3789,7 +4231,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "flow_id", .lname = "I/O flow ID", .type = FIO_OPT_INT, - .off1 = td_var_offset(flow_id), + .off1 = offsetof(struct thread_options, flow_id), .help = "The flow index ID to use", .def = "0", .category = FIO_OPT_C_IO, @@ -3799,7 +4241,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "flow", .lname = "I/O flow weight", .type = FIO_OPT_INT, - .off1 = td_var_offset(flow), + .off1 = offsetof(struct thread_options, flow), .help = "Weight for flow control of this job", .parent = "flow_id", .hide = 1, @@ -3811,7 +4253,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "flow_watermark", .lname = "I/O flow watermark", .type = FIO_OPT_INT, - .off1 = td_var_offset(flow_watermark), + .off1 = offsetof(struct thread_options, flow_watermark), .help = "High watermark for flow control. This option" " should be set to the same value for all threads" " with non-zero flow.", @@ -3825,7 +4267,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "flow_sleep", .lname = "I/O flow sleep", .type = FIO_OPT_INT, - .off1 = td_var_offset(flow_sleep), + .off1 = offsetof(struct thread_options, flow_sleep), .help = "How many microseconds to sleep after being held" " back by the flow control mechanism", .parent = "flow_id", @@ -3838,13 +4280,72 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "skip_bad", .lname = "Skip operations against bad blocks", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(skip_bad), + .off1 = offsetof(struct thread_options, skip_bad), .help = "Skip operations against known bad blocks.", .hide = 1, .def = "0", .category = FIO_OPT_C_IO, .group = FIO_OPT_G_MTD, }, + { + .name = "steadystate", + .lname = "Steady state threshold", + .alias = "ss", + .type = FIO_OPT_STR, + .off1 = offsetof(struct thread_options, ss_state), + .cb = str_steadystate_cb, + .help = "Define the criterion and limit to judge when a job has reached steady state", + .def = "iops_slope:0.01%", + .posval = { + { .ival = "iops", + .oval = FIO_SS_IOPS, + .help = "maximum mean deviation of IOPS measurements", + }, + { .ival = "iops_slope", + .oval = FIO_SS_IOPS_SLOPE, + .help = "slope calculated from IOPS measurements", + }, + { .ival = "bw", + .oval = FIO_SS_BW, + .help = "maximum mean deviation of bandwidth measurements", + }, + { + .ival = "bw_slope", + .oval = FIO_SS_BW_SLOPE, + .help = "slope calculated from bandwidth measurements", + }, + }, + .category = FIO_OPT_C_GENERAL, + .group = FIO_OPT_G_RUNTIME, + }, + { + .name = "steadystate_duration", + .lname = "Steady state duration", + .alias = "ss_dur", + .parent = "steadystate", + .type = FIO_OPT_STR_VAL_TIME, + .off1 = offsetof(struct thread_options, ss_dur), + .help = "Stop workload upon attaining steady state for specified duration", + .def = "0", + .is_seconds = 1, + .is_time = 1, + .category = FIO_OPT_C_GENERAL, + .group = FIO_OPT_G_RUNTIME, + }, + { + .name = "steadystate_ramp_time", + .lname = "Steady state ramp time", + .alias = "ss_ramp", + .parent = "steadystate", + .type = FIO_OPT_STR_VAL_TIME, + .off1 = offsetof(struct thread_options, ss_ramp_time), + .help = "Delay before initiation of data collection for steady state job termination testing", + .def = "0", + .is_seconds = 1, + .is_time = 1, + .category = FIO_OPT_C_GENERAL, + .group = FIO_OPT_G_RUNTIME, + }, { .name = NULL, }, @@ -4170,7 +4671,8 @@ static void show_closest_option(const char *opt) i++; } - if (best_option != -1 && string_distance_ok(name, best_distance)) + if (best_option != -1 && string_distance_ok(name, best_distance) && + fio_options[best_option].type != FIO_OPT_UNSUPPORTED) log_err("Did you mean %s?\n", fio_options[best_option].name); free(name); @@ -4187,7 +4689,7 @@ int fio_options_parse(struct thread_data *td, char **opts, int num_opts) for (ret = 0, i = 0, unknown = 0; i < num_opts; i++) { struct fio_option *o; int newret = parse_option(opts_copy[i], opts[i], fio_options, - &o, td, &td->opt_list); + &o, &td->o, &td->opt_list); if (!newret && o) fio_option_mark_set(&td->o, o); @@ -4240,7 +4742,7 @@ int fio_cmd_option_parse(struct thread_data *td, const char *opt, char *val) { int ret; - ret = parse_cmd_option(opt, val, fio_options, td, &td->opt_list); + ret = parse_cmd_option(opt, val, fio_options, &td->o, &td->opt_list); if (!ret) { struct fio_option *o; @@ -4262,7 +4764,7 @@ int fio_cmd_ioengine_option_parse(struct thread_data *td, const char *opt, void fio_fill_default_options(struct thread_data *td) { td->o.magic = OPT_MAGIC; - fill_default_options(td, fio_options); + fill_default_options(&td->o, fio_options); } int fio_show_option_help(const char *opt) @@ -4270,40 +4772,26 @@ int fio_show_option_help(const char *opt) return show_cmd_help(fio_options, opt); } -void options_mem_dupe(void *data, struct fio_option *options) -{ - struct fio_option *o; - char **ptr; - - for (o = &options[0]; o->name; o++) { - if (o->type != FIO_OPT_STR_STORE) - continue; - - ptr = td_var(data, o, 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, fio_options); + options_mem_dupe(fio_options, &td->o); 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); + options_mem_dupe(td->io_ops->options, td->eo); } } unsigned int fio_get_kb_base(void *data) { - struct thread_options *o = data; + struct thread_data *td = cb_data_to_td(data); + struct thread_options *o = &td->o; unsigned int kb_base = 0; /* @@ -4399,7 +4887,7 @@ void del_opt_posval(const char *optname, const char *ival) void fio_options_free(struct thread_data *td) { - options_free(fio_options, td); + options_free(fio_options, &td->o); if (td->eo && td->io_ops && td->io_ops->options) { options_free(td->io_ops->options, td->eo); free(td->eo);