X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=init.c;h=3532c49d968a7436699b8c9a3e75d9d5ed0d2a2e;hp=4ce2cc4bf973dfd728e70f3c5c34159063255ff9;hb=c7c280ed2e4f836bd8e9e125d55d097539b70e21;hpb=dad915e36819e74c4540db19faae488ede963ee4 diff --git a/init.c b/init.c index 4ce2cc4b..3532c49d 100644 --- a/init.c +++ b/init.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -49,13 +51,14 @@ #define DEF_UNLINK (0) #define DEF_WRITE_BW_LOG (0) #define DEF_WRITE_LAT_LOG (0) +#define DEF_NO_RAND_MAP (0) #define td_var_offset(var) ((size_t) &((struct thread_data *)0)->var) -static int str_rw_cb(void *, char *); -static int str_ioengine_cb(void *, char *); -static int str_mem_cb(void *, char *); -static int str_verify_cb(void *, char *); +static int str_rw_cb(void *, const char *); +static int str_ioengine_cb(void *, const char *); +static int str_mem_cb(void *, const char *); +static int str_verify_cb(void *, const char *); static int str_lockmem_cb(void *, unsigned long *); static int str_prio_cb(void *, unsigned int *); static int str_prioclass_cb(void *, unsigned int *); @@ -103,13 +106,13 @@ static struct fio_option options[] = { }, { .name = "write_iolog", - .type = FIO_OPT_INT, - .off1 = td_var_offset(write_iolog), + .type = FIO_OPT_STR_STORE, + .off1 = td_var_offset(write_iolog_file), }, { - .name = "iolog", + .name = "read_iolog", .type = FIO_OPT_STR_STORE, - .off1 = td_var_offset(iolog), + .off1 = td_var_offset(read_iolog_file), }, { .name = "exec_prerun", @@ -340,14 +343,68 @@ static struct fio_option options[] = { .type = FIO_OPT_STR_SET, .off1 = td_var_offset(write_lat_log), }, + { + .name = "norandommap", + .type = FIO_OPT_STR_SET, + .off1 = td_var_offset(norandommap), + }, + { + .name = "bs_unaligned", + .type = FIO_OPT_STR_SET, + .off1 = td_var_offset(bs_unaligned), + }, { .name = NULL, }, }; +#define FIO_JOB_OPTS (sizeof(options) / sizeof(struct fio_option)) +#define FIO_CMD_OPTS (16) +#define FIO_GETOPT_JOB (0x89988998) + +/* + * Command line options. These will contain the above, plus a few + * extra that only pertain to fio itself and not jobs. + */ +static struct option long_options[FIO_JOB_OPTS + FIO_CMD_OPTS] = { + { + .name = "output", + .has_arg = required_argument, + .val = 'o', + }, + { + .name = "timeout", + .has_arg = required_argument, + .val = 't', + }, + { + .name = "latency-log", + .has_arg = required_argument, + .val = 'l', + }, + { + .name = "bandwidth-log", + .has_arg = required_argument, + .val = 'b', + }, + { + .name = "minimal", + .has_arg = optional_argument, + .val = 'm', + }, + { + .name = "version", + .has_arg = no_argument, + .val = 'v', + }, + { + .name = NULL, + }, +}; + static int def_timeout = DEF_TIMEOUT; -static char fio_version_string[] = "fio 1.5"; +static char fio_version_string[] = "fio 1.7"; static char **ini_file; static int max_jobs = MAX_JOBS; @@ -379,7 +436,6 @@ static struct thread_data *get_new_job(int global, struct thread_data *parent) td = &threads[thread_number++]; *td = *parent; - td->name[0] = '\0'; td->thread_number = thread_number; return td; @@ -387,6 +443,9 @@ static struct thread_data *get_new_job(int global, struct thread_data *parent) static void put_job(struct thread_data *td) { + if (td == &def_thread) + return; + memset(&threads[td->thread_number - 1], 0, sizeof(*td)); thread_number--; } @@ -397,16 +456,47 @@ static void put_job(struct thread_data *td) */ static void fixup_options(struct thread_data *td) { + if (!td->rwmixread && td->rwmixwrite) + td->rwmixread = 100 - td->rwmixwrite; + + if (td->write_iolog_file && td->read_iolog_file) { + log_err("fio: read iolog overrides write_iolog\n"); + free(td->write_iolog_file); + td->write_iolog_file = NULL; + } + + if (td->io_ops->flags & FIO_SYNCIO) + td->iodepth = 1; + else { + if (!td->iodepth) + td->iodepth = td->nr_files; + } + + /* + * only really works for sequential io for now, and with 1 file + */ + if (td->zone_size && !td->sequential && td->nr_files == 1) + td->zone_size = 0; + + /* + * Reads can do overwrites, we always need to pre-create the file + */ + if (td_read(td) || td_rw(td)) + td->overwrite = 1; + if (!td->min_bs) td->min_bs = td->bs; if (!td->max_bs) td->max_bs = td->bs; + if (td_read(td) && !td_rw(td)) + td->verify = 0; - if (!td->rwmixread && td->rwmixwrite) - td->rwmixread = 100 - td->rwmixwrite; - - if (td->iolog && !td->write_iolog) - td->read_iolog = 1; + if (td->norandommap && td->verify != VERIFY_NONE) { + log_err("fio: norandommap given, verify disabled\n"); + td->verify = VERIFY_NONE; + } + if (td->bs_unaligned && (td->odirect || td->io_ops->flags & FIO_RAWIO)) + log_err("fio: bs_unaligned may not work with raw io\n"); } /* @@ -435,8 +525,6 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num) } #endif - fixup_options(td); - /* * the def_thread is just for options, it's not a real job */ @@ -454,24 +542,10 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num) } } - if (td->io_ops->flags & FIO_SYNCIO) - td->iodepth = 1; - else { - if (!td->iodepth) - td->iodepth = td->nr_files; - } - - /* - * only really works for sequential io for now, and with 1 file - */ - if (td->zone_size && !td->sequential && td->nr_files == 1) - td->zone_size = 0; + if (td->odirect) + td->io_ops->flags |= FIO_RAWIO; - /* - * Reads can do overwrites, we always need to pre-create the file - */ - if (td_read(td) || td_rw(td)) - td->overwrite = 1; + fixup_options(td); td->filetype = FIO_TYPE_FILE; if (!stat(jobname, &sb)) { @@ -481,9 +555,6 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num) td->filetype = FIO_TYPE_CHAR; } - if (td->odirect) - td->io_ops->flags |= FIO_RAWIO; - if (td->filename) td->nr_uniq_files = 1; else @@ -530,13 +601,6 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num) td->slat_stat[0].min_val = td->slat_stat[1].min_val = ULONG_MAX; td->bw_stat[0].min_val = td->bw_stat[1].min_val = ULONG_MAX; - if (td->min_bs == -1U) - td->min_bs = td->bs; - if (td->max_bs == -1U) - td->max_bs = td->bs; - if (td_read(td) && !td_rw(td)) - td->verify = 0; - if (td->stonewall && td->thread_number > 1) groupid++; @@ -552,8 +616,8 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num) if (td->write_bw_log) setup_log(&td->bw_log); - if (td->name[0] == '\0') - snprintf(td->name, sizeof(td->name)-1, "client%d", td->thread_number); + if (!td->name) + td->name = strdup(jobname); ddir = td->ddir + (!td->sequential << 1) + (td->iomix << 2); @@ -601,6 +665,9 @@ int init_random_state(struct thread_data *td) int fd, num_maps, blocks, i; struct fio_file *f; + if (td->io_ops->flags & FIO_CPUIO) + return 0; + fd = open("/dev/urandom", O_RDONLY); if (fd == -1) { td_verror(td, errno); @@ -625,12 +692,14 @@ int init_random_state(struct thread_data *td) if (td->rand_repeatable) seeds[3] = DEF_RANDSEED; - for_each_file(td, f, i) { - blocks = (f->file_size + td->min_bs - 1) / td->min_bs; - num_maps = blocks / BLOCKS_PER_MAP; - f->file_map = malloc(num_maps * sizeof(long)); - f->num_maps = num_maps; - memset(f->file_map, 0, num_maps * sizeof(long)); + if (!td->norandommap) { + for_each_file(td, f, i) { + blocks = (f->file_size + td->min_bs - 1) / td->min_bs; + num_maps = (blocks + BLOCKS_PER_MAP-1)/ BLOCKS_PER_MAP; + f->file_map = malloc(num_maps * sizeof(long)); + f->num_maps = num_maps; + memset(f->file_map, 0, num_maps * sizeof(long)); + } } os_random_seed(seeds[3], &td->random_state); @@ -665,7 +734,7 @@ static int is_empty_or_comment(char *line) return 1; } -static int str_rw_cb(void *data, char *mem) +static int str_rw_cb(void *data, const char *mem) { struct thread_data *td = data; @@ -701,7 +770,7 @@ static int str_rw_cb(void *data, char *mem) return 1; } -static int str_verify_cb(void *data, char *mem) +static int str_verify_cb(void *data, const char *mem) { struct thread_data *td = data; @@ -720,7 +789,7 @@ static int str_verify_cb(void *data, char *mem) return 1; } -static int str_mem_cb(void *data, char *mem) +static int str_mem_cb(void *data, const char *mem) { struct thread_data *td = data; @@ -739,7 +808,7 @@ static int str_mem_cb(void *data, char *mem) return 1; } -static int str_ioengine_cb(void *data, char *str) +static int str_ioengine_cb(void *data, const char *str) { struct thread_data *td = data; @@ -794,7 +863,7 @@ int parse_jobs_ini(char *file, int stonewall_flag) { unsigned int global; struct thread_data *td; - char *string, *name, *tmpbuf; + char *string, *name; fpos_t off; FILE *f; char *p; @@ -808,15 +877,16 @@ int parse_jobs_ini(char *file, int stonewall_flag) string = malloc(4096); name = malloc(256); - tmpbuf = malloc(4096); + memset(name, 0, 256); stonewall = stonewall_flag; - while ((p = fgets(string, 4096, f)) != NULL) { - if (ret) + do { + p = fgets(string, 4095, f); + if (!p) break; if (is_empty_or_comment(p)) continue; - if (sscanf(p, "[%s]", name) != 1) + if (sscanf(p, "[%255s]", name) != 1) continue; global = !strncmp(name, "global", 6); @@ -841,10 +911,12 @@ int parse_jobs_ini(char *file, int stonewall_flag) while ((p = fgets(string, 4096, f)) != NULL) { if (is_empty_or_comment(p)) continue; - if (strstr(p, "[")) - break; strip_blank_front(&p); + + if (p[0] == '[') + break; + strip_blank_end(p); fgetpos(f, &off); @@ -854,20 +926,20 @@ int parse_jobs_ini(char *file, int stonewall_flag) * dump all the bad ones. Makes trial/error fixups * easier on the user. */ - ret = parse_option(p, options, td); + ret |= parse_option(p, options, td); } if (!ret) { fsetpos(f, &off); ret = add_job(td, name, 0); + } else { + log_err("fio: job %s dropped\n", name); + put_job(td); } - if (ret) - break; - } + } while (!ret); free(string); free(name); - free(tmpbuf); fclose(f); return ret; } @@ -887,8 +959,8 @@ static int fill_def_thread(void) def_thread.ddir = DDIR_READ; def_thread.iomix = 0; def_thread.bs = DEF_BS; - def_thread.min_bs = -1; - def_thread.max_bs = -1; + def_thread.min_bs = 0; + def_thread.max_bs = 0; def_thread.odirect = DEF_ODIRECT; def_thread.ratecycle = DEF_RATE_CYCLE; def_thread.sequential = DEF_SEQUENTIAL; @@ -913,6 +985,7 @@ static int fill_def_thread(void) def_thread.unlink = DEF_UNLINK; def_thread.write_bw_log = write_bw_log; def_thread.write_lat_log = write_lat_log; + def_thread.norandommap = DEF_NO_RAND_MAP; #ifdef FIO_HAVE_DISK_UTIL def_thread.do_disk_util = 1; #endif @@ -923,64 +996,92 @@ static int fill_def_thread(void) static void usage(void) { printf("%s\n", fio_version_string); - printf("\t-o Write output to file\n"); - printf("\t-t Runtime in seconds\n"); - printf("\t-l Generate per-job latency logs\n"); - printf("\t-w Generate per-job bandwidth logs\n"); - printf("\t-m Minimal (terse) output\n"); - printf("\t-v Print version info and exit\n"); + printf("\t--output\tWrite output to file\n"); + printf("\t--timeout\tRuntime in seconds\n"); + printf("\t--latency-log\tGenerate per-job latency logs\n"); + printf("\t--bandwidth-log\tGenerate per-job bandwidth logs\n"); + printf("\t--minimal\tMinimal (terse) output\n"); + printf("\t--version\tPrint version info and exit\n"); } static int parse_cmd_line(int argc, char *argv[]) { - int c, idx = 1, ini_idx = 0; + struct thread_data *td = NULL; + int c, ini_idx = 0, lidx, ret; - while ((c = getopt(argc, argv, "t:o:lwvhm")) != EOF) { + while ((c = getopt_long(argc, argv, "", long_options, &lidx)) != -1) { switch (c) { - case 't': - def_timeout = atoi(optarg); - idx = optind; - break; - case 'l': - write_lat_log = 1; - idx = optind; - break; - case 'w': - write_bw_log = 1; - idx = optind; - break; - case 'o': - f_out = fopen(optarg, "w+"); - if (!f_out) { - perror("fopen output"); - exit(1); + case 't': + def_timeout = atoi(optarg); + break; + case 'l': + write_lat_log = 1; + break; + case 'w': + write_bw_log = 1; + break; + case 'o': + f_out = fopen(optarg, "w+"); + if (!f_out) { + perror("fopen output"); + exit(1); + } + f_err = f_out; + break; + case 'm': + terse_output = 1; + break; + case 'h': + usage(); + exit(0); + case 'v': + printf("%s\n", fio_version_string); + exit(0); + case FIO_GETOPT_JOB: { + const char *opt = long_options[lidx].name; + char *val = optarg; + + if (!strncmp(opt, "name", 4) && td) { + ret = add_job(td, td->name ?: "fio", 0); + if (ret) { + put_job(td); + return 0; } - f_err = f_out; - idx = optind; - break; - case 'm': - terse_output = 1; - idx = optind; - break; - case 'h': - usage(); - exit(0); - case 'v': - printf("%s\n", fio_version_string); - exit(0); + td = NULL; + } + if (!td) { + int global = !strncmp(val, "global", 6); + + td = get_new_job(global, &def_thread); + if (!td) + return 0; + } + + ret = parse_cmd_option(opt, val, options, td); + if (ret) { + log_err("fio: job dropped\n"); + put_job(td); + td = NULL; + } + break; + } + default: + printf("optarg <<%s>>\n", argv[optind]); + break; } } - while (idx < argc) { - ini_idx++; - ini_file = realloc(ini_file, ini_idx * sizeof(char *)); - ini_file[ini_idx - 1] = strdup(argv[idx]); - idx++; + if (td) { + ret = add_job(td, td->name ?: "fio", 0); + if (ret) + put_job(td); } - if (!f_out) { - f_out = stdout; - f_err = stderr; + while (optind < argc) { + ini_idx++; + ini_file = realloc(ini_file, ini_idx * sizeof(char *)); + ini_file[ini_idx - 1] = strdup(argv[optind]); + optind++; } return ini_idx; @@ -1035,21 +1136,49 @@ static int setup_thread_area(void) return 0; } +/* + * Copy the fio options into the long options map, so we mirror + * job and cmd line options. + */ +static void dupe_job_options(void) +{ + struct fio_option *o; + unsigned int i; + + i = 0; + while (long_options[i].name) + i++; + + o = &options[0]; + while (o->name) { + long_options[i].name = o->name; + long_options[i].val = FIO_GETOPT_JOB; + if (o->type == FIO_OPT_STR_SET) + long_options[i].has_arg = no_argument; + else + long_options[i].has_arg = required_argument; + + i++; + o++; + assert(i < FIO_JOB_OPTS + FIO_CMD_OPTS); + } +} + int parse_options(int argc, char *argv[]) { int job_files, i; + f_out = stdout; + f_err = stderr; + + dupe_job_options(); + if (setup_thread_area()) return 1; if (fill_def_thread()) return 1; job_files = parse_cmd_line(argc, argv); - if (!job_files) { - log_err("Need job file(s)\n"); - usage(); - return 1; - } for (i = 0; i < job_files; i++) { if (fill_def_thread()) @@ -1060,5 +1189,11 @@ int parse_options(int argc, char *argv[]) } free(ini_file); + + if (!thread_number) { + log_err("No jobs defined(s)\n"); + return 1; + } + return 0; }