X-Git-Url: https://git.kernel.dk/?a=blobdiff_plain;ds=sidebyside;f=init.c;h=6604a18f2f3750f71811760f3bba2c96e44368ec;hb=d79db1222039e906dd49ae290daa59701f4e2385;hp=3865d099c94c569771571323920b4598c7a79fcf;hpb=0922d61ffe92cf10a563d7168decec84ab7a0d16;p=fio.git diff --git a/init.c b/init.c index 3865d099..6604a18f 100644 --- a/init.c +++ b/init.c @@ -36,9 +36,8 @@ static struct thread_data def_thread; struct thread_data *threads = NULL; int exitall_on_terminate = 0; -int terse_output = 0; +int output_format = FIO_OUTPUT_NORMAL; int eta_print; -unsigned long long mlock_size = 0; FILE *f_out = NULL; FILE *f_err = NULL; char **job_sections = NULL; @@ -116,6 +115,11 @@ static struct option l_opts[FIO_NR_OPTIONS] = { .has_arg = optional_argument, .val = 'm' | FIO_CLIENT_FLAG, }, + { + .name = (char *) "output-format", + .has_arg = optional_argument, + .val = 'F' | FIO_CLIENT_FLAG, + }, { .name = (char *) "version", .has_arg = no_argument, @@ -205,7 +209,7 @@ static struct option l_opts[FIO_NR_OPTIONS] = { }, }; -static void free_shm(void) +void free_threads_shm(void) { struct shmid_ds sbuf; @@ -213,11 +217,19 @@ static void free_shm(void) void *tp = threads; threads = NULL; + shmdt(tp); + shmctl(shm_id, IPC_RMID, &sbuf); + shm_id = -1; + } +} + +void free_shm(void) +{ + if (threads) { file_hash_exit(); flow_exit(); fio_debug_jobp = NULL; - shmdt(tp); - shmctl(shm_id, IPC_RMID, &sbuf); + free_threads_shm(); } scleanup(); @@ -362,6 +374,8 @@ static int setup_rate(struct thread_data *td) ret = __setup_rate(td, DDIR_READ); if (td->o.rate[DDIR_WRITE] || td->o.rate_iops[DDIR_WRITE]) ret |= __setup_rate(td, DDIR_WRITE); + if (td->o.rate[DDIR_TRIM] || td->o.rate_iops[DDIR_TRIM]) + ret |= __setup_rate(td, DDIR_TRIM); return ret; } @@ -370,7 +384,9 @@ static int fixed_block_size(struct thread_options *o) { return o->min_bs[DDIR_READ] == o->max_bs[DDIR_READ] && o->min_bs[DDIR_WRITE] == o->max_bs[DDIR_WRITE] && - o->min_bs[DDIR_READ] == o->min_bs[DDIR_WRITE]; + o->min_bs[DDIR_TRIM] == o->max_bs[DDIR_TRIM] && + o->min_bs[DDIR_READ] == o->min_bs[DDIR_WRITE] && + o->min_bs[DDIR_READ] == o->min_bs[DDIR_TRIM]; } /* @@ -426,8 +442,14 @@ static int fixup_options(struct thread_data *td) o->min_bs[DDIR_WRITE] = o->bs[DDIR_WRITE]; if (!o->max_bs[DDIR_WRITE]) o->max_bs[DDIR_WRITE] = o->bs[DDIR_WRITE]; + if (!o->min_bs[DDIR_TRIM]) + o->min_bs[DDIR_TRIM] = o->bs[DDIR_TRIM]; + if (!o->max_bs[DDIR_TRIM]) + o->max_bs[DDIR_TRIM] = o->bs[DDIR_TRIM]; + o->rw_min_bs = min(o->min_bs[DDIR_READ], o->min_bs[DDIR_WRITE]); + o->rw_min_bs = min(o->min_bs[DDIR_TRIM], o->rw_min_bs); /* * For random IO, allow blockalign offset other than min_bs. @@ -436,9 +458,12 @@ static int fixup_options(struct thread_data *td) o->ba[DDIR_READ] = o->min_bs[DDIR_READ]; if (!o->ba[DDIR_WRITE] || !td_random(td)) o->ba[DDIR_WRITE] = o->min_bs[DDIR_WRITE]; + if (!o->ba[DDIR_TRIM] || !td_random(td)) + o->ba[DDIR_TRIM] = o->min_bs[DDIR_TRIM]; if ((o->ba[DDIR_READ] != o->min_bs[DDIR_READ] || - o->ba[DDIR_WRITE] != o->min_bs[DDIR_WRITE]) && + o->ba[DDIR_WRITE] != o->min_bs[DDIR_WRITE] || + o->ba[DDIR_TRIM] != o->min_bs[DDIR_TRIM]) && !o->norandommap) { log_err("fio: Any use of blockalign= turns off randommap\n"); o->norandommap = 1; @@ -491,15 +516,19 @@ static int fixup_options(struct thread_data *td) if (o->open_files > o->nr_files || !o->open_files) o->open_files = o->nr_files; - if (((o->rate[0] + o->rate[1]) && (o->rate_iops[0] + o->rate_iops[1]))|| - ((o->ratemin[0] + o->ratemin[1]) && (o->rate_iops_min[0] + - o->rate_iops_min[1]))) { + if (((o->rate[DDIR_READ] + o->rate[DDIR_WRITE] + o->rate[DDIR_TRIM]) && + (o->rate_iops[DDIR_READ] + o->rate_iops[DDIR_WRITE] + o->rate_iops[DDIR_TRIM])) || + ((o->ratemin[DDIR_READ] + o->ratemin[DDIR_WRITE] + o->ratemin[DDIR_TRIM]) && + (o->rate_iops_min[DDIR_READ] + o->rate_iops_min[DDIR_WRITE] + o->rate_iops_min[DDIR_TRIM]))) { log_err("fio: rate and rate_iops are mutually exclusive\n"); ret = 1; } - if ((o->rate[0] < o->ratemin[0]) || (o->rate[1] < o->ratemin[1]) || - (o->rate_iops[0] < o->rate_iops_min[0]) || - (o->rate_iops[1] < o->rate_iops_min[1])) { + if ((o->rate[DDIR_READ] < o->ratemin[DDIR_READ]) || + (o->rate[DDIR_WRITE] < o->ratemin[DDIR_WRITE]) || + (o->rate[DDIR_TRIM] < o->ratemin[DDIR_TRIM]) || + (o->rate_iops[DDIR_READ] < o->rate_iops_min[DDIR_READ]) || + (o->rate_iops[DDIR_WRITE] < o->rate_iops_min[DDIR_WRITE]) || + (o->rate_iops[DDIR_TRIM] < o->rate_iops_min[DDIR_TRIM])) { log_err("fio: minimum rate exceeds rate\n"); ret = 1; } @@ -576,7 +605,7 @@ static int fixup_options(struct thread_data *td) /* * This function leaks the buffer */ -static char *to_kmg(unsigned int val) +char *fio_uint_to_kmg(unsigned int val) { char *buf = malloc(32); char post[] = { 0, 'K', 'M', 'G', 'P', 'E', 0 }; @@ -737,10 +766,9 @@ int ioengine_load(struct thread_data *td) * to make sure we don't have conflicts, and initializes various * members of td. */ -static int add_job(struct thread_data *td, const char *jobname, int job_add_num) +static int add_job(struct thread_data *td, const char *jobname, int job_add_num, + int recursed, int client_type) { - const char *ddir_str[] = { NULL, "read", "write", "rw", NULL, - "randread", "randwrite", "randrw" }; unsigned int i; char fname[PATH_MAX]; int numjobs, file_alloced; @@ -759,17 +787,14 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num) return 0; } + td->client_type = client_type; + if (profile_td_init(td)) goto err; if (ioengine_load(td)) goto err; - if (td->o.use_thread) - nr_thread++; - else - nr_process++; - if (td->o.odirect) td->io_ops->flags |= FIO_RAWIO; @@ -807,7 +832,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num) f->real_file_size = -1ULL; } - td->mutex = fio_mutex_init(0); + td->mutex = fio_mutex_init(FIO_MUTEX_LOCKED); td->ts.clat_percentiles = td->o.clat_percentiles; if (td->o.overwrite_plist) @@ -815,10 +840,12 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num) else memcpy(td->ts.percentile_list, def_percentile_list, sizeof(def_percentile_list)); - td->ts.clat_stat[0].min_val = td->ts.clat_stat[1].min_val = ULONG_MAX; - td->ts.slat_stat[0].min_val = td->ts.slat_stat[1].min_val = ULONG_MAX; - td->ts.lat_stat[0].min_val = td->ts.lat_stat[1].min_val = ULONG_MAX; - td->ts.bw_stat[0].min_val = td->ts.bw_stat[1].min_val = ULONG_MAX; + for (i = 0; i < DDIR_RWDIR_CNT; i++) { + td->ts.clat_stat[i].min_val = ULONG_MAX; + td->ts.slat_stat[i].min_val = ULONG_MAX; + td->ts.lat_stat[i].min_val = ULONG_MAX; + td->ts.bw_stat[i].min_val = ULONG_MAX; + } td->ddir_seq_nr = td->o.ddir_seq_nr; if ((td->o.stonewall || td->o.new_group) && prev_group_jobs) { @@ -837,39 +864,39 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num) if (setup_rate(td)) goto err; - if (td->o.write_lat_log) { - setup_log(&td->lat_log, td->o.log_avg_msec); - setup_log(&td->slat_log, td->o.log_avg_msec); - setup_log(&td->clat_log, td->o.log_avg_msec); + if (td->o.lat_log_file) { + setup_log(&td->lat_log, td->o.log_avg_msec, IO_LOG_TYPE_LAT); + setup_log(&td->slat_log, td->o.log_avg_msec, IO_LOG_TYPE_SLAT); + setup_log(&td->clat_log, td->o.log_avg_msec, IO_LOG_TYPE_CLAT); } - if (td->o.write_bw_log) - setup_log(&td->bw_log, td->o.log_avg_msec); - if (td->o.write_iops_log) - setup_log(&td->iops_log, td->o.log_avg_msec); + if (td->o.bw_log_file) + setup_log(&td->bw_log, td->o.log_avg_msec, IO_LOG_TYPE_BW); + if (td->o.iops_log_file) + setup_log(&td->iops_log, td->o.log_avg_msec, IO_LOG_TYPE_IOPS); if (!td->o.name) td->o.name = strdup(jobname); - if (!terse_output) { + if (output_format == FIO_OUTPUT_NORMAL) { if (!job_add_num) { - if (!strcmp(td->io_ops->name, "cpuio")) { - log_info("%s: ioengine=cpu, cpuload=%u," - " cpucycle=%u\n", td->o.name, - td->o.cpuload, - td->o.cpucycle); - } else { - char *c1, *c2, *c3, *c4; + if (is_backend && !recursed) + fio_server_send_add_job(td); + + if (!(td->io_ops->flags & FIO_NOIO)) { + char *c1, *c2, *c3, *c4, *c5, *c6; - c1 = to_kmg(td->o.min_bs[DDIR_READ]); - c2 = to_kmg(td->o.max_bs[DDIR_READ]); - c3 = to_kmg(td->o.min_bs[DDIR_WRITE]); - c4 = to_kmg(td->o.max_bs[DDIR_WRITE]); + c1 = fio_uint_to_kmg(td->o.min_bs[DDIR_READ]); + c2 = fio_uint_to_kmg(td->o.max_bs[DDIR_READ]); + c3 = fio_uint_to_kmg(td->o.min_bs[DDIR_WRITE]); + c4 = fio_uint_to_kmg(td->o.max_bs[DDIR_WRITE]); + c5 = fio_uint_to_kmg(td->o.min_bs[DDIR_TRIM]); + c6 = fio_uint_to_kmg(td->o.max_bs[DDIR_TRIM]); - log_info("%s: (g=%d): rw=%s, bs=%s-%s/%s-%s," + log_info("%s: (g=%d): rw=%s, bs=%s-%s/%s-%s/%s-%s," " ioengine=%s, iodepth=%u\n", td->o.name, td->groupid, - ddir_str[td->o.td_ddir], - c1, c2, c3, c4, + ddir_str(td->o.td_ddir), + c1, c2, c3, c4, c5, c6, td->io_ops->name, td->o.iodepth); @@ -877,6 +904,8 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num) free(c2); free(c3); free(c4); + free(c5); + free(c6); } } else if (job_add_num == 1) log_info("...\n"); @@ -906,7 +935,7 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num) job_add_num = numjobs - 1; - if (add_job(td_new, jobname, job_add_num)) + if (add_job(td_new, jobname, job_add_num, 1, client_type)) goto err; } @@ -919,7 +948,7 @@ err: /* * Parse as if 'o' was a command line */ -void add_job_opts(const char **o) +void add_job_opts(const char **o, int client_type) { struct thread_data *td, *td_parent; int i, in_global = 1; @@ -931,7 +960,7 @@ void add_job_opts(const char **o) if (!strncmp(o[i], "name", 4)) { in_global = 0; if (td) - add_job(td, jobname, 0); + add_job(td, jobname, 0, 0, client_type); td = NULL; sprintf(jobname, "%s", o[i] + 5); } @@ -950,7 +979,7 @@ void add_job_opts(const char **o) } if (td) - add_job(td, jobname, 0); + add_job(td, jobname, 0, 0, client_type); } static int skip_this_section(const char *name) @@ -988,7 +1017,7 @@ static int is_empty_or_comment(char *line) /* * This is our [ini] type file parser. */ -int parse_jobs_ini(char *file, int is_buf, int stonewall_flag) +int parse_jobs_ini(char *file, int is_buf, int stonewall_flag, int type) { unsigned int global; struct thread_data *td; @@ -1132,7 +1161,7 @@ int parse_jobs_ini(char *file, int is_buf, int stonewall_flag) for (i = 0; i < num_opts; i++) log_info("--%s ", opts[i]); - ret = add_job(td, name, 0); + ret = add_job(td, name, 0, 0, type); } else { log_err("fio: job %s dropped\n", name); put_job(td); @@ -1185,12 +1214,13 @@ static void usage(const char *name) "\t\t\tprocess,file,io,mem,blktrace,verify,random,parse,\n" "\t\t\tdiskutil,job,mutex,profile,time,net\n"); printf(" --output\t\tWrite output to file\n"); - printf(" --timeout\t\tRuntime in seconds\n"); + printf(" --runtime\t\tRuntime in seconds\n"); printf(" --latency-log\t\tGenerate per-job latency logs\n"); printf(" --bandwidth-log\tGenerate per-job bandwidth logs\n"); printf(" --minimal\t\tMinimal (terse) output\n"); - printf(" --version\t\tPrint version info and exit\n"); + printf(" --output-format=x\tOutput format (terse,json,normal)\n"); printf(" --terse-version=x\tSet terse version output format to 'x'\n"); + printf(" --version\t\tPrint version info and exit\n"); printf(" --help\t\tPrint this page\n"); printf(" --cmdhelp=cmd\t\tPrint command help, \"all\" for all of" " them\n"); @@ -1217,20 +1247,62 @@ static void usage(const char *name) #ifdef FIO_INC_DEBUG struct debug_level debug_levels[] = { - { .name = "process", .shift = FD_PROCESS, }, - { .name = "file", .shift = FD_FILE, }, - { .name = "io", .shift = FD_IO, }, - { .name = "mem", .shift = FD_MEM, }, - { .name = "blktrace", .shift = FD_BLKTRACE }, - { .name = "verify", .shift = FD_VERIFY }, - { .name = "random", .shift = FD_RANDOM }, - { .name = "parse", .shift = FD_PARSE }, - { .name = "diskutil", .shift = FD_DISKUTIL }, - { .name = "job", .shift = FD_JOB }, - { .name = "mutex", .shift = FD_MUTEX }, - { .name = "profile", .shift = FD_PROFILE }, - { .name = "time", .shift = FD_TIME }, - { .name = "net", .shift = FD_NET }, + { .name = "process", + .help = "Process creation/exit logging", + .shift = FD_PROCESS, + }, + { .name = "file", + .help = "File related action logging", + .shift = FD_FILE, + }, + { .name = "io", + .help = "IO and IO engine action logging (offsets, queue, completions, etc)", + .shift = FD_IO, + }, + { .name = "mem", + .help = "Memory allocation/freeing logging", + .shift = FD_MEM, + }, + { .name = "blktrace", + .help = "blktrace action logging", + .shift = FD_BLKTRACE, + }, + { .name = "verify", + .help = "IO verification action logging", + .shift = FD_VERIFY, + }, + { .name = "random", + .help = "Random generation logging", + .shift = FD_RANDOM, + }, + { .name = "parse", + .help = "Parser logging", + .shift = FD_PARSE, + }, + { .name = "diskutil", + .help = "Disk utility logging actions", + .shift = FD_DISKUTIL, + }, + { .name = "job", + .help = "Logging related to creating/destroying jobs", + .shift = FD_JOB, + }, + { .name = "mutex", + .help = "Mutex logging", + .shift = FD_MUTEX + }, + { .name = "profile", + .help = "Logging related to profiles", + .shift = FD_PROFILE, + }, + { .name = "time", + .help = "Logging related to time keeping functions", + .shift = FD_TIME, + }, + { .name = "net", + .help = "Network logging", + .shift = FD_NET, + }, { .name = NULL, }, }; @@ -1337,7 +1409,7 @@ void parse_cmd_client(void *client, char *opt) fio_client_add_cmd_option(client, opt); } -int parse_cmd_line(int argc, char *argv[]) +int parse_cmd_line(int argc, char *argv[], int client_type) { struct thread_data *td = NULL; int c, ini_idx = 0, lidx, ret = 0, do_exit = 0, exit_val = 0; @@ -1382,7 +1454,17 @@ int parse_cmd_line(int argc, char *argv[]) f_err = f_out; break; case 'm': - terse_output = 1; + output_format = FIO_OUTPUT_TERSE; + break; + case 'F': + if (!strcmp(optarg, "minimal") || + !strcmp(optarg, "terse") || + !strcmp(optarg, "csv")) + output_format = FIO_OUTPUT_TERSE; + else if (!strcmp(optarg, "json")) + output_format = FIO_OUTPUT_JSON; + else + output_format = FIO_OUTPUT_NORMAL; break; case 'h': if (!cur_client) { @@ -1416,7 +1498,8 @@ int parse_cmd_line(int argc, char *argv[]) break; case 'V': terse_version = atoi(optarg); - if (!(terse_version == 2 || terse_version == 3)) { + if (!(terse_version == 2 || terse_version == 3) || + (terse_version == 4)) { log_err("fio: bad terse version format\n"); exit_val = 1; do_exit++; @@ -1456,7 +1539,7 @@ int parse_cmd_line(int argc, char *argv[]) char *val = optarg; if (!strncmp(opt, "name", 4) && td) { - ret = add_job(td, td->o.name ?: "fio", 0); + ret = add_job(td, td->o.name ?: "fio", 0, 0, client_type); if (ret) return 0; td = NULL; @@ -1528,12 +1611,24 @@ int parse_cmd_line(int argc, char *argv[]) exit_val = 1; break; } - if (fio_client_add(optarg, &cur_client)) { + if (fio_client_add(&fio_client_ops, optarg, &cur_client)) { log_err("fio: failed adding client %s\n", optarg); do_exit++; exit_val = 1; break; } + /* + * If the next argument exists and isn't an option, + * assume it's a job file for this client only. + */ + while (optind < argc) { + if (!strncmp(argv[optind], "--", 2) || + !strncmp(argv[optind], "-", 1)) + break; + + fio_client_add_ini_file(cur_client, argv[optind]); + optind++; + } break; default: do_exit++; @@ -1560,7 +1655,7 @@ int parse_cmd_line(int argc, char *argv[]) if (td) { if (!ret) - ret = add_job(td, td->o.name ?: "fio", 0); + ret = add_job(td, td->o.name ?: "fio", 0, 0, client_type); } while (!ret && optind < argc) { @@ -1573,10 +1668,8 @@ int parse_cmd_line(int argc, char *argv[]) return ini_idx; } -int parse_options(int argc, char *argv[]) +int fio_init_options(void) { - int job_files, i; - f_out = stdout; f_err = stderr; @@ -1588,7 +1681,22 @@ int parse_options(int argc, char *argv[]) if (fill_def_thread()) return 1; - job_files = parse_cmd_line(argc, argv); + return 0; +} + +extern int fio_check_options(struct thread_options *); + +int parse_options(int argc, char *argv[]) +{ + const int type = FIO_CLIENT_TYPE_CLI; + int job_files, i; + + if (fio_init_options()) + return 1; + if (fio_test_cconv(&def_thread.o)) + log_err("fio: failed internal cconv test\n"); + + job_files = parse_cmd_line(argc, argv, type); if (job_files > 0) { for (i = 0; i < job_files; i++) { @@ -1599,11 +1707,16 @@ int parse_options(int argc, char *argv[]) return 1; free(ini_file[i]); } else if (!is_backend) { - if (parse_jobs_ini(ini_file[i], 0, i)) + if (parse_jobs_ini(ini_file[i], 0, i, type)) return 1; free(ini_file[i]); } } + } else if (nr_clients) { + if (fill_def_thread()) + return 1; + if (fio_clients_send_ini(NULL)) + return 1; } free(ini_file); @@ -1635,8 +1748,13 @@ int parse_options(int argc, char *argv[]) fio_gtod_cpu = def_thread.o.gtod_cpu; } - if (!terse_output) + if (output_format == FIO_OUTPUT_NORMAL) log_info("%s\n", fio_version_string); return 0; } + +void options_default_fill(struct thread_options *o) +{ + memcpy(o, &def_thread.o, sizeof(*o)); +}