X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=options.c;h=ab6e399db5208d81689c0ec9752fa0a7e4adeda1;hp=3a3321f7b9d6a3d9b0b5bbf4b8da1d283182c3e3;hb=c95f94046bc8c659b0fab718bd4029fadc87be5a;hpb=b26317c97b8bcca7cc9144ea6e1d4ed753f2311a diff --git a/options.c b/options.c index 3a3321f7..ab6e399d 100644 --- a/options.c +++ b/options.c @@ -98,11 +98,11 @@ static int bssplit_ddir(struct thread_options *o, int ddir, char *str) if (perc > 100) perc = 100; else if (!perc) - perc = -1; + perc = -1U; } else - perc = -1; + perc = -1U; - if (str_to_decimal(fname, &val, 1, o, 0)) { + if (str_to_decimal(fname, &val, 1, o, 0, 0)) { log_err("fio: bssplit conversion failed\n"); free(bssplit); return 1; @@ -127,26 +127,29 @@ static int bssplit_ddir(struct thread_options *o, int ddir, char *str) for (i = 0; i < o->bssplit_nr[ddir]; i++) { struct bssplit *bsp = &bssplit[i]; - if (bsp->perc == (unsigned char) -1) + if (bsp->perc == -1U) perc_missing++; else perc += bsp->perc; } - if (perc > 100) { + if (perc > 100 && perc_missing > 1) { log_err("fio: bssplit percentages add to more than 100%%\n"); free(bssplit); return 1; } + /* * If values didn't have a percentage set, divide the remains between * them. */ if (perc_missing) { + if (perc_missing == 1 && o->bssplit_nr[ddir] == 1) + perc = 100; for (i = 0; i < o->bssplit_nr[ddir]; i++) { struct bssplit *bsp = &bssplit[i]; - if (bsp->perc == (unsigned char) -1) + if (bsp->perc == -1U) bsp->perc = (100 - perc) / perc_missing; } } @@ -339,7 +342,7 @@ static int str_rw_cb(void *data, const char *str) else { long long val; - if (str_to_decimal(nr, &val, 1, o, 0)) { + if (str_to_decimal(nr, &val, 1, o, 0, 0)) { log_err("fio: rw postfix parsing failed\n"); free(nr); return 1; @@ -450,7 +453,6 @@ static int str_cpumask_cb(void *data, unsigned long long *val) } } - td->o.cpumask_set = 1; return 0; } @@ -517,36 +519,24 @@ static int set_cpus_allowed(struct thread_data *td, os_cpu_mask_t *mask, } free(p); - if (!ret) - td->o.cpumask_set = 1; return ret; } static int str_cpus_allowed_cb(void *data, const char *input) { struct thread_data *td = data; - int ret; if (parse_dryrun()) return 0; - ret = set_cpus_allowed(td, &td->o.cpumask, input); - if (!ret) - td->o.cpumask_set = 1; - - return ret; + return set_cpus_allowed(td, &td->o.cpumask, input); } static int str_verify_cpus_allowed_cb(void *data, const char *input) { struct thread_data *td = data; - int ret; - ret = set_cpus_allowed(td, &td->o.verify_cpumask, input); - if (!ret) - td->o.verify_cpumask_set = 1; - - return ret; + return set_cpus_allowed(td, &td->o.verify_cpumask, input); } #endif @@ -573,7 +563,6 @@ static int str_numa_cpunodes_cb(void *data, char *input) numa_free_nodemask(verify_bitmask); td->o.numa_cpunodes = strdup(input); - td->o.numa_cpumask_set = 1; return 0; } @@ -672,7 +661,7 @@ static int str_numa_mpol_cb(void *data, char *input) } td->o.numa_memnodes = strdup(nodelist); numa_free_nodemask(verify_bitmask); - + break; case MPOL_LOCAL: case MPOL_DEFAULT: @@ -680,9 +669,7 @@ static int str_numa_mpol_cb(void *data, char *input) break; } - td->o.numa_memmask_set = 1; return 0; - out: return 1; } @@ -728,14 +715,14 @@ static int str_random_distribution_cb(void *data, const char *str) return 0; if (td->o.random_distribution == FIO_RAND_DIST_ZIPF) - val = 1.1; + val = FIO_DEF_ZIPF; else if (td->o.random_distribution == FIO_RAND_DIST_PARETO) - val = 0.2; + val = FIO_DEF_PARETO; else return 0; nr = get_opt_postfix(str); - if (nr && !str_to_float(nr, &val)) { + if (nr && !str_to_float(nr, &val, 0)) { log_err("fio: random postfix parsing failed\n"); free(nr); return 1; @@ -897,18 +884,6 @@ out: return ret; } -static int str_lockfile_cb(void *data, const char fio_unused *str) -{ - struct thread_data *td = data; - - if (td->files_index) { - log_err("fio: lockfile= option must precede filename=\n"); - return 1; - } - - return 0; -} - static int str_opendir_cb(void *data, const char fio_unused *str) { struct thread_data *td = data; @@ -930,6 +905,29 @@ static int pattern_cb(char *pattern, unsigned int max_size, uint32_t pattern_length; char *loc1, *loc2; + /* + * Check if it's a string input + */ + loc1 = strchr(input, '\"'); + if (loc1) { + do { + loc1++; + if (*loc1 == '\0' || *loc1 == '\"') + break; + + pattern[i] = *loc1; + i++; + } while (i < max_size); + + if (!i) + return 1; + + goto fill; + } + + /* + * No string, find out if it's decimal or hexidecimal + */ loc1 = strstr(input, "0x"); loc2 = strstr(input, "0X"); if (loc1 || loc2) @@ -966,6 +964,7 @@ static int pattern_cb(char *pattern, unsigned int max_size, * Fill the pattern all the way to the end. This greatly reduces * the number of memcpy's we have to do when verifying the IO. */ +fill: pattern_length = i; while (i > 1 && i * 2 <= max_size) { memcpy(&pattern[i], &pattern[0], i); @@ -985,8 +984,8 @@ static int pattern_cb(char *pattern, unsigned int max_size, if (i == 1) { /* - * The code in verify_io_u_pattern assumes a single byte pattern - * fills the whole verify pattern buffer. + * The code in verify_io_u_pattern assumes a single byte + * pattern fills the whole verify pattern buffer. */ memset(pattern, pattern[0], max_size); } @@ -1003,10 +1002,14 @@ static int str_buffer_pattern_cb(void *data, const char *input) ret = pattern_cb(td->o.buffer_pattern, MAX_PATTERN_SIZE, input, &td->o.buffer_pattern_bytes); - if (!ret) { - td->o.refill_buffers = 0; + if (!ret && td->o.buffer_pattern_bytes) { + if (!td->o.compress_percentage) + td->o.refill_buffers = 0; td->o.scramble_buffers = 0; td->o.zero_buffers = 0; + } else { + log_err("fio: failed parsing pattern `%s`\n", input); + ret = 1; } return ret; @@ -1021,6 +1024,16 @@ static int str_buffer_compress_cb(void *data, unsigned long long *il) return 0; } +static int str_dedupe_cb(void *data, unsigned long long *il) +{ + struct thread_data *td = data; + + td->flags |= TD_F_COMPRESS; + td->o.dedupe_percentage = *il; + td->o.refill_buffers = 1; + return 0; +} + static int str_verify_pattern_cb(void *data, const char *input) { struct thread_data *td = data; @@ -1054,16 +1067,6 @@ static int str_gtod_reduce_cb(void *data, int *il) return 0; } -static int str_gtod_cpu_cb(void *data, long long *il) -{ - struct thread_data *td = data; - int val = *il; - - td->o.gtod_cpu = val; - td->o.gtod_offload = 1; - return 0; -} - static int str_size_cb(void *data, unsigned long long *__val) { struct thread_data *td = data; @@ -1328,7 +1331,6 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .parent = "filename", .hide = 0, .def = "none", - .cb = str_lockfile_cb, .category = FIO_OPT_C_FILE, .group = FIO_OPT_G_FILENAME, .posval = { @@ -1541,7 +1543,11 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .help = "Glusterfs libgfapi(async) based engine" }, #endif - +#ifdef CONFIG_LIBHDFS + { .ival = "libhdfs", + .help = "Hadoop Distributed Filesystem (HDFS) engine" + }, +#endif { .ival = "external", .help = "Load external engine (append name)", }, @@ -1605,14 +1611,16 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Size", .type = FIO_OPT_STR_VAL, .cb = str_size_cb, + .off1 = td_var_offset(size), .help = "Total size of device or files", .interval = 1024 * 1024, .category = FIO_OPT_C_IO, .group = FIO_OPT_G_INVALID, }, { - .name = "io_limit", - .lname = "IO Limit", + .name = "io_size", + .alias = "io_limit", + .lname = "IO Size", .type = FIO_OPT_STR_VAL, .off1 = td_var_offset(io_limit), .interval = 1024 * 1024, @@ -1682,7 +1690,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Number of IOs to perform", .type = FIO_OPT_STR_VAL, .off1 = td_var_offset(number_ios), - .help = "Force job completion of this number of IOs", + .help = "Force job completion after this number of IOs", .def = "0", .category = FIO_OPT_C_IO, .group = FIO_OPT_G_INVALID, @@ -1744,6 +1752,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), .help = "Set a specific mix of block sizes", .parent = "rw", .hide = 1, @@ -1767,7 +1776,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .lname = "Block size division is seq/random (not read/write)", .type = FIO_OPT_BOOL, .off1 = td_var_offset(bs_is_seq_rand), - .help = "Consider any blocksize setting to be sequential,ramdom", + .help = "Consider any blocksize setting to be sequential,random", .def = "0", .parent = "blocksize", .category = FIO_OPT_C_IO, @@ -1798,12 +1807,8 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { { .name = "use_os_rand", .lname = "Use OS random", - .type = FIO_OPT_BOOL, - .off1 = td_var_offset(use_os_rand), - .help = "Set to use OS random generator", - .def = "0", - .parent = "rw", - .hide = 1, + .type = FIO_OPT_DEPRECATED, + .off1 = td_var_offset(dep_use_os_rand), .category = FIO_OPT_C_IO, .group = FIO_OPT_G_RANDOM, }, @@ -2137,6 +2142,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .help = "Only start job when this period has passed", .def = "0", .is_seconds = 1, + .is_time = 1, .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_RUNTIME, }, @@ -2149,6 +2155,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .help = "Stop workload when this amount of time has passed", .def = "0", .is_seconds = 1, + .is_time = 1, .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_RUNTIME, }, @@ -2177,6 +2184,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .off1 = td_var_offset(ramp_time), .help = "Ramp up time before measuring performance", .is_seconds = 1, + .is_time = 1, .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_RUNTIME, }, @@ -2399,6 +2407,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), .help = "Fill pattern for IO buffers", .parent = "verify", .hide = 1, @@ -2469,6 +2478,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), .help = "Set CPUs allowed for async verify threads", .parent = "verify_async", .hide = 1, @@ -2481,6 +2491,28 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .off1 = td_var_offset(experimental_verify), .type = FIO_OPT_BOOL, .help = "Enable experimental verification", + .parent = "verify", + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_VERIFY, + }, + { + .name = "verify_state_load", + .lname = "Load verify state", + .off1 = td_var_offset(verify_state), + .type = FIO_OPT_BOOL, + .help = "Load verify termination state", + .parent = "verify", + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_VERIFY, + }, + { + .name = "verify_state_save", + .lname = "Save verify state", + .off1 = td_var_offset(verify_state_save), + .type = FIO_OPT_BOOL, + .def = "1", + .help = "Save verify state on termination", + .parent = "verify", .category = FIO_OPT_C_IO, .group = FIO_OPT_G_VERIFY, }, @@ -2656,6 +2688,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]), .maxval = 100, .help = "Percentage of mixed workload that is reads", .def = "50", @@ -2669,6 +2702,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]), .maxval = 100, .help = "Percentage of mixed workload that is writes", .def = "50", @@ -2730,6 +2764,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .off1 = td_var_offset(thinktime), .help = "Idle time between IO buffers (usec)", .def = "0", + .is_time = 1, .category = FIO_OPT_C_IO, .group = FIO_OPT_G_THINKTIME, }, @@ -2740,6 +2775,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .off1 = td_var_offset(thinktime_spin), .help = "Start think time by spinning this amount (usec)", .def = "0", + .is_time = 1, .parent = "thinktime", .hide = 1, .category = FIO_OPT_C_IO, @@ -2823,6 +2859,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .type = FIO_OPT_INT, .off1 = td_var_offset(max_latency), .help = "Maximum tolerated IO latency (usec)", + .is_time = 1, .category = FIO_OPT_C_IO, .group = FIO_OPT_G_LATPROF, }, @@ -2832,6 +2869,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .type = FIO_OPT_STR_VAL_TIME, .off1 = td_var_offset(latency_target), .help = "Ramp to max queue depth supporting this latency", + .is_time = 1, .category = FIO_OPT_C_IO, .group = FIO_OPT_G_LATPROF, }, @@ -2841,6 +2879,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .type = FIO_OPT_STR_VAL_TIME, .off1 = td_var_offset(latency_window), .help = "Time to sustain latency_target", + .is_time = 1, .category = FIO_OPT_C_IO, .group = FIO_OPT_G_LATPROF, }, @@ -2933,6 +2972,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), .help = "CPU affinity mask", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_CRED, @@ -2942,6 +2982,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), .help = "Set CPUs allowed", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_CRED, @@ -2973,6 +3014,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "numa_cpu_nodes", .type = FIO_OPT_STR, .cb = str_numa_cpunodes_cb, + .off1 = td_var_offset(numa_cpunodes), .help = "NUMA CPU nodes bind", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_INVALID, @@ -2981,6 +3023,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "numa_mem_policy", .type = FIO_OPT_STR, .cb = str_numa_mpol_cb, + .off1 = td_var_offset(numa_memnodes), .help = "NUMA memory policy setup", .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_INVALID, @@ -3195,6 +3238,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), .help = "Fill pattern for IO buffers", .category = FIO_OPT_C_IO, .group = FIO_OPT_G_IO_BUF, @@ -3204,6 +3248,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), .maxval = 100, .minval = 0, .help = "How compressible the buffer is (approximately)", @@ -3223,6 +3268,19 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .category = FIO_OPT_C_IO, .group = FIO_OPT_G_IO_BUF, }, + { + .name = "dedupe_percentage", + .lname = "Dedupe percentage", + .type = FIO_OPT_INT, + .cb = str_dedupe_cb, + .off1 = td_var_offset(dedupe_percentage), + .maxval = 100, + .minval = 0, + .help = "Percentage of buffers that are dedupable", + .interval = 1, + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_IO_BUF, + }, { .name = "clat_percentiles", .lname = "Completion latency percentiles", @@ -3323,7 +3381,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "gtod_cpu", .lname = "Dedicated gettimeofday() CPU", .type = FIO_OPT_INT, - .cb = str_gtod_cpu_cb, + .off1 = td_var_offset(gtod_cpu), .help = "Set up dedicated gettimeofday() thread on this CPU", .verify = gtod_cpu_verify, .category = FIO_OPT_C_GENERAL, @@ -3386,6 +3444,7 @@ struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "ignore_error", .type = FIO_OPT_STR, .cb = str_ignore_error_cb, + .off1 = td_var_offset(ignore_error_nr), .help = "Set a specific list of errors to ignore", .parent = "rw", .category = FIO_OPT_C_GENERAL, @@ -3850,6 +3909,39 @@ static char **dup_and_sub_options(char **opts, int num_opts) return opts_copy; } +static void show_closest_option(const char *opt) +{ + int best_option, best_distance; + int i, distance; + char *name; + + if (!strlen(opt)) + return; + + name = strdup(opt); + i = 0; + while (name[i] != '\0' && name[i] != '=') + i++; + name[i] = '\0'; + + best_option = -1; + best_distance = INT_MAX; + i = 0; + while (fio_options[i].name) { + distance = string_distance(name, fio_options[i].name); + if (distance < best_distance) { + best_distance = distance; + best_option = i; + } + i++; + } + + if (best_option != -1) + log_err("Did you mean %s?\n", fio_options[best_option].name); + + free(name); +} + int fio_options_parse(struct thread_data *td, char **opts, int num_opts, int dump_cmdline) { @@ -3864,6 +3956,9 @@ int fio_options_parse(struct thread_data *td, char **opts, int num_opts, int newret = parse_option(opts_copy[i], opts[i], fio_options, &o, td, dump_cmdline); + if (!newret && o) + fio_option_mark_set(&td->o, o); + if (opts_copy[i]) { if (newret && !o) { unknown++; @@ -3885,6 +3980,7 @@ int fio_options_parse(struct thread_data *td, char **opts, int num_opts, for (i = 0; i < num_opts; i++) { struct fio_option *o = NULL; int newret = 1; + if (!opts_copy[i]) continue; @@ -3894,9 +3990,10 @@ int fio_options_parse(struct thread_data *td, char **opts, int num_opts, td->eo, dump_cmdline); ret |= newret; - if (!o) + if (!o) { log_err("Bad option <%s>\n", opts[i]); - + show_closest_option(opts[i]); + } free(opts_copy[i]); opts_copy[i] = NULL; } @@ -3908,7 +4005,18 @@ int fio_options_parse(struct thread_data *td, char **opts, int num_opts, int fio_cmd_option_parse(struct thread_data *td, const char *opt, char *val) { - return parse_cmd_option(opt, val, fio_options, td); + int ret; + + ret = parse_cmd_option(opt, val, fio_options, td); + if (!ret) { + struct fio_option *o; + + o = find_option(fio_options, opt); + if (o) + fio_option_mark_set(&td->o, o); + } + + return ret; } int fio_cmd_ioengine_option_parse(struct thread_data *td, const char *opt, @@ -4070,3 +4178,60 @@ struct fio_option *fio_option_find(const char *name) return find_option(fio_options, name); } +static struct fio_option *find_next_opt(struct thread_options *o, + struct fio_option *from, + unsigned int off1) +{ + struct fio_option *opt; + + if (!from) + from = &fio_options[0]; + else + from++; + + opt = NULL; + do { + if (off1 == from->off1) { + opt = from; + break; + } + from++; + } while (from->name); + + return opt; +} + +static int opt_is_set(struct thread_options *o, struct fio_option *opt) +{ + unsigned int opt_off, index, offset; + + opt_off = opt - &fio_options[0]; + index = opt_off / (8 * sizeof(uint64_t)); + offset = opt_off & ((8 * sizeof(uint64_t)) - 1); + return (o->set_options[index] & (1UL << offset)) != 0; +} + +int __fio_option_is_set(struct thread_options *o, unsigned int off1) +{ + struct fio_option *opt, *next; + + next = NULL; + while ((opt = find_next_opt(o, next, off1)) != NULL) { + if (opt_is_set(o, opt)) + return 1; + + next = opt; + } + + return 0; +} + +void fio_option_mark_set(struct thread_options *o, struct fio_option *opt) +{ + unsigned int opt_off, index, offset; + + opt_off = opt - &fio_options[0]; + index = opt_off / (8 * sizeof(uint64_t)); + offset = opt_off & ((8 * sizeof(uint64_t)) - 1); + o->set_options[index] |= 1UL << offset; +}