X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=options.c;h=bca217f6466ca8c80086f733726c924651fcdd09;hp=d1cf7e8a70ba6480c9dc1b1021b5a739c5dec9f9;hb=e25b6d5a0a16b978385bfb93a4c156b03363d136;hpb=1ecc95ca3117d146254dec621f1402f8be7f375c diff --git a/options.c b/options.c index d1cf7e8a..bca217f6 100644 --- a/options.c +++ b/options.c @@ -214,6 +214,101 @@ static int str_bssplit_cb(void *data, const char *input) return ret; } +static int str2error(char *str) +{ + 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"}; + int i = 0, num = sizeof(err) / sizeof(void *); + + while( i < num) { + if (!strcmp(err[i], str)) + return i + 1; + i++; + } + return 0; +} + +static int ignore_error_type(struct thread_data *td, int etype, char *str) +{ + unsigned int i; + int *error; + char *fname; + + if (etype >= ERROR_TYPE_CNT) { + log_err("Illegal error type\n"); + return 1; + } + + td->o.ignore_error_nr[etype] = 4; + error = malloc(4 * sizeof(struct bssplit)); + + i = 0; + while ((fname = strsep(&str, ":")) != NULL) { + + if (!strlen(fname)) + break; + + /* + * grow struct buffer, if needed + */ + if (i == td->o.ignore_error_nr[etype]) { + td->o.ignore_error_nr[etype] <<= 1; + error = realloc(error, td->o.ignore_error_nr[etype] + * sizeof(int)); + } + if (fname[0] == 'E') { + error[i] = str2error(fname); + } else { + error[i] = atoi(fname); + if (error[i] < 0) + error[i] = error[i]; + } + if (!error[i]) { + log_err("Unknown error %s, please use number value \n", + fname); + return 1; + } + i++; + } + if (i) { + td->o.continue_on_error |= 1 << etype; + td->o.ignore_error_nr[etype] = i; + td->o.ignore_error[etype] = error; + } + return 0; + +} + +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; + p = str = strdup(input); + + strip_blank_front(&str); + strip_blank_end(str); + + while (p) { + n = strchr(p, ','); + if (n) + *n++ = '\0'; + ret = ignore_error_type(td, type, p); + if (ret) + break; + p = n; + type++; + } + free(str); + return ret; +} + static int str_rw_cb(void *data, const char *str) { struct thread_data *td = data; @@ -247,13 +342,8 @@ 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) { + if (td->o.mem_type == MEM_MMAPHUGE || td->o.mem_type == MEM_MMAP) td->mmapfile = get_opt_postfix(mem); - if (td->o.mem_type == MEM_MMAPHUGE && !td->mmapfile) { - log_err("fio: mmaphuge:/path/to/file\n"); - return 1; - } - } return 0; } @@ -275,7 +365,8 @@ static int fio_clock_source_cb(void *data, const char *str) struct thread_data *td = data; fio_clock_source = td->o.clocksource; - fio_time_init(); + fio_clock_source_set = 1; + fio_clock_init(); return 0; } @@ -469,6 +560,130 @@ static int str_verify_cpus_allowed_cb(void *data, const char *input) } #endif +#ifdef CONFIG_LIBNUMA +static int str_numa_cpunodes_cb(void *data, char *input) +{ + struct thread_data *td = data; + + /* 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 + * numa_free_nodemask(). + */ + td->o.numa_cpunodesmask = numa_parse_nodestring(input); + if (td->o.numa_cpunodesmask == NULL) { + log_err("fio: numa_parse_nodestring failed\n"); + td_verror(td, 1, "str_numa_cpunodes_cb"); + return 1; + } + + td->o.numa_cpumask_set = 1; + return 0; +} + +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" }; + int i; + + char *nodelist = strchr(input, ':'); + if (nodelist) { + /* NUL-terminate mode */ + *nodelist++ = '\0'; + } + + for (i = 0; i <= MPOL_LOCAL; i++) { + if (!strcmp(input, policy_types[i])) { + td->o.numa_mem_mode = i; + break; + } + } + if (i > MPOL_LOCAL) { + log_err("fio: memory policy should be: default, prefer, bind, interleave, local\n"); + goto out; + } + + switch (td->o.numa_mem_mode) { + case MPOL_PREFERRED: + /* + * Insist on a nodelist of one node only + */ + if (nodelist) { + char *rest = nodelist; + while (isdigit(*rest)) + rest++; + if (*rest) { + log_err("fio: one node only for \'prefer\'\n"); + goto out; + } + } else { + log_err("fio: one node is needed for \'prefer\'\n"); + goto out; + } + break; + case MPOL_INTERLEAVE: + /* + * Default to online nodes with memory if no nodelist + */ + if (!nodelist) + nodelist = strdup("all"); + break; + case MPOL_LOCAL: + case MPOL_DEFAULT: + /* + * Don't allow a nodelist + */ + if (nodelist) { + log_err("fio: NO nodelist for \'local\'\n"); + goto out; + } + break; + case MPOL_BIND: + /* + * Insist on a nodelist + */ + if (!nodelist) { + log_err("fio: a nodelist is needed for \'bind\'\n"); + goto out; + } + break; + } + + + /* 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 + * numa_free_nodemask(). + */ + switch (td->o.numa_mem_mode) { + case MPOL_PREFERRED: + td->o.numa_mem_prefer_node = atoi(nodelist); + break; + case MPOL_INTERLEAVE: + case MPOL_BIND: + td->o.numa_memnodesmask = numa_parse_nodestring(nodelist); + if (td->o.numa_memnodesmask == NULL) { + log_err("fio: numa_parse_nodestring failed\n"); + td_verror(td, 1, "str_numa_memnodes_cb"); + return 1; + } + break; + case MPOL_LOCAL: + case MPOL_DEFAULT: + default: + break; + } + + td->o.numa_memmask_set = 1; + return 0; + +out: + return 1; +} +#endif + #ifdef FIO_HAVE_TRIM static int str_verify_trim_cb(void *data, unsigned long long *val) { @@ -493,7 +708,7 @@ static int str_fst_cb(void *data, const char *str) return 0; } -#ifdef FIO_HAVE_SYNC_FILE_RANGE +#ifdef CONFIG_SYNC_FILE_RANGE static int str_sfr_cb(void *data, const char *str) { struct thread_data *td = data; @@ -509,6 +724,45 @@ static int str_sfr_cb(void *data, const char *str) } #endif +static int str_random_distribution_cb(void *data, const char *str) +{ + struct thread_data *td = data; + double val; + char *nr; + + if (td->o.random_distribution == FIO_RAND_DIST_ZIPF) + val = 1.1; + else if (td->o.random_distribution == FIO_RAND_DIST_PARETO) + val = 0.2; + else + return 0; + + nr = get_opt_postfix(str); + if (nr && !str_to_float(nr, &val)) { + log_err("fio: random postfix parsing failed\n"); + free(nr); + return 1; + } + + free(nr); + + if (td->o.random_distribution == FIO_RAND_DIST_ZIPF) { + if (val == 1.00) { + log_err("fio: zipf theta must different than 1.0\n"); + return 1; + } + td->o.zipf_theta = 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 @@ -732,20 +986,6 @@ static int str_verify_pattern_cb(void *data, const char *input) return 0; } -static int str_lockfile_cb(void *data, const char *str) -{ - struct thread_data *td = data; - char *nr = get_opt_postfix(str); - - td->o.lockfile_batch = 1; - if (nr) { - td->o.lockfile_batch = atoi(nr); - free(nr); - } - - return 0; -} - static int str_write_bw_log_cb(void *data, const char *str) { struct thread_data *td = data; @@ -891,6 +1131,14 @@ static struct fio_option options[FIO_MAX_OPTS] = { .prio = -1, /* must come after "directory" */ .help = "File(s) to use for the workload", }, + { + .name = "filename_format", + .type = FIO_OPT_STR_STORE, + .off1 = td_var_offset(filename_format), + .prio = -1, /* must come after "directory" */ + .help = "Override default $jobname.$jobnum.$filenum naming", + .def = "$jobname.$jobnum.$filenum", + }, { .name = "kb_base", .type = FIO_OPT_INT, @@ -903,7 +1151,6 @@ static struct fio_option options[FIO_MAX_OPTS] = { { .name = "lockfile", .type = FIO_OPT_STR, - .cb = str_lockfile_cb, .off1 = td_var_offset(file_lock_mode), .help = "Lock file when doing IO to it", .parent = "filename", @@ -1013,22 +1260,22 @@ static struct fio_option options[FIO_MAX_OPTS] = { { .ival = "vsync", .help = "Use readv/writev", }, -#ifdef FIO_HAVE_LIBAIO +#ifdef CONFIG_LIBAIO { .ival = "libaio", .help = "Linux native asynchronous IO", }, #endif -#ifdef FIO_HAVE_POSIXAIO +#ifdef CONFIG_POSIXAIO { .ival = "posixaio", .help = "POSIX asynchronous IO", }, #endif -#ifdef FIO_HAVE_SOLARISAIO +#ifdef CONFIG_SOLARISAIO { .ival = "solarisaio", .help = "Solaris native asynchronous IO", }, #endif -#ifdef FIO_HAVE_WINDOWSAIO +#ifdef CONFIG_WINDOWSAIO { .ival = "windowsaio", .help = "Windows native asynchronous IO" }, @@ -1036,7 +1283,7 @@ static struct fio_option options[FIO_MAX_OPTS] = { { .ival = "mmap", .help = "Memory mapped IO" }, -#ifdef FIO_HAVE_SPLICE +#ifdef CONFIG_LINUX_SPLICE { .ival = "splice", .help = "splice/vmsplice based IO", }, @@ -1055,15 +1302,10 @@ static struct fio_option options[FIO_MAX_OPTS] = { { .ival = "net", .help = "Network IO", }, -#ifdef FIO_HAVE_SYSLET - { .ival = "syslet-rw", - .help = "syslet enabled async pread/pwrite IO", - }, -#endif { .ival = "cpuio", .help = "CPU cycle burner engine", }, -#ifdef FIO_HAVE_GUASI +#ifdef CONFIG_GUASI { .ival = "guasi", .help = "GUASI IO engine", }, @@ -1073,22 +1315,22 @@ static struct fio_option options[FIO_MAX_OPTS] = { .help = "binject direct inject block engine", }, #endif -#ifdef FIO_HAVE_RDMA +#ifdef CONFIG_RDMA { .ival = "rdma", .help = "RDMA IO engine", }, #endif -#ifdef FIO_HAVE_FUSION_AW +#ifdef CONFIG_FUSION_AW { .ival = "fusion-aw-sync", .help = "Fusion-io atomic write engine", }, #endif -#ifdef FIO_HAVE_E4_ENG +#ifdef CONFIG_LINUX_EXT4_MOVE_EXTENT { .ival = "e4defrag", .help = "ext4 defrag engine", }, #endif -#ifdef FIO_HAVE_FALLOC_ENG +#ifdef CONFIG_LINUX_FALLOCATE { .ival = "falloc", .help = "fallocate() file based engine", }, @@ -1253,6 +1495,45 @@ static struct fio_option options[FIO_MAX_OPTS] = { .parent = "norandommap", .def = "0", }, + { + .name = "random_generator", + .type = FIO_OPT_STR, + .off1 = td_var_offset(random_generator), + .help = "Type of random number generator to use", + .def = "tausworthe", + .posval = { + { .ival = "tausworthe", + .oval = FIO_RAND_GEN_TAUSWORTHE, + .help = "Strong Tausworthe generator", + }, + { .ival = "lfsr", + .oval = FIO_RAND_GEN_LFSR, + .help = "Variable length LFSR", + }, + }, + }, + { + .name = "random_distribution", + .type = FIO_OPT_STR, + .off1 = td_var_offset(random_distribution), + .cb = str_random_distribution_cb, + .help = "Random offset distribution generator", + .def = "random", + .posval = { + { .ival = "random", + .oval = FIO_RAND_DIST_RANDOM, + .help = "Completely random", + }, + { .ival = "zipf", + .oval = FIO_RAND_DIST_ZIPF, + .help = "Zipf distribution", + }, + { .ival = "pareto", + .oval = FIO_RAND_DIST_PARETO, + .help = "Pareto distribution", + }, + }, + }, { .name = "nrfiles", .alias = "nr_files", @@ -1290,7 +1571,7 @@ static struct fio_option options[FIO_MAX_OPTS] = { }, .parent = "nrfiles", }, -#ifdef FIO_HAVE_FALLOCATE +#ifdef CONFIG_POSIX_FALLOCATE { .name = "fallocate", .type = FIO_OPT_STR, @@ -1306,7 +1587,7 @@ static struct fio_option options[FIO_MAX_OPTS] = { .oval = FIO_FALLOCATE_POSIX, .help = "Use posix_fallocate()", }, -#ifdef FIO_HAVE_LINUX_FALLOCATE +#ifdef CONFIG_LINUX_FALLOCATE { .ival = "keep", .oval = FIO_FALLOCATE_KEEP_SIZE, .help = "Use fallocate(..., FALLOC_FL_KEEP_SIZE, ...)", @@ -1323,7 +1604,7 @@ static struct fio_option options[FIO_MAX_OPTS] = { }, }, }, -#endif /* FIO_HAVE_FALLOCATE */ +#endif /* CONFIG_POSIX_FALLOCATE */ { .name = "fadvise_hint", .type = FIO_OPT_BOOL, @@ -1352,7 +1633,7 @@ static struct fio_option options[FIO_MAX_OPTS] = { .help = "Make every Nth write a barrier write", .def = "0", }, -#ifdef FIO_HAVE_SYNC_FILE_RANGE +#ifdef CONFIG_SYNC_FILE_RANGE { .name = "sync_file_range", .posval = { @@ -1449,14 +1730,18 @@ static struct fio_option options[FIO_MAX_OPTS] = { .off1 = td_var_offset(clocksource), .help = "What type of timing source to use", .posval = { +#ifdef CONFIG_GETTIMEOFDAY { .ival = "gettimeofday", .oval = CS_GTOD, .help = "Use gettimeofday(2) for timing", }, +#endif +#ifdef CONFIG_CLOCK_GETTIME { .ival = "clock_gettime", .oval = CS_CGETTIME, .help = "Use clock_gettime(2) for timing", }, +#endif #ifdef ARCH_HAVE_CPU_CLOCK { .ival = "cpu", .oval = CS_CPUCLOCK, @@ -1589,6 +1874,16 @@ static struct fio_option options[FIO_MAX_OPTS] = { .def = "1", .parent = "verify", }, + { + .name = "verifysort_nr", + .type = FIO_OPT_INT, + .off1 = td_var_offset(verifysort_nr), + .help = "Pre-load and sort verify blocks for a read workload", + .minval = 0, + .maxval = 131072, + .def = "1024", + .parent = "verify", + }, { .name = "verify_interval", .type = FIO_OPT_INT, @@ -1659,6 +1954,12 @@ static struct fio_option options[FIO_MAX_OPTS] = { .parent = "verify_async", }, #endif + { + .name = "experimental_verify", + .off1 = td_var_offset(experimental_verify), + .type = FIO_OPT_BOOL, + .help = "Enable experimental verification", + }, #ifdef FIO_HAVE_TRIM { .name = "trim_percentage", @@ -1879,6 +2180,12 @@ static struct fio_option options[FIO_MAX_OPTS] = { .def = "1000", .parent = "rate", }, + { + .name = "max_latency", + .type = FIO_OPT_INT, + .off1 = td_var_offset(max_latency), + .help = "Maximum tolerated IO latency (usec)", + }, { .name = "invalidate", .type = FIO_OPT_BOOL, @@ -1973,6 +2280,20 @@ static struct fio_option options[FIO_MAX_OPTS] = { .cb = str_cpus_allowed_cb, .help = "Set CPUs allowed", }, +#endif +#ifdef CONFIG_LIBNUMA + { + .name = "numa_cpu_nodes", + .type = FIO_OPT_STR, + .cb = str_numa_cpunodes_cb, + .help = "NUMA CPU nodes bind", + }, + { + .name = "numa_mem_policy", + .type = FIO_OPT_STR, + .cb = str_numa_mpol_cb, + .help = "NUMA memory policy setup", + }, #endif { .name = "end_fsync", @@ -2106,8 +2427,9 @@ static struct fio_option options[FIO_MAX_OPTS] = { .name = "percentile_list", .type = FIO_OPT_FLOAT_LIST, .off1 = td_var_offset(percentile_list), - .off2 = td_var_offset(overwrite_plist), + .off2 = td_var_offset(percentile_precision), .help = "Specify a custom list of percentiles to report", + .def = "1:5:10:20:30:40:50:60:70:80:90:95:99:99.5:99.9:99.95:99.99", .maxlen = FIO_IO_U_LIST_MAX_LEN, .minfp = 0.0, .maxfp = 100.0, @@ -2168,6 +2490,13 @@ static struct fio_option options[FIO_MAX_OPTS] = { .help = "Set up dedicated gettimeofday() thread on this CPU", .verify = gtod_cpu_verify, }, + { + .name = "unified_rw_reporting", + .type = FIO_OPT_BOOL, + .off1 = td_var_offset(unified_rw_rep), + .help = "Unify reporting across data direction", + .def = "0", + }, { .name = "continue_on_error", .type = FIO_OPT_STR, @@ -2209,6 +2538,21 @@ static struct fio_option options[FIO_MAX_OPTS] = { }, }, }, + { + .name = "ignore_error", + .type = FIO_OPT_STR, + .cb = str_ignore_error_cb, + .help = "Set a specific list of errors to ignore", + .parent = "rw", + }, + { + .name = "error_dump", + .type = FIO_OPT_BOOL, + .off1 = td_var_offset(error_dump), + .def = "0", + .help = "Dump info on each error", + }, + { .name = "profile", .type = FIO_OPT_STR_STORE, @@ -2383,7 +2727,7 @@ void fio_keywords_init(void) char buf[128]; long l; - sprintf(buf, "%lu", page_size); + sprintf(buf, "%lu", (unsigned long) page_size); fio_keywords[0].replace = strdup(buf); mb_memory = os_phys_mem() / (1024 * 1024);