static int init_submit_worker(struct submit_worker *sw) { struct thread_data *parent = sw->wq->td; struct thread_data *td = &sw->td; int fio_unused ret; memcpy(&td->o, &parent->o, sizeof(td->o)); memcpy(&td->ts, &parent->ts, sizeof(td->ts)); td->o.uid = td->o.gid = -1U; dup_files(td, parent); td->eo = parent->eo; fio_options_mem_dupe(td); if (ioengine_load(td)) goto err; if (td->o.odirect) td->io_ops->flags |= FIO_RAWIO; td->pid = gettid(); INIT_FLIST_HEAD(&td->io_log_list); INIT_FLIST_HEAD(&td->io_hist_list); INIT_FLIST_HEAD(&td->verify_list); INIT_FLIST_HEAD(&td->trim_list); INIT_FLIST_HEAD(&td->next_rand_list); td->io_hist_tree = RB_ROOT; td->o.iodepth = 1; if (td_io_init(td)) goto err_io_init; fio_gettime(&td->epoch, NULL); fio_getrusage(&td->ru_start); clear_io_state(td); td_set_runstate(td, TD_RUNNING); td->flags |= TD_F_CHILD; td->parent = parent; return 0; err_io_init: close_ioengine(td); err: return 1; }
static int io_workqueue_init_worker_fn(struct submit_worker *sw) { struct thread_data *parent = sw->wq->td; struct thread_data *td = sw->priv; memcpy(&td->o, &parent->o, sizeof(td->o)); memcpy(&td->ts, &parent->ts, sizeof(td->ts)); td->o.uid = td->o.gid = -1U; dup_files(td, parent); td->eo = parent->eo; fio_options_mem_dupe(td); if (ioengine_load(td)) goto err; td->pid = gettid(); INIT_FLIST_HEAD(&td->io_log_list); INIT_FLIST_HEAD(&td->io_hist_list); INIT_FLIST_HEAD(&td->verify_list); INIT_FLIST_HEAD(&td->trim_list); td->io_hist_tree = RB_ROOT; td->o.iodepth = 1; if (td_io_init(td)) goto err_io_init; set_epoch_time(td, td->o.log_unix_epoch); fio_getrusage(&td->ru_start); clear_io_state(td, 1); td_set_runstate(td, TD_RUNNING); td->flags |= TD_F_CHILD | TD_F_NEED_LOCK; td->parent = parent; return 0; err_io_init: close_ioengine(td); err: return 1; }
/* * Adds a job to the list of things todo. Sanitizes the various options * 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) { const char *ddir_str[] = { NULL, "read", "write", "rw", NULL, "randread", "randwrite", "randrw" }; unsigned int i; char fname[PATH_MAX]; int numjobs, file_alloced; /* * the def_thread is just for options, it's not a real job */ if (td == &def_thread) return 0; /* * if we are just dumping the output command line, don't add the job */ if (dump_cmdline) { put_job(td); return 0; } 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; file_alloced = 0; if (!td->o.filename && !td->files_index && !td->o.read_iolog_file) { file_alloced = 1; if (td->o.nr_files == 1 && exists_and_not_file(jobname)) add_file(td, jobname); else { for (i = 0; i < td->o.nr_files; i++) { sprintf(fname, "%s.%d.%d", jobname, td->thread_number, i); add_file(td, fname); } } } if (fixup_options(td)) goto err; flow_init_job(td); /* * IO engines only need this for option callbacks, and the address may * change in subprocesses. */ if (td->eo) *(struct thread_data **)td->eo = NULL; if (td->io_ops->flags & FIO_DISKLESSIO) { struct fio_file *f; for_each_file(td, f, i) f->real_file_size = -1ULL; } td->mutex = fio_mutex_init(0); td->ts.clat_percentiles = td->o.clat_percentiles; if (td->o.overwrite_plist) memcpy(td->ts.percentile_list, td->o.percentile_list, sizeof(td->o.percentile_list)); 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; td->ddir_seq_nr = td->o.ddir_seq_nr; if ((td->o.stonewall || td->o.new_group) && prev_group_jobs) { prev_group_jobs = 0; groupid++; } td->groupid = groupid; prev_group_jobs++; if (init_random_state(td, td->rand_seeds, sizeof(td->rand_seeds))) { td_verror(td, errno, "init_random_state"); goto err; } 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.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.name) td->o.name = strdup(jobname); if (!terse_output) { 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; 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]); log_info("%s: (g=%d): rw=%s, bs=%s-%s/%s-%s," " ioengine=%s, iodepth=%u\n", td->o.name, td->groupid, ddir_str[td->o.td_ddir], c1, c2, c3, c4, td->io_ops->name, td->o.iodepth); free(c1); free(c2); free(c3); free(c4); } } else if (job_add_num == 1) log_info("...\n"); } /* * recurse add identical jobs, clear numjobs and stonewall options * as they don't apply to sub-jobs */ numjobs = td->o.numjobs; while (--numjobs) { struct thread_data *td_new = get_new_job(0, td, 1); if (!td_new) goto err; td_new->o.numjobs = 1; td_new->o.stonewall = 0; td_new->o.new_group = 0; if (file_alloced) { td_new->o.filename = NULL; td_new->files_index = 0; td_new->files_size = 0; td_new->files = NULL; } job_add_num = numjobs - 1; if (add_job(td_new, jobname, job_add_num)) goto err; } return 0; err: put_job(td); return -1; }
int parse_cmd_line(int argc, char *argv[]) { struct thread_data *td = NULL; int c, ini_idx = 0, lidx, ret = 0, do_exit = 0, exit_val = 0; char *ostr = cmd_optstr; void *pid_file = NULL; void *cur_client = NULL; int backend = 0; /* * Reset optind handling, since we may call this multiple times * for the backend. */ optind = 1; while ((c = getopt_long_only(argc, argv, ostr, l_opts, &lidx)) != -1) { did_arg = 1; if ((c & FIO_CLIENT_FLAG) || client_flag_set(c)) { parse_cmd_client(cur_client, argv[optind - 1]); c &= ~FIO_CLIENT_FLAG; } switch (c) { case 'a': smalloc_pool_size = atoi(optarg); break; case 't': def_timeout = atoi(optarg); break; case 'l': write_lat_log = 1; break; case 'b': 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': if (!cur_client) { usage(argv[0]); do_exit++; } break; case 'c': if (!cur_client) { fio_show_option_help(optarg); do_exit++; } break; case 'i': if (!cur_client) { fio_show_ioengine_help(optarg); do_exit++; } break; case 's': dump_cmdline = 1; break; case 'r': read_only = 1; break; case 'v': if (!cur_client) { log_info("%s\n", fio_version_string); do_exit++; } break; case 'V': terse_version = atoi(optarg); if (!(terse_version == 2 || terse_version == 3)) { log_err("fio: bad terse version format\n"); exit_val = 1; do_exit++; } break; case 'e': if (!strcmp("always", optarg)) eta_print = FIO_ETA_ALWAYS; else if (!strcmp("never", optarg)) eta_print = FIO_ETA_NEVER; break; case 'd': if (set_debug(optarg)) do_exit++; break; case 'x': { size_t new_size; if (!strcmp(optarg, "global")) { log_err("fio: can't use global as only " "section\n"); do_exit++; exit_val = 1; break; } new_size = (nr_job_sections + 1) * sizeof(char *); job_sections = realloc(job_sections, new_size); job_sections[nr_job_sections] = strdup(optarg); nr_job_sections++; break; } case 'p': exec_profile = strdup(optarg); break; case FIO_GETOPT_JOB: { const char *opt = l_opts[lidx].name; char *val = optarg; if (!strncmp(opt, "name", 4) && td) { ret = add_job(td, td->o.name ?: "fio", 0); if (ret) return 0; td = NULL; } if (!td) { int is_section = !strncmp(opt, "name", 4); int global = 0; if (!is_section || !strncmp(val, "global", 6)) global = 1; if (is_section && skip_this_section(val)) continue; td = get_new_job(global, &def_thread, 1); if (!td || ioengine_load(td)) return 0; fio_options_set_ioengine_opts(l_opts, td); } ret = fio_cmd_option_parse(td, opt, val); if (!ret && !strcmp(opt, "ioengine")) { free_ioengine(td); if (ioengine_load(td)) return 0; fio_options_set_ioengine_opts(l_opts, td); } break; } case FIO_GETOPT_IOENGINE: { const char *opt = l_opts[lidx].name; char *val = optarg; opt = l_opts[lidx].name; val = optarg; ret = fio_cmd_ioengine_option_parse(td, opt, val); break; } case 'w': warnings_fatal = 1; break; case 'j': max_jobs = atoi(optarg); if (!max_jobs || max_jobs > REAL_MAX_JOBS) { log_err("fio: invalid max jobs: %d\n", max_jobs); do_exit++; exit_val = 1; } break; case 'S': if (nr_clients) { log_err("fio: can't be both client and server\n"); do_exit++; exit_val = 1; break; } if (optarg) fio_server_set_arg(optarg); is_backend = 1; backend = 1; break; case 'D': pid_file = strdup(optarg); break; case 'C': if (is_backend) { log_err("fio: can't be both client and server\n"); do_exit++; exit_val = 1; break; } if (fio_client_add(optarg, &cur_client)) { log_err("fio: failed adding client %s\n", optarg); do_exit++; exit_val = 1; break; } break; default: do_exit++; exit_val = 1; break; } if (do_exit) break; }