X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=options.c;h=4dcefba05511879ad658efce073f699591351e3d;hp=84ac412874a2742f083d691fcfc1c70bdbb301d1;hb=ebadc0ce34c11b3e0130d5602b18f4ed0e638386;hpb=81c6b6cdd2f696528c423ff940a06efae8def583 diff --git a/options.c b/options.c index 84ac4128..4dcefba0 100644 --- a/options.c +++ b/options.c @@ -37,7 +37,7 @@ static int converthexchartoint(char a) { int base; - switch(a) { + switch (a) { case '0'...'9': base = '0'; break; @@ -50,7 +50,7 @@ static int converthexchartoint(char a) default: base = 0; } - return (a - base); + return a - base; } static int bs_cmp(const void *p1, const void *p2) @@ -61,7 +61,7 @@ static int bs_cmp(const void *p1, const void *p2) return bsp1->perc < bsp2->perc; } -static int bssplit_ddir(struct thread_data *td, int ddir, char *str) +static int bssplit_ddir(struct thread_options *o, int ddir, char *str) { struct bssplit *bssplit; unsigned int i, perc, perc_missing; @@ -69,7 +69,7 @@ static int bssplit_ddir(struct thread_data *td, int ddir, char *str) long long val; char *fname; - td->o.bssplit_nr[ddir] = 4; + o->bssplit_nr[ddir] = 4; bssplit = malloc(4 * sizeof(struct bssplit)); i = 0; @@ -84,9 +84,9 @@ static int bssplit_ddir(struct thread_data *td, int ddir, char *str) /* * grow struct buffer, if needed */ - if (i == td->o.bssplit_nr[ddir]) { - td->o.bssplit_nr[ddir] <<= 1; - bssplit = realloc(bssplit, td->o.bssplit_nr[ddir] + if (i == o->bssplit_nr[ddir]) { + o->bssplit_nr[ddir] <<= 1; + bssplit = realloc(bssplit, o->bssplit_nr[ddir] * sizeof(struct bssplit)); } @@ -102,9 +102,9 @@ static int bssplit_ddir(struct thread_data *td, int ddir, char *str) } else perc = -1; - if (str_to_decimal(fname, &val, 1, td)) { + if (str_to_decimal(fname, &val, 1, o)) { log_err("fio: bssplit conversion failed\n"); - free(td->o.bssplit); + free(bssplit); return 1; } @@ -118,13 +118,13 @@ static int bssplit_ddir(struct thread_data *td, int ddir, char *str) i++; } - td->o.bssplit_nr[ddir] = i; + o->bssplit_nr[ddir] = i; /* * Now check if the percentages add up, and how much is missing */ perc = perc_missing = 0; - for (i = 0; i < td->o.bssplit_nr[ddir]; i++) { + for (i = 0; i < o->bssplit_nr[ddir]; i++) { struct bssplit *bsp = &bssplit[i]; if (bsp->perc == (unsigned char) -1) @@ -143,7 +143,7 @@ static int bssplit_ddir(struct thread_data *td, int ddir, char *str) * them. */ if (perc_missing) { - for (i = 0; i < td->o.bssplit_nr[ddir]; i++) { + for (i = 0; i < o->bssplit_nr[ddir]; i++) { struct bssplit *bsp = &bssplit[i]; if (bsp->perc == (unsigned char) -1) @@ -151,16 +151,15 @@ static int bssplit_ddir(struct thread_data *td, int ddir, char *str) } } - td->o.min_bs[ddir] = min_bs; - td->o.max_bs[ddir] = max_bs; + o->min_bs[ddir] = min_bs; + o->max_bs[ddir] = max_bs; /* * now sort based on percentages, for ease of lookup */ - qsort(bssplit, td->o.bssplit_nr[ddir], sizeof(struct bssplit), bs_cmp); - td->o.bssplit[ddir] = bssplit; + qsort(bssplit, o->bssplit_nr[ddir], sizeof(struct bssplit), bs_cmp); + o->bssplit[ddir] = bssplit; return 0; - } static int str_bssplit_cb(void *data, const char *input) @@ -169,6 +168,9 @@ static int str_bssplit_cb(void *data, const char *input) char *str, *p, *odir, *ddir; int ret = 0; + if (parse_dryrun()) + return 0; + p = str = strdup(input); strip_blank_front(&str); @@ -178,36 +180,36 @@ static int str_bssplit_cb(void *data, const char *input) if (odir) { ddir = strchr(odir + 1, ','); if (ddir) { - ret = bssplit_ddir(td, DDIR_TRIM, ddir + 1); + ret = bssplit_ddir(&td->o, DDIR_TRIM, ddir + 1); if (!ret) *ddir = '\0'; } else { char *op; op = strdup(odir + 1); - ret = bssplit_ddir(td, DDIR_TRIM, op); + ret = bssplit_ddir(&td->o, DDIR_TRIM, op); free(op); } - if (!ret) - ret = bssplit_ddir(td, DDIR_WRITE, odir + 1); + if (!ret) + ret = bssplit_ddir(&td->o, DDIR_WRITE, odir + 1); if (!ret) { *odir = '\0'; - ret = bssplit_ddir(td, DDIR_READ, str); + ret = bssplit_ddir(&td->o, DDIR_READ, str); } } else { char *op; op = strdup(str); - ret = bssplit_ddir(td, DDIR_WRITE, op); + ret = bssplit_ddir(&td->o, DDIR_WRITE, op); free(op); if (!ret) { op = strdup(str); - ret = bssplit_ddir(td, DDIR_TRIM, op); + ret = bssplit_ddir(&td->o, DDIR_TRIM, op); free(op); } - ret = bssplit_ddir(td, DDIR_READ, str); + ret = bssplit_ddir(&td->o, DDIR_READ, str); } free(p); @@ -216,17 +218,17 @@ static int str_bssplit_cb(void *data, const char *input) static int str2error(char *str) { - const char * err[] = {"EPERM", "ENOENT", "ESRCH", "EINTR", "EIO", + const char *err[] = { "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO", "ENXIO", "E2BIG", "ENOEXEC", "EBADF", "ECHILD", "EAGAIN", "ENOMEM", "EACCES", "EFAULT", "ENOTBLK", "EBUSY", "EEXIST", "EXDEV", "ENODEV", "ENOTDIR", "EISDIR", "EINVAL", "ENFILE", "EMFILE", "ENOTTY", "ETXTBSY","EFBIG", "ENOSPC", "ESPIPE", - "EROFS","EMLINK", "EPIPE", "EDOM", "ERANGE"}; + "EROFS","EMLINK", "EPIPE", "EDOM", "ERANGE" }; int i = 0, num = sizeof(err) / sizeof(void *); - while( i < num) { + while (i < num) { if (!strcmp(err[i], str)) return i + 1; i++; @@ -272,6 +274,7 @@ static int ignore_error_type(struct thread_data *td, int etype, char *str) if (!error[i]) { log_err("Unknown error %s, please use number value \n", fname); + free(error); return 1; } i++; @@ -290,6 +293,10 @@ static int str_ignore_error_cb(void *data, const char *input) struct thread_data *td = data; char *str, *p, *n; int type = 0, ret = 1; + + if (parse_dryrun()) + return 0; + p = str = strdup(input); strip_blank_front(&str); @@ -312,26 +319,30 @@ 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_options *o = &td->o; char *nr = get_opt_postfix(str); - td->o.ddir_seq_nr = 1; - td->o.ddir_seq_add = 0; + if (parse_dryrun()) + return 0; + + o->ddir_seq_nr = 1; + o->ddir_seq_add = 0; if (!nr) return 0; if (td_random(td)) - td->o.ddir_seq_nr = atoi(nr); + o->ddir_seq_nr = atoi(nr); else { long long val; - if (str_to_decimal(nr, &val, 1, td)) { + if (str_to_decimal(nr, &val, 1, o)) { log_err("fio: rw postfix parsing failed\n"); free(nr); return 1; } - td->o.ddir_seq_add = val; + o->ddir_seq_add = val; } free(nr); @@ -343,19 +354,7 @@ static int str_mem_cb(void *data, const char *mem) struct thread_data *td = data; if (td->o.mem_type == MEM_MMAPHUGE || td->o.mem_type == MEM_MMAP) - td->mmapfile = get_opt_postfix(mem); - - return 0; -} - -static int str_verify_cb(void *data, const char *mem) -{ - struct thread_data *td = data; - - if (td->o.verify == VERIFY_CRC32C_INTEL || - td->o.verify == VERIFY_CRC32C) { - crc32c_intel_probe(); - } + td->o.mmapfile = get_opt_postfix(mem); return 0; } @@ -388,40 +387,6 @@ static int str_rwmix_write_cb(void *data, unsigned long long *val) return 0; } -#ifdef FIO_HAVE_IOPRIO -static int str_prioclass_cb(void *data, unsigned long long *val) -{ - struct thread_data *td = data; - unsigned short mask; - - /* - * mask off old class bits, str_prio_cb() may have set a default class - */ - mask = (1 << IOPRIO_CLASS_SHIFT) - 1; - td->ioprio &= mask; - - td->ioprio |= *val << IOPRIO_CLASS_SHIFT; - td->ioprio_set = 1; - return 0; -} - -static int str_prio_cb(void *data, unsigned long long *val) -{ - struct thread_data *td = data; - - td->ioprio |= *val; - - /* - * If no class is set, assume BE - */ - if ((td->ioprio >> IOPRIO_CLASS_SHIFT) == 0) - td->ioprio |= IOPRIO_CLASS_BE << IOPRIO_CLASS_SHIFT; - - td->ioprio_set = 1; - return 0; -} -#endif - static int str_exitall_cb(void) { exitall_on_terminate = 1; @@ -436,6 +401,9 @@ static int str_cpumask_cb(void *data, unsigned long long *val) long max_cpu; int ret; + if (parse_dryrun()) + return 0; + ret = fio_cpuset_init(&td->o.cpumask); if (ret < 0) { log_err("fio: cpuset_init failed\n"); @@ -534,6 +502,9 @@ 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; @@ -559,6 +530,9 @@ static int str_numa_cpunodes_cb(void *data, char *input) { struct thread_data *td = data; + if (parse_dryrun()) + return 0; + /* numa_parse_nodestring() parses a character string list * of nodes into a bit mask. The bit mask is allocated by * numa_allocate_nodemask(), so it should be freed by @@ -579,10 +553,14 @@ static int str_numa_mpol_cb(void *data, char *input) { struct thread_data *td = data; const char * const policy_types[] = - { "default", "prefer", "bind", "interleave", "local" }; + { "default", "prefer", "bind", "interleave", "local", NULL }; int i; + char *nodelist; - char *nodelist = strchr(input, ':'); + if (parse_dryrun()) + return 0; + + nodelist = strchr(input, ':'); if (nodelist) { /* NUL-terminate mode */ *nodelist++ = '\0'; @@ -714,6 +692,9 @@ static int str_random_distribution_cb(void *data, const char *str) double val; char *nr; + if (parse_dryrun()) + return 0; + if (td->o.random_distribution == FIO_RAND_DIST_ZIPF) val = 1.1; else if (td->o.random_distribution == FIO_RAND_DIST_PARETO) @@ -735,53 +716,14 @@ static int str_random_distribution_cb(void *data, const char *str) log_err("fio: zipf theta must different than 1.0\n"); return 1; } - td->o.zipf_theta = val; + td->o.zipf_theta.u.f = val; } else { if (val <= 0.00 || val >= 1.00) { log_err("fio: pareto input out of range (0 < input < 1.0)\n"); return 1; } - td->o.pareto_h = val; - } - - return 0; -} - -static int check_dir(struct thread_data *td, char *fname) -{ -#if 0 - char file[PATH_MAX], *dir; - int elen = 0; - - if (td->o.directory) { - strcpy(file, td->o.directory); - strcat(file, "/"); - elen = strlen(file); - } - - sprintf(file + elen, "%s", fname); - dir = dirname(file); - - { - struct stat sb; - /* - * We can't do this on FIO_DISKLESSIO engines. The engine isn't loaded - * yet, so we can't do this check right here... - */ - if (lstat(dir, &sb) < 0) { - int ret = errno; - - log_err("fio: %s is not a directory\n", dir); - td_verror(td, ret, "lstat"); - return 1; - } - - if (!S_ISDIR(sb.st_mode)) { - log_err("fio: %s is not a directory\n", dir); - return 1; - } + td->o.pareto_h.u.f = val; } -#endif return 0; } @@ -848,10 +790,6 @@ static int str_filename_cb(void *data, const char *input) while ((fname = get_next_file_name(&str)) != NULL) { if (!strlen(fname)) break; - if (check_dir(td, fname)) { - free(p); - return 1; - } add_file(td, fname); td->o.nr_files++; } @@ -865,6 +803,9 @@ static int str_directory_cb(void *data, const char fio_unused *str) struct thread_data *td = data; struct stat sb; + if (parse_dryrun()) + return 0; + if (lstat(td->o.directory, &sb) < 0) { int ret = errno; @@ -880,35 +821,38 @@ static int str_directory_cb(void *data, const char fio_unused *str) return 0; } -static int str_opendir_cb(void *data, const char fio_unused *str) +static int str_lockfile_cb(void *data, const char fio_unused *str) { struct thread_data *td = data; - if (!td->files_index) - td->o.nr_files = 0; + if (td->files_index) { + log_err("fio: lockfile= option must precede filename=\n"); + return 1; + } - return add_dir_files(td, td->o.opendir); + return 0; } -static int str_verify_offset_cb(void *data, unsigned long long *off) +static int str_opendir_cb(void *data, const char fio_unused *str) { struct thread_data *td = data; - if (*off && *off < sizeof(struct verify_header)) { - log_err("fio: verify_offset too small\n"); - return 1; - } + if (parse_dryrun()) + return 0; - td->o.verify_offset = *off; - return 0; + if (!td->files_index) + td->o.nr_files = 0; + + return add_dir_files(td, td->o.opendir); } -static int str_verify_pattern_cb(void *data, const char *input) +static int pattern_cb(char *pattern, unsigned int max_size, + const char *input, unsigned int *pattern_bytes) { - struct thread_data *td = data; long off; int i = 0, j = 0, len, k, base = 10; - char* loc1, * loc2; + uint32_t pattern_length; + char *loc1, *loc2; loc1 = strstr(input, "0x"); loc2 = strstr(input, "0X"); @@ -917,7 +861,7 @@ static int str_verify_pattern_cb(void *data, const char *input) off = strtol(input, NULL, base); if (off != LONG_MAX || errno != ERANGE) { while (off) { - td->o.verify_pattern[i] = off & 0xff; + pattern[i] = off & 0xff; off >>= 8; i++; } @@ -931,13 +875,13 @@ static int str_verify_pattern_cb(void *data, const char *input) j = loc2 - input + 2; } else return 1; - if (len - j < MAX_PATTERN_SIZE * 2) { + if (len - j < max_size * 2) { while (k >= j) { off = converthexchartoint(input[k--]); if (k >= j) off += (converthexchartoint(input[k--]) * 16); - td->o.verify_pattern[i++] = (char) off; + pattern[i++] = (char) off; } } } @@ -946,61 +890,67 @@ static int str_verify_pattern_cb(void *data, const char *input) * 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. */ - while (i > 1 && i * 2 <= MAX_PATTERN_SIZE) { - memcpy(&td->o.verify_pattern[i], &td->o.verify_pattern[0], i); + pattern_length = i; + while (i > 1 && i * 2 <= max_size) { + memcpy(&pattern[i], &pattern[0], i); i *= 2; } + + /* + * Fill remainder, if the pattern multiple ends up not being + * max_size. + */ + while (i > 1 && i < max_size) { + unsigned int b = min(pattern_length, max_size - i); + + memcpy(&pattern[i], &pattern[0], b); + i += b; + } + if (i == 1) { /* * The code in verify_io_u_pattern assumes a single byte pattern * fills the whole verify pattern buffer. */ - memset(td->o.verify_pattern, td->o.verify_pattern[0], - MAX_PATTERN_SIZE); + memset(pattern, pattern[0], max_size); } - td->o.verify_pattern_bytes = i; - - /* - * VERIFY_META could already be set - */ - if (td->o.verify == VERIFY_NONE) - td->o.verify = VERIFY_PATTERN; - + *pattern_bytes = i; return 0; } -static int str_write_bw_log_cb(void *data, const char *str) +static int str_buffer_pattern_cb(void *data, const char *input) { struct thread_data *td = data; + int ret; - 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 = data; + ret = pattern_cb(td->o.buffer_pattern, MAX_PATTERN_SIZE, input, + &td->o.buffer_pattern_bytes); - if (str) - td->o.lat_log_file = strdup(str); + if (!ret) { + td->o.refill_buffers = 0; + td->o.scramble_buffers = 0; + td->o.zero_buffers = 0; + } - td->o.write_lat_log = 1; - return 0; + return ret; } -static int str_write_iops_log_cb(void *data, const char *str) +static int str_verify_pattern_cb(void *data, const char *input) { struct thread_data *td = data; + int ret; - if (str) - td->o.iops_log_file = strdup(str); + ret = pattern_cb(td->o.verify_pattern, MAX_PATTERN_SIZE, input, + &td->o.verify_pattern_bytes); - td->o.write_iops_log = 1; - return 0; + /* + * VERIFY_META could already be set + */ + if (!ret && td->o.verify == VERIFY_NONE) + td->o.verify = VERIFY_PATTERN; + + return ret; } static int str_gtod_reduce_cb(void *data, int *il) @@ -1131,6 +1081,10 @@ struct opt_group *opt_group_from_mask(unsigned int *mask) } static struct opt_group fio_opt_cat_groups[] = { + { + .name = "Latency profiling", + .mask = FIO_OPT_G_LATPROF, + }, { .name = "Rate", .mask = FIO_OPT_G_RATE, @@ -1229,7 +1183,7 @@ struct opt_group *opt_group_cat_from_mask(unsigned int *mask) /* * Map of job/command line options */ -static struct fio_option fio_options[FIO_MAX_OPTS] = { +struct fio_option fio_options[FIO_MAX_OPTS] = { { .name = "description", .lname = "Description of job", @@ -1285,9 +1239,11 @@ static struct fio_option fio_options[FIO_MAX_OPTS] = { .type = FIO_OPT_STR, .off1 = td_var_offset(file_lock_mode), .help = "Lock file when doing IO to it", + .prio = 1, .parent = "filename", .hide = 0, .def = "none", + .cb = str_lockfile_cb, .category = FIO_OPT_C_FILE, .group = FIO_OPT_G_FILENAME, .posval = { @@ -1407,6 +1363,11 @@ static struct fio_option fio_options[FIO_MAX_OPTS] = { { .ival = "vsync", .help = "Use readv/writev", }, +#ifdef CONFIG_PWRITEV + { .ival = "pvsync", + .help = "Use preadv/pwritev", + }, +#endif #ifdef CONFIG_LIBAIO { .ival = "libaio", .help = "Linux native asynchronous IO", @@ -1598,6 +1559,16 @@ static struct fio_option fio_options[FIO_MAX_OPTS] = { .category = FIO_OPT_C_IO, .group = FIO_OPT_G_INVALID, }, + { + .name = "number_ios", + .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", + .def = "0", + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_INVALID, + }, { .name = "bs", .lname = "Block size", @@ -1673,6 +1644,17 @@ static struct fio_option fio_options[FIO_MAX_OPTS] = { .category = FIO_OPT_C_IO, .group = FIO_OPT_G_INVALID, }, + { + .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), + .help = "Consider any blocksize setting to be sequential,ramdom", + .def = "0", + .parent = "blocksize", + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_INVALID, + }, { .name = "randrepeat", .lname = "Random repeatable", @@ -1685,6 +1667,16 @@ static struct fio_option fio_options[FIO_MAX_OPTS] = { .category = FIO_OPT_C_IO, .group = FIO_OPT_G_RANDOM, }, + { + .name = "randseed", + .lname = "The random generator seed", + .type = FIO_OPT_STR_VAL, + .off1 = td_var_offset(rand_seed), + .help = "Set the random generator seed value", + .parent = "rw", + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_RANDOM, + }, { .name = "use_os_rand", .lname = "Use OS random", @@ -1764,6 +1756,28 @@ static struct fio_option fio_options[FIO_MAX_OPTS] = { .category = FIO_OPT_C_IO, .group = FIO_OPT_G_RANDOM, }, + { + .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]), + .maxval = 100, + .help = "Percentage of seq/random mix that should be random", + .def = "100,100,100", + .interval = 5, + .inverse = "percentage_sequential", + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_RANDOM, + }, + { + .name = "percentage_sequential", + .lname = "Percentage Sequential", + .type = FIO_OPT_DEPRECATED, + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_RANDOM, + }, { .name = "nrfiles", .lname = "Number of files", @@ -1900,18 +1914,18 @@ static struct fio_option fio_options[FIO_MAX_OPTS] = { { .ival = "wait_before", .oval = SYNC_FILE_RANGE_WAIT_BEFORE, .help = "SYNC_FILE_RANGE_WAIT_BEFORE", - .or = 1, + .orval = 1, }, { .ival = "write", .oval = SYNC_FILE_RANGE_WRITE, .help = "SYNC_FILE_RANGE_WRITE", - .or = 1, + .orval = 1, }, { .ival = "wait_after", .oval = SYNC_FILE_RANGE_WAIT_AFTER, .help = "SYNC_FILE_RANGE_WAIT_AFTER", - .or = 1, + .orval = 1, }, }, .type = FIO_OPT_STR_MULTI, @@ -1933,6 +1947,16 @@ static struct fio_option fio_options[FIO_MAX_OPTS] = { .category = FIO_OPT_C_IO, .group = FIO_OPT_G_IO_TYPE, }, + { + .name = "atomic", + .lname = "Atomic I/O", + .type = FIO_OPT_BOOL, + .off1 = td_var_offset(oatomic), + .help = "Use Atomic IO with O_DIRECT (implies O_DIRECT)", + .def = "0", + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_IO_TYPE, + }, { .name = "buffered", .lname = "Buffered I/O", @@ -2007,6 +2031,15 @@ static struct fio_option fio_options[FIO_MAX_OPTS] = { .category = FIO_OPT_C_GENERAL, .group = FIO_OPT_G_RUNTIME, }, + { + .name = "verify_only", + .lname = "Verify only", + .type = FIO_OPT_STR_SET, + .off1 = td_var_offset(verify_only), + .help = "Verifies previously written data is still valid", + .category = FIO_OPT_C_GENERAL, + .group = FIO_OPT_G_RUNTIME, + }, { .name = "ramp_time", .lname = "Ramp time", @@ -2104,7 +2137,6 @@ static struct fio_option fio_options[FIO_MAX_OPTS] = { .type = FIO_OPT_STR, .off1 = td_var_offset(verify), .help = "Verify data written", - .cb = str_verify_cb, .def = "0", .category = FIO_OPT_C_IO, .group = FIO_OPT_G_VERIFY, @@ -2220,7 +2252,6 @@ static struct fio_option fio_options[FIO_MAX_OPTS] = { .help = "Offset verify header location by N bytes", .off1 = td_var_offset(verify_offset), .minval = sizeof(struct verify_header), - .cb = str_verify_offset_cb, .parent = "verify", .hide = 1, .category = FIO_OPT_C_IO, @@ -2534,7 +2565,6 @@ static struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "prio", .lname = "I/O nice priority", .type = FIO_OPT_INT, - .cb = str_prio_cb, .off1 = td_var_offset(ioprio), .help = "Set job IO priority value", .minval = 0, @@ -2547,7 +2577,6 @@ static struct fio_option fio_options[FIO_MAX_OPTS] = { .name = "prioclass", .lname = "I/O nice priority class", .type = FIO_OPT_INT, - .cb = str_prioclass_cb, .off1 = td_var_offset(ioprio_class), .help = "Set job IO priority class", .minval = 0, @@ -2658,7 +2687,38 @@ static struct fio_option fio_options[FIO_MAX_OPTS] = { .off1 = td_var_offset(max_latency), .help = "Maximum tolerated IO latency (usec)", .category = FIO_OPT_C_IO, - .group = FIO_OPT_G_RATE, + .group = FIO_OPT_G_LATPROF, + }, + { + .name = "latency_target", + .lname = "Latency Target (usec)", + .type = FIO_OPT_STR_VAL_TIME, + .off1 = td_var_offset(latency_target), + .help = "Ramp to max queue depth supporting this latency", + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_LATPROF, + }, + { + .name = "latency_window", + .lname = "Latency Window (usec)", + .type = FIO_OPT_STR_VAL_TIME, + .off1 = td_var_offset(latency_window), + .help = "Time to sustain latency_target", + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_LATPROF, + }, + { + .name = "latency_percentile", + .lname = "Latency Percentile", + .type = FIO_OPT_FLOAT_LIST, + .off1 = td_var_offset(latency_percentile), + .help = "Percentile of IOs must be below latency_target", + .def = "100", + .maxlen = 1, + .minfp = 0.0, + .maxfp = 100.0, + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_LATPROF, }, { .name = "invalidate", @@ -2838,9 +2898,8 @@ static struct fio_option fio_options[FIO_MAX_OPTS] = { { .name = "write_bw_log", .lname = "Write bandwidth log", - .type = FIO_OPT_STR, + .type = FIO_OPT_STR_STORE, .off1 = td_var_offset(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, @@ -2848,9 +2907,8 @@ static struct fio_option fio_options[FIO_MAX_OPTS] = { { .name = "write_lat_log", .lname = "Write latency log", - .type = FIO_OPT_STR, + .type = FIO_OPT_STR_STORE, .off1 = td_var_offset(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, @@ -2858,9 +2916,8 @@ static struct fio_option fio_options[FIO_MAX_OPTS] = { { .name = "write_iops_log", .lname = "Write IOPS log", - .type = FIO_OPT_STR, + .type = FIO_OPT_STR_STORE, .off1 = td_var_offset(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, @@ -2905,10 +2962,9 @@ static struct fio_option fio_options[FIO_MAX_OPTS] = { { .name = "group_reporting", .lname = "Group reporting", - .type = FIO_OPT_BOOL, + .type = FIO_OPT_STR_SET, .off1 = td_var_offset(group_reporting), .help = "Do reporting on a per-group basis", - .def = "1", .category = FIO_OPT_C_STAT, .group = FIO_OPT_G_INVALID, }, @@ -2940,6 +2996,15 @@ static struct fio_option fio_options[FIO_MAX_OPTS] = { .category = FIO_OPT_C_IO, .group = FIO_OPT_G_IO_BUF, }, + { + .name = "buffer_pattern", + .lname = "Buffer pattern", + .type = FIO_OPT_STR, + .cb = str_buffer_pattern_cb, + .help = "Fill pattern for IO buffers", + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_IO_BUF, + }, { .name = "buffer_compress_percentage", .lname = "Buffer compression percentage", @@ -3316,7 +3381,7 @@ static void add_to_lopt(struct option *lopt, struct fio_option *o, lopt->name = (char *) name; lopt->val = val; if (o->type == FIO_OPT_STR_SET) - lopt->has_arg = no_argument; + lopt->has_arg = optional_argument; else lopt->has_arg = required_argument; } @@ -3456,14 +3521,12 @@ static char *bc_calc(char *str) sprintf(buf, "echo '%s' | %s", tmp, BC_APP); f = popen(buf, "r"); - if (!f) { + if (!f) return NULL; - } ret = fread(&buf[tmp - str], 1, 128 - (tmp - str), f); - if (ret <= 0) { + if (ret <= 0) return NULL; - } pclose(f); buf[(tmp - str) + ret - 1] = '\0'; @@ -3591,7 +3654,8 @@ static char **dup_and_sub_options(char **opts, int num_opts) return opts_copy; } -int fio_options_parse(struct thread_data *td, char **opts, int num_opts) +int fio_options_parse(struct thread_data *td, char **opts, int num_opts, + int dump_cmdline) { int i, ret, unknown; char **opts_copy; @@ -3602,7 +3666,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); + &o, td, dump_cmdline); if (opts_copy[i]) { if (newret && !o) { @@ -3631,7 +3695,7 @@ int fio_options_parse(struct thread_data *td, char **opts, int num_opts) if (td->eo) newret = parse_option(opts_copy[i], opts[i], td->io_ops->options, &o, - td->eo); + td->eo, dump_cmdline); ret |= newret; if (!o) @@ -3654,7 +3718,7 @@ int fio_cmd_option_parse(struct thread_data *td, const char *opt, char *val) int fio_cmd_ioengine_option_parse(struct thread_data *td, const char *opt, char *val) { - return parse_cmd_option(opt, val, td->io_ops->options, td); + return parse_cmd_option(opt, val, td->io_ops->options, td->eo); } void fio_fill_default_options(struct thread_data *td) @@ -3676,7 +3740,7 @@ void options_mem_dupe(void *data, struct fio_option *options) if (o->type != FIO_OPT_STR_STORE) continue; - ptr = td_var(data, o->off1); + ptr = td_var(data, o, o->off1); if (*ptr) *ptr = strdup(*ptr); } @@ -3700,11 +3764,11 @@ void fio_options_mem_dupe(struct thread_data *td) unsigned int fio_get_kb_base(void *data) { - struct thread_data *td = data; + struct thread_options *o = data; unsigned int kb_base = 0; - if (td) - kb_base = td->o.kb_base; + if (o) + kb_base = o->kb_base; if (!kb_base) kb_base = 1024; @@ -3722,7 +3786,13 @@ int add_option(struct fio_option *o) __o++; } + if (opt_index + 1 == FIO_MAX_OPTS) { + log_err("fio: FIO_MAX_OPTS is too small\n"); + return 1; + } + memcpy(&fio_options[opt_index], o, sizeof(*o)); + fio_options[opt_index + 1].name = NULL; return 0; } @@ -3788,3 +3858,9 @@ void fio_options_free(struct thread_data *td) td->eo = NULL; } } + +struct fio_option *fio_option_find(const char *name) +{ + return find_option(fio_options, name); +} +