X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=init.c;h=d6dbaf7cb437d894ab990fa4531bd44ef05872c7;hp=b5315334c6ba9f6edc8aad0d477c25f7eff4ceaa;hb=48ff7df9daea86c82a572b0a840bb8371b6b1a29;hpb=f4cd67d8dc96ca947b294f6a5c9fdced2b64215d diff --git a/init.c b/init.c index b5315334..d6dbaf7c 100644 --- a/init.c +++ b/init.c @@ -45,13 +45,12 @@ const char fio_version_string[] = FIO_VERSION; #define FIO_RANDSEED (0xb1899bedUL) static char **ini_file; -static int max_jobs = FIO_MAX_JOBS; static bool dump_cmdline; static bool parse_only; static bool merge_blktrace_only; static struct thread_data def_thread; -struct thread_data *threads = NULL; +struct thread_segment segments[REAL_MAX_SEG]; static char **job_sections; static int nr_job_sections; @@ -301,25 +300,35 @@ static struct option l_opts[FIO_NR_OPTIONS] = { void free_threads_shm(void) { - if (threads) { - void *tp = threads; + int i; + + for (i = 0; i < nr_segments; i++) { + struct thread_segment *seg = &segments[i]; + + if (seg->threads) { + void *tp = seg->threads; #ifndef CONFIG_NO_SHM - struct shmid_ds sbuf; + struct shmid_ds sbuf; - threads = NULL; - shmdt(tp); - shmctl(shm_id, IPC_RMID, &sbuf); - shm_id = -1; + seg->threads = NULL; + shmdt(tp); + shmctl(seg->shm_id, IPC_RMID, &sbuf); + seg->shm_id = -1; #else - threads = NULL; - free(tp); + seg->threads = NULL; + free(tp); #endif + } } + + nr_segments = 0; + cur_segment = 0; } static void free_shm(void) { - if (threads) { +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (nr_segments) { flow_exit(); fio_debug_jobp = NULL; fio_warned = NULL; @@ -335,73 +344,82 @@ static void free_shm(void) fio_filelock_exit(); file_hash_exit(); scleanup(); +#endif } -/* - * The thread area is shared between the main process and the job - * threads/processes. So setup a shared memory segment that will hold - * all the job info. We use the end of the region for keeping track of - * open files across jobs, for file sharing. - */ -static int setup_thread_area(void) +static int add_thread_segment(void) { + struct thread_segment *seg = &segments[nr_segments]; + size_t size = JOBS_PER_SEG * sizeof(struct thread_data); int i; - if (threads) - return 0; - - /* - * 1024 is too much on some machines, scale max_jobs if - * we get a failure that looks like too large a shm segment - */ - do { - size_t size = max_jobs * sizeof(struct thread_data); + if (nr_segments + 1 >= REAL_MAX_SEG) { + log_err("error: maximum number of jobs reached.\n"); + return -1; + } - size += 2 * sizeof(unsigned int); + size += 2 * sizeof(unsigned int); #ifndef CONFIG_NO_SHM - shm_id = shmget(0, size, IPC_CREAT | 0600); - if (shm_id != -1) - break; - if (errno != EINVAL && errno != ENOMEM && errno != ENOSPC) { + seg->shm_id = shmget(0, size, IPC_CREAT | 0600); + if (seg->shm_id == -1) { + if (errno != EINVAL && errno != ENOMEM && errno != ENOSPC) perror("shmget"); - break; - } + return -1; + } #else - threads = malloc(size); - if (threads) - break; + seg->threads = malloc(size); + if (!seg->threads) + return -1; #endif - max_jobs >>= 1; - } while (max_jobs); - #ifndef CONFIG_NO_SHM - if (shm_id == -1) - return 1; - - threads = shmat(shm_id, NULL, 0); - if (threads == (void *) -1) { + seg->threads = shmat(seg->shm_id, NULL, 0); + if (seg->threads == (void *) -1) { perror("shmat"); return 1; } if (shm_attach_to_open_removed()) - shmctl(shm_id, IPC_RMID, NULL); + shmctl(seg->shm_id, IPC_RMID, NULL); #endif - memset(threads, 0, max_jobs * sizeof(struct thread_data)); - for (i = 0; i < max_jobs; i++) - DRD_IGNORE_VAR(threads[i]); - fio_debug_jobp = (unsigned int *)(threads + max_jobs); + nr_segments++; + + memset(seg->threads, 0, JOBS_PER_SEG * sizeof(struct thread_data)); + for (i = 0; i < JOBS_PER_SEG; i++) + DRD_IGNORE_VAR(seg->threads[i]); + seg->nr_threads = 0; + + /* Not first segment, we're done */ + if (nr_segments != 1) { + cur_segment++; + return 0; + } + + fio_debug_jobp = (unsigned int *)(seg->threads + JOBS_PER_SEG); *fio_debug_jobp = -1; fio_warned = fio_debug_jobp + 1; *fio_warned = 0; flow_init(); - return 0; } +/* + * The thread areas are shared between the main process and the job + * threads/processes, and is split into chunks of JOBS_PER_SEG. If the current + * segment has no more room, add a new chunk. + */ +static int expand_thread_area(void) +{ + struct thread_segment *seg = &segments[cur_segment]; + + if (nr_segments && seg->nr_threads < JOBS_PER_SEG) + return 0; + + return add_thread_segment(); +} + static void dump_print_option(struct print_option *p) { const char *delim; @@ -470,21 +488,19 @@ static void copy_opt_list(struct thread_data *dst, struct thread_data *src) static struct thread_data *get_new_job(bool global, struct thread_data *parent, bool preserve_eo, const char *jobname) { + struct thread_segment *seg; struct thread_data *td; if (global) return &def_thread; - if (setup_thread_area()) { + if (expand_thread_area()) { log_err("error: failed to setup shm segment\n"); return NULL; } - if (thread_number >= max_jobs) { - log_err("error: maximum number of jobs (%d) reached.\n", - max_jobs); - return NULL; - } - td = &threads[thread_number++]; + seg = &segments[cur_segment]; + td = &seg->threads[seg->nr_threads++]; + thread_number++; *td = *parent; INIT_FLIST_HEAD(&td->opt_list); @@ -534,7 +550,8 @@ static void put_job(struct thread_data *td) if (td->o.name) free(td->o.name); - memset(&threads[td->thread_number - 1], 0, sizeof(*td)); + memset(td, 0, sizeof(*td)); + segments[cur_segment].nr_threads--; thread_number--; } @@ -564,13 +581,11 @@ static int setup_rate(struct thread_data *td) { int ret = 0; - if (td->o.rate[DDIR_READ] || td->o.rate_iops[DDIR_READ]) - 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); - + for_each_rw_ddir(ddir) { + if (td->o.rate[ddir] || td->o.rate_iops[ddir]) { + ret |= __setup_rate(td, ddir); + } + } return ret; } @@ -662,31 +677,25 @@ static int fixup_options(struct thread_data *td) if (td_read(td)) o->overwrite = 1; - if (!o->min_bs[DDIR_READ]) - o->min_bs[DDIR_READ] = o->bs[DDIR_READ]; - if (!o->max_bs[DDIR_READ]) - o->max_bs[DDIR_READ] = o->bs[DDIR_READ]; - if (!o->min_bs[DDIR_WRITE]) - 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_each_rw_ddir(ddir) { + if (!o->min_bs[ddir]) + o->min_bs[ddir] = o->bs[ddir]; + if (!o->max_bs[ddir]) + o->max_bs[ddir] = o->bs[ddir]; + } + + o->rw_min_bs = -1; + for_each_rw_ddir(ddir) { + o->rw_min_bs = min(o->rw_min_bs, o->min_bs[ddir]); + } /* * For random IO, allow blockalign offset other than min_bs. */ - if (!o->ba[DDIR_READ] || !td_random(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]; + for_each_rw_ddir(ddir) { + if (!o->ba[ddir] || !td_random(td)) + o->ba[ddir] = o->min_bs[ddir]; + } if ((o->ba[DDIR_READ] != o->min_bs[DDIR_READ] || o->ba[DDIR_WRITE] != o->min_bs[DDIR_WRITE] || @@ -765,14 +774,12 @@ static int fixup_options(struct thread_data *td) log_err("fio: rate and rate_iops are mutually exclusive\n"); ret |= 1; } - if ((o->rate[DDIR_READ] && (o->rate[DDIR_READ] < o->ratemin[DDIR_READ])) || - (o->rate[DDIR_WRITE] && (o->rate[DDIR_WRITE] < o->ratemin[DDIR_WRITE])) || - (o->rate[DDIR_TRIM] && (o->rate[DDIR_TRIM] < o->ratemin[DDIR_TRIM])) || - (o->rate_iops[DDIR_READ] && (o->rate_iops[DDIR_READ] < o->rate_iops_min[DDIR_READ])) || - (o->rate_iops[DDIR_WRITE] && (o->rate_iops[DDIR_WRITE] < o->rate_iops_min[DDIR_WRITE])) || - (o->rate_iops[DDIR_TRIM] && (o->rate_iops[DDIR_TRIM] < o->rate_iops_min[DDIR_TRIM]))) { - log_err("fio: minimum rate exceeds rate\n"); - ret |= 1; + for_each_rw_ddir(ddir) { + if ((o->rate[ddir] && (o->rate[ddir] < o->ratemin[ddir])) || + (o->rate_iops[ddir] && (o->rate_iops[ddir] < o->rate_iops_min[ddir]))) { + log_err("fio: minimum rate exceeds rate, ddir %d\n", +ddir); + ret |= 1; + } } if (!o->timeout && o->time_based) { @@ -956,7 +963,6 @@ static int fixup_options(struct thread_data *td) */ o->max_latency *= 1000ULL; o->latency_target *= 1000ULL; - o->latency_window *= 1000ULL; return ret; } @@ -967,13 +973,13 @@ static void init_rand_file_service(struct thread_data *td) const unsigned int seed = td->rand_seeds[FIO_RAND_FILE_OFF]; if (td->o.file_service_type == FIO_FSERVICE_ZIPF) { - zipf_init(&td->next_file_zipf, nranges, td->zipf_theta, seed); + zipf_init(&td->next_file_zipf, nranges, td->zipf_theta, td->random_center, seed); zipf_disable_hash(&td->next_file_zipf); } else if (td->o.file_service_type == FIO_FSERVICE_PARETO) { - pareto_init(&td->next_file_zipf, nranges, td->pareto_h, seed); + pareto_init(&td->next_file_zipf, nranges, td->pareto_h, td->random_center, seed); zipf_disable_hash(&td->next_file_zipf); } else if (td->o.file_service_type == FIO_FSERVICE_GAUSS) { - gauss_init(&td->next_file_gauss, nranges, td->gauss_dev, seed); + gauss_init(&td->next_file_gauss, nranges, td->gauss_dev, td->random_center, seed); gauss_disable_hash(&td->next_file_gauss); } } @@ -993,9 +999,9 @@ void td_fill_verify_state_seed(struct thread_data *td) static void td_fill_rand_seeds_internal(struct thread_data *td, bool use64) { - unsigned int read_seed = td->rand_seeds[FIO_RAND_BS_OFF]; - unsigned int write_seed = td->rand_seeds[FIO_RAND_BS1_OFF]; - unsigned int trim_seed = td->rand_seeds[FIO_RAND_BS2_OFF]; + uint64_t read_seed = td->rand_seeds[FIO_RAND_BS_OFF]; + uint64_t write_seed = td->rand_seeds[FIO_RAND_BS1_OFF]; + uint64_t trim_seed = td->rand_seeds[FIO_RAND_BS2_OFF]; int i; /* @@ -1098,15 +1104,15 @@ int ioengine_load(struct thread_data *td) * for this name and see if they match. If they do, then * the engine is unchanged. */ - dlhandle = td->io_ops_dlhandle; + dlhandle = td->io_ops->dlhandle; ops = load_ioengine(td); - if (ops == td->io_ops && dlhandle == td->io_ops_dlhandle) { - if (dlhandle) - dlclose(dlhandle); + if (!ops) + goto fail; + + if (ops == td->io_ops && dlhandle == td->io_ops->dlhandle) return 0; - } - if (dlhandle && dlhandle != td->io_ops_dlhandle) + if (dlhandle && dlhandle != td->io_ops->dlhandle) dlclose(dlhandle); /* Unload the old engine. */ @@ -1114,10 +1120,8 @@ int ioengine_load(struct thread_data *td) } td->io_ops = load_ioengine(td); - if (!td->io_ops) { - log_err("fio: failed to load engine\n"); - return 1; - } + if (!td->io_ops) + goto fail; if (td->io_ops->option_struct_size && td->io_ops->options) { /* @@ -1156,6 +1160,11 @@ int ioengine_load(struct thread_data *td) td_set_ioengine_flags(td); return 0; + +fail: + log_err("fio: failed to load engine\n"); + return 1; + } static void init_flags(struct thread_data *td) @@ -1740,11 +1749,8 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num, if (file_alloced) { if (td_new->files) { struct fio_file *f; - for_each_file(td_new, f, i) { - if (f->file_name) - sfree(f->file_name); - sfree(f); - } + for_each_file(td_new, f, i) + fio_file_free(f); free(td_new->files); td_new->files = NULL; } @@ -1843,6 +1849,7 @@ static int __parse_jobs_ini(struct thread_data *td, int nested, char *name, char ***popts, int *aopts, int *nopts) { bool global = false; + bool stdin_occupied = false; char *string; FILE *f; char *p; @@ -1859,9 +1866,10 @@ static int __parse_jobs_ini(struct thread_data *td, if (is_buf) f = NULL; else { - if (!strcmp(file, "-")) + if (!strcmp(file, "-")) { f = stdin; - else + stdin_occupied = true; + } else f = fopen(file, "r"); if (!f) { @@ -2063,6 +2071,20 @@ static int __parse_jobs_ini(struct thread_data *td, } ret = fio_options_parse(td, opts, num_opts); + + if (!ret && td->o.read_iolog_file != NULL) { + char *fname = get_name_by_idx(td->o.read_iolog_file, + td->subjob_number); + if (!strcmp(fname, "-")) { + if (stdin_occupied) { + log_err("fio: only one user (read_iolog_file/job " + "file) of stdin is permitted at once but " + "more than one was found.\n"); + ret = 1; + } + stdin_occupied = true; + } + } if (!ret) { if (dump_cmdline) dump_opt_list(td); @@ -2535,7 +2557,7 @@ int parse_cmd_line(int argc, char *argv[], int client_type) case 'i': did_arg = true; if (!cur_client) { - fio_show_ioengine_help(optarg); + exit_val = fio_show_ioengine_help(optarg); do_exit++; } break; @@ -2714,12 +2736,7 @@ int parse_cmd_line(int argc, char *argv[], int client_type) 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; - } + /* we don't track/need this anymore, ignore it */ break; case 'S': did_arg = true; @@ -2887,7 +2904,7 @@ int parse_cmd_line(int argc, char *argv[], int client_type) log_err("%s: unrecognized option '%s'\n", argv[0], argv[optind - 1]); show_closest_option(argv[optind - 1]); - /* fall through */ + fallthrough; default: do_exit++; exit_val = 1;