X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=options.c;h=45a5b82be4f2789629dd1f5a6d32a7e6c24938de;hp=56d3e2b6027e3aa5215a48a0af078902308a2c30;hb=553f965256ab58b0e0958b0e367d8e63698f7a26;hpb=3c746856fd63f3f7624a34ea70e226a290d9cc01 diff --git a/options.c b/options.c index 56d3e2b6..45a5b82b 100644 --- a/options.c +++ b/options.c @@ -4,23 +4,21 @@ #include #include #include -#include -#include -#include #include #include #include "fio.h" #include "verify.h" #include "parse.h" -#include "lib/fls.h" #include "lib/pattern.h" #include "options.h" #include "optgroup.h" 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), @@ -54,14 +52,16 @@ static int bs_cmp(const void *p1, const void *p2) struct split { unsigned int nr; - unsigned int val1[100]; - unsigned int val2[100]; + unsigned int val1[ZONESPLIT_MAX]; + unsigned long long val2[ZONESPLIT_MAX]; }; static int split_parse_ddir(struct thread_options *o, struct split *split, - enum fio_ddir ddir, char *str) + enum fio_ddir ddir, char *str, bool absolute, + unsigned int max_splits) { - unsigned int i, perc; + unsigned long long perc; + unsigned int i; long long val; char *fname; @@ -78,31 +78,46 @@ static int split_parse_ddir(struct thread_options *o, struct split *split, if (perc_str) { *perc_str = '\0'; perc_str++; - perc = atoi(perc_str); - if (perc > 100) - perc = 100; - else if (!perc) + if (absolute) { + if (str_to_decimal(perc_str, &val, 1, o, 0, 0)) { + log_err("fio: split conversion failed\n"); + return 1; + } + perc = val; + } else { + perc = atoi(perc_str); + if (perc > 100) + perc = 100; + else if (!perc) + perc = -1U; + } + } else { + if (absolute) + perc = 0; + else perc = -1U; - } else - perc = -1U; + } if (str_to_decimal(fname, &val, 1, o, 0, 0)) { - log_err("fio: bssplit conversion failed\n"); + log_err("fio: split conversion failed\n"); return 1; } split->val1[i] = val; split->val2[i] = perc; i++; - if (i == 100) + if (i == max_splits) { + log_err("fio: hit max of %d split entries\n", i); break; + } } split->nr = i; return 0; } -static int bssplit_ddir(struct thread_options *o, enum fio_ddir ddir, char *str) +static int bssplit_ddir(struct thread_options *o, enum fio_ddir ddir, char *str, + bool data) { unsigned int i, perc, perc_missing; unsigned int max_bs, min_bs; @@ -110,7 +125,7 @@ static int bssplit_ddir(struct thread_options *o, enum fio_ddir ddir, char *str) memset(&split, 0, sizeof(split)); - if (split_parse_ddir(o, &split, ddir, str)) + if (split_parse_ddir(o, &split, ddir, str, data, BSSPLIT_MAX)) return 1; if (!split.nr) return 0; @@ -174,9 +189,10 @@ static int bssplit_ddir(struct thread_options *o, enum fio_ddir ddir, char *str) return 0; } -typedef int (split_parse_fn)(struct thread_options *, enum fio_ddir, char *); +typedef int (split_parse_fn)(struct thread_options *, enum fio_ddir, char *, bool); -static int str_split_parse(struct thread_data *td, char *str, split_parse_fn *fn) +static int str_split_parse(struct thread_data *td, char *str, + split_parse_fn *fn, bool data) { char *odir, *ddir; int ret = 0; @@ -185,37 +201,37 @@ static int str_split_parse(struct thread_data *td, char *str, split_parse_fn *fn if (odir) { ddir = strchr(odir + 1, ','); if (ddir) { - ret = fn(&td->o, DDIR_TRIM, ddir + 1); + ret = fn(&td->o, DDIR_TRIM, ddir + 1, data); if (!ret) *ddir = '\0'; } else { char *op; op = strdup(odir + 1); - ret = fn(&td->o, DDIR_TRIM, op); + ret = fn(&td->o, DDIR_TRIM, op, data); free(op); } if (!ret) - ret = fn(&td->o, DDIR_WRITE, odir + 1); + ret = fn(&td->o, DDIR_WRITE, odir + 1, data); if (!ret) { *odir = '\0'; - ret = fn(&td->o, DDIR_READ, str); + ret = fn(&td->o, DDIR_READ, str, data); } } else { char *op; op = strdup(str); - ret = fn(&td->o, DDIR_WRITE, op); + ret = fn(&td->o, DDIR_WRITE, op, data); free(op); if (!ret) { op = strdup(str); - ret = fn(&td->o, DDIR_TRIM, op); + ret = fn(&td->o, DDIR_TRIM, op, data); free(op); } if (!ret) - ret = fn(&td->o, DDIR_READ, str); + ret = fn(&td->o, DDIR_READ, str, data); } return ret; @@ -223,7 +239,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; @@ -232,7 +248,7 @@ static int str_bssplit_cb(void *data, const char *input) strip_blank_front(&str); strip_blank_end(str); - ret = str_split_parse(td, str, bssplit_ddir); + ret = str_split_parse(td, str, bssplit_ddir, false); if (parse_dryrun()) { int i; @@ -268,7 +284,8 @@ static int str2error(char *str) return 0; } -static int ignore_error_type(struct thread_data *td, int etype, char *str) +static int ignore_error_type(struct thread_data *td, enum error_type_bit etype, + char *str) { unsigned int i; int *error; @@ -280,7 +297,7 @@ static int ignore_error_type(struct thread_data *td, int etype, char *str) } td->o.ignore_error_nr[etype] = 4; - error = malloc(4 * sizeof(struct bssplit)); + error = calloc(4, sizeof(int)); i = 0; while ((fname = strsep(&str, ":")) != NULL) { @@ -304,8 +321,9 @@ static int ignore_error_type(struct thread_data *td, int etype, char *str) error[i] = -error[i]; } if (!error[i]) { - log_err("Unknown error %s, please use number value \n", + log_err("Unknown error %s, please use number value\n", fname); + td->o.ignore_error_nr[etype] = 0; free(error); return 1; } @@ -315,8 +333,10 @@ static int ignore_error_type(struct thread_data *td, int etype, char *str) td->o.continue_on_error |= 1 << etype; td->o.ignore_error_nr[etype] = i; td->o.ignore_error[etype] = error; - } else + } else { + td->o.ignore_error_nr[etype] = 0; free(error); + } return 0; @@ -324,9 +344,10 @@ 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; + int ret = 1; + enum error_type_bit type = 0; if (parse_dryrun()) return 0; @@ -352,7 +373,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 +407,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 +418,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 +428,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 +437,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 +475,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 +575,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 +585,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 +596,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 +610,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 +635,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,7 +744,7 @@ out: static int str_fst_cb(void *data, const char *str) { - struct thread_data *td = data; + struct thread_data *td = cb_data_to_td(data); double val; bool done = false; char *nr; @@ -803,7 +824,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; @@ -816,23 +837,15 @@ static int str_sfr_cb(void *data, const char *str) } #endif -static int zone_cmp(const void *p1, const void *p2) -{ - const struct zone_split *zsp1 = p1; - const struct zone_split *zsp2 = p2; - - return (int) zsp2->access_perc - (int) zsp1->access_perc; -} - static int zone_split_ddir(struct thread_options *o, enum fio_ddir ddir, - char *str) + char *str, bool absolute) { unsigned int i, perc, perc_missing, sperc, sperc_missing; struct split split; memset(&split, 0, sizeof(split)); - if (split_parse_ddir(o, &split, ddir, str)) + if (split_parse_ddir(o, &split, ddir, str, absolute, ZONESPLIT_MAX)) return 1; if (!split.nr) return 0; @@ -841,7 +854,10 @@ static int zone_split_ddir(struct thread_options *o, enum fio_ddir ddir, o->zone_split_nr[ddir] = split.nr; for (i = 0; i < split.nr; i++) { o->zone_split[ddir][i].access_perc = split.val1[i]; - o->zone_split[ddir][i].size_perc = split.val2[i]; + if (absolute) + o->zone_split[ddir][i].size = split.val2[i]; + else + o->zone_split[ddir][i].size_perc = split.val2[i]; } /* @@ -857,11 +873,12 @@ static int zone_split_ddir(struct thread_options *o, enum fio_ddir ddir, else perc += zsp->access_perc; - if (zsp->size_perc == (uint8_t) -1U) - sperc_missing++; - else - sperc += zsp->size_perc; - + if (!absolute) { + if (zsp->size_perc == (uint8_t) -1U) + sperc_missing++; + else + sperc += zsp->size_perc; + } } if (perc > 100 || sperc > 100) { @@ -903,20 +920,17 @@ static int zone_split_ddir(struct thread_options *o, enum fio_ddir ddir, } } - /* - * now sort based on percentages, for ease of lookup - */ - qsort(o->zone_split[ddir], o->zone_split_nr[ddir], sizeof(struct zone_split), zone_cmp); return 0; } static void __td_zone_gen_index(struct thread_data *td, enum fio_ddir ddir) { unsigned int i, j, sprev, aprev; + uint64_t sprev_sz; td->zone_state_index[ddir] = malloc(sizeof(struct zone_split_index) * 100); - sprev = aprev = 0; + sprev_sz = sprev = aprev = 0; for (i = 0; i < td->o.zone_split_nr[ddir]; i++) { struct zone_split *zsp = &td->o.zone_split[ddir][i]; @@ -925,10 +939,14 @@ static void __td_zone_gen_index(struct thread_data *td, enum fio_ddir ddir) zsi->size_perc = sprev + zsp->size_perc; zsi->size_perc_prev = sprev; + + zsi->size = sprev_sz + zsp->size; + zsi->size_prev = sprev_sz; } aprev += zsp->access_perc; sprev += zsp->size_perc; + sprev_sz += zsp->size; } } @@ -947,8 +965,10 @@ static void td_zone_gen_index(struct thread_data *td) __td_zone_gen_index(td, i); } -static int parse_zoned_distribution(struct thread_data *td, const char *input) +static int parse_zoned_distribution(struct thread_data *td, const char *input, + bool absolute) { + const char *pre = absolute ? "zoned_abs:" : "zoned:"; char *str, *p; int i, ret = 0; @@ -958,14 +978,14 @@ static int parse_zoned_distribution(struct thread_data *td, const char *input) strip_blank_end(str); /* We expect it to start like that, bail if not */ - if (strncmp(str, "zoned:", 6)) { + if (strncmp(str, pre, strlen(pre))) { log_err("fio: mismatch in zoned input <%s>\n", str); free(p); return 1; } - str += strlen("zoned:"); + str += strlen(pre); - ret = str_split_parse(td, str, zone_split_ddir); + ret = str_split_parse(td, str, zone_split_ddir, absolute); free(p); @@ -977,8 +997,15 @@ static int parse_zoned_distribution(struct thread_data *td, const char *input) for (j = 0; j < td->o.zone_split_nr[i]; j++) { struct zone_split *zsp = &td->o.zone_split[i][j]; - dprint(FD_PARSE, "\t%d: %u/%u\n", j, zsp->access_perc, - zsp->size_perc); + if (absolute) { + dprint(FD_PARSE, "\t%d: %u/%llu\n", j, + zsp->access_perc, + (unsigned long long) zsp->size); + } else { + dprint(FD_PARSE, "\t%d: %u/%u\n", j, + zsp->access_perc, + zsp->size_perc); + } } } @@ -1006,7 +1033,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; @@ -1017,7 +1044,9 @@ static int str_random_distribution_cb(void *data, const char *str) else if (td->o.random_distribution == FIO_RAND_DIST_GAUSS) val = 0.0; else if (td->o.random_distribution == FIO_RAND_DIST_ZONED) - return parse_zoned_distribution(td, str); + return parse_zoned_distribution(td, str, false); + else if (td->o.random_distribution == FIO_RAND_DIST_ZONED_ABS) + return parse_zoned_distribution(td, str, true); else return 0; @@ -1059,6 +1088,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 @@ -1151,7 +1252,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); @@ -1159,6 +1260,9 @@ static int str_filename_cb(void *data, const char *input) strip_blank_front(&str); strip_blank_end(str); + /* + * Ignore what we may already have from nrfiles option. + */ if (!td->files_index) td->o.nr_files = 0; @@ -1174,7 +1278,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; @@ -1205,7 +1309,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; @@ -1218,7 +1322,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 */ @@ -1229,8 +1333,17 @@ static int str_buffer_pattern_cb(void *data, const char *input) assert(ret != 0); td->o.buffer_pattern_bytes = ret; - if (!td->o.compress_percentage) + + /* + * If this job is doing any reading or has compression set, + * ensure that we refill buffers for writes or we could be + * invalidating the pattern through reads. + */ + if (!td->o.compress_percentage && !td_read(td)) td->o.refill_buffers = 0; + else + td->o.refill_buffers = 1; + td->o.scramble_buffers = 0; td->o.zero_buffers = 0; @@ -1239,7 +1352,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; @@ -1248,7 +1361,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; @@ -1258,7 +1371,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); @@ -1281,7 +1394,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; @@ -1290,28 +1403,123 @@ static int str_gtod_reduce_cb(void *data, int *il) td->o.disable_bw = !!val; td->o.clat_percentiles = !val; if (val) - td->tv_cache_mask = 63; + td->ts_cache_mask = 63; + + return 0; +} + +static int str_offset_cb(void *data, unsigned long long *__val) +{ + struct thread_data *td = cb_data_to_td(data); + unsigned long long v = *__val; + + if (parse_is_percent(v)) { + td->o.start_offset = 0; + td->o.start_offset_percent = -1ULL - v; + dprint(FD_PARSE, "SET start_offset_percent %d\n", + td->o.start_offset_percent); + } else + td->o.start_offset = v; return 0; } 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)) { td->o.size = 0; td->o.size_percent = -1ULL - v; + dprint(FD_PARSE, "SET size_percent %d\n", + td->o.size_percent); } else td->o.size = v; 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; +} + +/* + * str is supposed to be a substring of the strdup'd original string, + * and is valid only if it's a regular file path. + * This function keeps the pointer to the path as needed later. + * + * "external:/path/to/so\0" <- original pointer updated with strdup'd + * "external\0" <- above pointer after parsed, i.e. ->ioengine + * "/path/to/so\0" <- str argument, i.e. ->ioengine_so_path + */ +static int str_ioengine_external_cb(void *data, const char *str) +{ + struct thread_data *td = cb_data_to_td(data); + struct stat sb; + char *p; + + if (!str) { + log_err("fio: null external ioengine path\n"); + return 1; + } + + p = (char *)str; /* str is mutable */ + strip_blank_front(&p); + strip_blank_end(p); + + if (stat(p, &sb) || !S_ISREG(sb.st_mode)) { + log_err("fio: invalid external ioengine path \"%s\"\n", p); + return 1; + } + + td->o.ioengine_so_path = p; + 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" @@ -1325,7 +1533,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" @@ -1345,7 +1553,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, @@ -1354,7 +1562,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, @@ -1363,7 +1571,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, @@ -1372,7 +1580,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", @@ -1383,7 +1591,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, @@ -1393,7 +1601,7 @@ 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", @@ -1404,7 +1612,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "unique_filename", .lname = "Unique Filename", .type = FIO_OPT_BOOL, - .off1 = td_var_offset(unique_filename), + .off1 = offsetof(struct thread_options, unique_filename), .help = "For network clients, prefix file with source IP", .def = "1", .category = FIO_OPT_C_FILE, @@ -1414,7 +1622,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .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", @@ -1442,7 +1650,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, @@ -1454,7 +1662,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, @@ -1507,7 +1715,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, @@ -1528,7 +1736,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, @@ -1652,16 +1860,31 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { }, #endif +#ifdef CONFIG_LINUX_DEVDAX + { .ival = "dev-dax", + .help = "DAX Device based IO engine", + }, +#endif + { + .ival = "filecreate", + .help = "File creation engine", + }, { .ival = "external", .help = "Load external engine (append name)", + .cb = str_ioengine_external_cb, }, +#ifdef CONFIG_LIBPMEM + { .ival = "libpmem", + .help = "NVML libpmem based IO engine", + }, +#endif }, }, { .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, @@ -1674,7 +1897,7 @@ 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, @@ -1688,7 +1911,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, @@ -1702,7 +1925,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, @@ -1715,7 +1938,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, @@ -1723,11 +1946,22 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .category = FIO_OPT_C_IO, .group = FIO_OPT_G_IO_BASIC, }, + { + .name = "serialize_overlap", + .lname = "Serialize overlap", + .off1 = offsetof(struct thread_options, serialize_overlap), + .type = FIO_OPT_BOOL, + .help = "Wait for in-flight IOs that collide to complete", + .parent = "iodepth", + .def = "0", + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_IO_BASIC, + }, { .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, @@ -1748,7 +1982,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, @@ -1759,7 +1993,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_size), + .help = "Total size of I/O to be performed", .interval = 1024 * 1024, .category = FIO_OPT_C_IO, .group = FIO_OPT_G_INVALID, @@ -1769,7 +2004,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, @@ -1779,8 +2014,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, @@ -1791,7 +2026,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, @@ -1802,18 +2037,30 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "IO offset", .alias = "fileoffset", .type = FIO_OPT_STR_VAL, - .off1 = td_var_offset(start_offset), + .cb = str_offset_cb, + .off1 = offsetof(struct thread_options, start_offset), .help = "Start IO from this offset", .def = "0", .interval = 1024 * 1024, .category = FIO_OPT_C_IO, .group = FIO_OPT_G_INVALID, }, + { + .name = "offset_align", + .lname = "IO offset alignment", + .type = FIO_OPT_INT, + .off1 = offsetof(struct thread_options, start_offset_align), + .help = "Start IO from this offset alignment", + .def = "0", + .interval = 512, + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_INVALID, + }, { .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, @@ -1826,7 +2073,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, @@ -1837,12 +2084,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, @@ -1854,9 +2101,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", @@ -1870,12 +2117,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", @@ -1889,7 +2136,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, @@ -1901,7 +2148,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, @@ -1912,7 +2159,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", @@ -1923,7 +2170,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", @@ -1935,7 +2182,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", @@ -1946,7 +2193,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, }, @@ -1954,7 +2201,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, @@ -1966,7 +2213,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, @@ -1978,7 +2225,7 @@ 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 = { @@ -2003,7 +2250,7 @@ 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", @@ -2028,7 +2275,10 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .oval = FIO_RAND_DIST_ZONED, .help = "Zoned random distribution", }, - + { .ival = "zoned_abs", + .oval = FIO_RAND_DIST_ZONED_ABS, + .help = "Zoned absolute random distribution", + }, }, .category = FIO_OPT_C_IO, .group = FIO_OPT_G_RANDOM, @@ -2037,9 +2287,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", @@ -2059,7 +2309,7 @@ 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, @@ -2070,7 +2320,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, @@ -2081,7 +2331,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, @@ -2091,7 +2341,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, @@ -2109,9 +2359,13 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .oval = FIO_FSERVICE_PARETO, .help = "Pareto randomized", }, + { .ival = "normal", + .oval = FIO_FSERVICE_GAUSS, + .help = "Normal (Gaussian) randomized", + }, { .ival = "gauss", .oval = FIO_FSERVICE_GAUSS, - .help = "Normal (guassian) distribution", + .help = "Alias for normal", }, { .ival = "roundrobin", .oval = FIO_FSERVICE_RR, @@ -2125,14 +2379,14 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .parent = "nrfiles", .hide = 1, }, -#ifdef CONFIG_POSIX_FALLOCATE +#ifdef FIO_HAVE_ANY_FALLOCATE { .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", + .def = "native", .category = FIO_OPT_C_FILE, .group = FIO_OPT_G_INVALID, .posval = { @@ -2140,10 +2394,16 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .oval = FIO_FALLOCATE_NONE, .help = "Do not pre-allocate space", }, + { .ival = "native", + .oval = FIO_FALLOCATE_NATIVE, + .help = "Use native pre-allocation if possible", + }, +#ifdef CONFIG_POSIX_FALLOCATE { .ival = "posix", .oval = FIO_FALLOCATE_POSIX, .help = "Use posix_fallocate()", }, +#endif #ifdef CONFIG_LINUX_FALLOCATE { .ival = "keep", .oval = FIO_FALLOCATE_KEEP_SIZE, @@ -2155,53 +2415,55 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .oval = FIO_FALLOCATE_NONE, .help = "Alias for 'none'", }, +#ifdef CONFIG_POSIX_FALLOCATE { .ival = "1", .oval = FIO_FALLOCATE_POSIX, .help = "Alias for 'posix'", }, +#endif }, }, -#else /* CONFIG_POSIX_FALLOCATE */ +#else /* FIO_HAVE_ANY_FALLOCATE */ { .name = "fallocate", .lname = "Fallocate", .type = FIO_OPT_UNSUPPORTED, .help = "Your platform does not support fallocate", }, -#endif /* CONFIG_POSIX_FALLOCATE */ +#endif /* FIO_HAVE_ANY_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/madvise", + }, + { .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, .group = FIO_OPT_G_INVALID, }, -#ifdef FIO_HAVE_STREAMID - { - .name = "fadvise_stream", - .lname = "Fadvise stream", - .type = FIO_OPT_INT, - .off1 = td_var_offset(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, @@ -2212,7 +2474,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, @@ -2223,7 +2485,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, @@ -2254,7 +2516,7 @@ 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, @@ -2271,7 +2533,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .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", @@ -2282,7 +2544,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, @@ -2292,7 +2554,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", @@ -2304,7 +2566,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, @@ -2314,7 +2576,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, @@ -2325,7 +2587,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, @@ -2336,8 +2598,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, @@ -2350,7 +2612,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, @@ -2362,7 +2624,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, @@ -2371,7 +2633,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, @@ -2380,7 +2642,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, @@ -2392,7 +2654,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, @@ -2423,7 +2685,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, @@ -2458,6 +2720,12 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .oval = MEM_MMAPHUGE, .help = "Like mmap, but use huge pages", }, +#endif +#ifdef CONFIG_CUDA + { .ival = "cudamalloc", + .oval = MEM_CUDA_MALLOC, + .help = "Allocate GPU device memory for GPUDirect RDMA", + }, #endif }, }, @@ -2466,7 +2734,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", @@ -2479,7 +2747,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, @@ -2529,6 +2797,22 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .oval = VERIFY_SHA512, .help = "Use sha512 checksums for verification", }, + { .ival = "sha3-224", + .oval = VERIFY_SHA3_224, + .help = "Use sha3-224 checksums for verification", + }, + { .ival = "sha3-256", + .oval = VERIFY_SHA3_256, + .help = "Use sha3-256 checksums for verification", + }, + { .ival = "sha3-384", + .oval = VERIFY_SHA3_384, + .help = "Use sha3-384 checksums for verification", + }, + { .ival = "sha3-512", + .oval = VERIFY_SHA3_512, + .help = "Use sha3-512 checksums for verification", + }, { .ival = "xxhash", .oval = VERIFY_XXHASH, .help = "Use xxhash checksums for verification", @@ -2556,7 +2840,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", @@ -2568,7 +2852,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", @@ -2580,7 +2864,7 @@ 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, @@ -2593,7 +2877,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", @@ -2607,7 +2891,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, @@ -2619,7 +2903,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, @@ -2630,7 +2914,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", @@ -2642,7 +2926,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", @@ -2654,7 +2938,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", @@ -2666,7 +2950,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, @@ -2677,7 +2961,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, @@ -2690,7 +2974,7 @@ 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, @@ -2708,7 +2992,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { { .name = "experimental_verify", .lname = "Experimental Verify", - .off1 = td_var_offset(experimental_verify), + .off1 = offsetof(struct thread_options, experimental_verify), .type = FIO_OPT_BOOL, .help = "Enable experimental verification", .parent = "verify", @@ -2718,7 +3002,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", @@ -2728,7 +3012,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", @@ -2741,10 +3025,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, @@ -2756,8 +3040,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", @@ -2768,7 +3052,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, @@ -2780,7 +3064,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, @@ -2818,7 +3102,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .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, @@ -2827,7 +3111,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, @@ -2836,7 +3120,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, @@ -2848,7 +3132,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", @@ -2859,7 +3143,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", @@ -2870,7 +3154,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, @@ -2881,7 +3165,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, @@ -2890,7 +3174,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, @@ -2900,7 +3184,7 @@ 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, @@ -2917,7 +3201,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .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, @@ -2928,7 +3212,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, @@ -2939,7 +3223,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, @@ -2950,7 +3234,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, @@ -2962,7 +3246,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", @@ -2976,7 +3260,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", @@ -2996,10 +3280,10 @@ 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, + .minval = -20, + .maxval = 19, .def = "0", .interval = 1, .category = FIO_OPT_C_GENERAL, @@ -3010,7 +3294,7 @@ 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 = IOPRIO_MIN_PRIO, .maxval = IOPRIO_MAX_PRIO, @@ -3034,7 +3318,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .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 = IOPRIO_MIN_PRIO_CLASS, .maxval = IOPRIO_MAX_PRIO_CLASS, @@ -3054,7 +3338,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .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, @@ -3065,7 +3349,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, @@ -3078,7 +3362,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", @@ -3090,9 +3374,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, @@ -3102,9 +3386,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, @@ -3115,9 +3399,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, @@ -3127,9 +3411,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, @@ -3140,7 +3424,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, @@ -3163,7 +3447,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", @@ -3171,11 +3455,21 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .category = FIO_OPT_C_IO, .group = FIO_OPT_G_RATE, }, + { + .name = "rate_ignore_thinktime", + .lname = "Rate ignore thinktime", + .type = FIO_OPT_BOOL, + .off1 = offsetof(struct thread_options, rate_ign_think), + .help = "Rated IO ignores thinktime settings", + .parent = "rate", + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_RATE, + }, { .name = "max_latency", - .lname = "Max Latency", - .type = FIO_OPT_INT, - .off1 = td_var_offset(max_latency), + .lname = "Max Latency (usec)", + .type = FIO_OPT_STR_VAL_TIME, + .off1 = offsetof(struct thread_options, max_latency), .help = "Maximum tolerated IO latency (usec)", .is_time = 1, .category = FIO_OPT_C_IO, @@ -3185,7 +3479,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, @@ -3195,7 +3489,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, @@ -3205,7 +3499,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, @@ -3218,7 +3512,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, @@ -3228,7 +3522,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", @@ -3236,12 +3530,40 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .category = FIO_OPT_C_IO, .group = FIO_OPT_G_IO_TYPE, }, +#ifdef FIO_HAVE_WRITE_HINT + { + .name = "write_hint", + .lname = "Write hint", + .type = FIO_OPT_STR, + .off1 = offsetof(struct thread_options, write_hint), + .help = "Set expected write life time", + .category = FIO_OPT_C_ENGINE, + .group = FIO_OPT_G_INVALID, + .posval = { + { .ival = "none", + .oval = RWH_WRITE_LIFE_NONE, + }, + { .ival = "short", + .oval = RWH_WRITE_LIFE_SHORT, + }, + { .ival = "medium", + .oval = RWH_WRITE_LIFE_MEDIUM, + }, + { .ival = "long", + .oval = RWH_WRITE_LIFE_LONG, + }, + { .ival = "extreme", + .oval = RWH_WRITE_LIFE_EXTREME, + }, + }, + }, +#endif { .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, @@ -3250,7 +3572,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, @@ -3260,7 +3582,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, @@ -3270,7 +3592,7 @@ 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", @@ -3279,7 +3601,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, @@ -3289,7 +3611,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, @@ -3299,7 +3621,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, @@ -3311,7 +3633,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, @@ -3321,7 +3643,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, @@ -3330,7 +3652,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, @@ -3373,7 +3695,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .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, @@ -3383,7 +3705,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .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, @@ -3401,12 +3723,24 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .type = FIO_OPT_UNSUPPORTED, .help = "Build fio with libnuma-dev(el) to enable this option", }, +#endif +#ifdef CONFIG_CUDA + { + .name = "gpu_dev_id", + .lname = "GPU device ID", + .type = FIO_OPT_INT, + .off1 = offsetof(struct thread_options, gpu_dev_id), + .help = "Set GPU device ID for GPUDirect RDMA", + .def = "0", + .category = FIO_OPT_C_GENERAL, + .group = FIO_OPT_G_INVALID, + }, #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, @@ -3416,7 +3750,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, @@ -3426,12 +3760,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", @@ -3445,7 +3789,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "exitall_on_error", .lname = "Exit-all on terminate in error", .type = FIO_OPT_STR_SET, - .off1 = td_var_offset(exitall_error), + .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, @@ -3455,7 +3799,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, @@ -3464,7 +3808,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, @@ -3473,7 +3817,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", @@ -3486,7 +3830,7 @@ 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, @@ -3495,8 +3839,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, @@ -3504,8 +3849,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, @@ -3513,8 +3859,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, @@ -3523,7 +3870,7 @@ 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, @@ -3533,7 +3880,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "log_hist_msec", .lname = "Log histograms (msec)", .type = FIO_OPT_INT, - .off1 = td_var_offset(log_hist_msec), + .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, @@ -3543,7 +3890,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "log_hist_coarseness", .lname = "Histogram logs coarseness", .type = FIO_OPT_INT, - .off1 = td_var_offset(log_hist_coarseness), + .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.", @@ -3554,8 +3901,9 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { { .name = "write_hist_log", .lname = "Write latency histogram logs", - .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(hist_log_file), + .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, @@ -3564,7 +3912,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .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, @@ -3574,7 +3922,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, @@ -3585,7 +3933,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, @@ -3598,7 +3946,7 @@ 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, @@ -3616,7 +3964,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .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, @@ -3635,11 +3983,20 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .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, @@ -3649,7 +4006,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", @@ -3663,7 +4020,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", @@ -3676,16 +4033,26 @@ 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, }, + { + .name = "stats", + .lname = "Stats", + .type = FIO_OPT_BOOL, + .off1 = offsetof(struct thread_options, stats), + .help = "Enable collection of stats", + .def = "1", + .category = FIO_OPT_C_STAT, + .group = FIO_OPT_G_INVALID, + }, { .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, @@ -3694,7 +4061,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, @@ -3703,7 +4070,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, @@ -3714,7 +4081,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, @@ -3724,7 +4091,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)", @@ -3736,10 +4103,11 @@ 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", + .def = "512", .interval = 256, .category = FIO_OPT_C_IO, .group = FIO_OPT_G_IO_BUF, @@ -3749,7 +4117,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", @@ -3761,9 +4129,21 @@ 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", + .inverse = "lat_percentiles", + .category = FIO_OPT_C_STAT, + .group = FIO_OPT_G_INVALID, + }, + { + .name = "lat_percentiles", + .lname = "IO latency percentiles", + .type = FIO_OPT_BOOL, + .off1 = offsetof(struct thread_options, lat_percentiles), + .help = "Enable the reporting of IO latency percentiles", + .def = "0", + .inverse = "clat_percentiles", .category = FIO_OPT_C_STAT, .group = FIO_OPT_G_INVALID, }, @@ -3771,8 +4151,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", @@ -3782,13 +4162,26 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .category = FIO_OPT_C_STAT, .group = FIO_OPT_G_INVALID, }, + { + .name = "significant_figures", + .lname = "Significant figures", + .type = FIO_OPT_INT, + .off1 = offsetof(struct thread_options, sig_figs), + .maxval = 10, + .minval = 1, + .help = "Significant figures for output-format set to normal", + .def = "4", + .interval = 1, + .category = FIO_OPT_C_STAT, + .group = FIO_OPT_G_INVALID, + }, #ifdef FIO_HAVE_DISK_UTIL { .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, @@ -3817,7 +4210,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, @@ -3829,7 +4222,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, @@ -3841,7 +4234,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, @@ -3851,9 +4244,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, @@ -3865,7 +4259,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, @@ -3875,7 +4269,7 @@ 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, @@ -3885,7 +4279,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, @@ -3930,7 +4324,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .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, @@ -3940,7 +4334,7 @@ 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, @@ -3950,7 +4344,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, @@ -3959,7 +4353,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, @@ -3968,7 +4362,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", @@ -3979,7 +4373,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, @@ -3991,7 +4385,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, @@ -4000,7 +4394,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, @@ -4009,28 +4403,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", @@ -4054,7 +4448,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, @@ -4065,7 +4459,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, @@ -4075,7 +4469,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, @@ -4087,7 +4481,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.", @@ -4101,7 +4495,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", @@ -4111,15 +4505,63 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .group = FIO_OPT_G_IO_FLOW, }, { - .name = "skip_bad", - .lname = "Skip operations against bad blocks", - .type = FIO_OPT_BOOL, - .off1 = td_var_offset(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, @@ -4464,7 +4906,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); @@ -4517,7 +4959,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; @@ -4539,7 +4981,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) @@ -4547,40 +4989,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; /* @@ -4676,7 +5104,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);