X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=filesetup.c;h=b45a58265a140700bfb96b4e6dd0930e4ed1a6c2;hp=17fa31fb3cd48924be886d55569b7c1356e72faf;hb=refs%2Fheads%2Fmaster;hpb=308aa5d011158d5f7fa533a60199066dd1858e4c diff --git a/filesetup.c b/filesetup.c index 17fa31fb..cb42a852 100644 --- a/filesetup.c +++ b/filesetup.c @@ -95,6 +95,18 @@ static void fallocate_file(struct thread_data *td, struct fio_file *f) break; } #endif /* CONFIG_LINUX_FALLOCATE */ + case FIO_FALLOCATE_TRUNCATE: { + int r; + + dprint(FD_FILE, "ftruncate file %s size %llu\n", + f->file_name, + (unsigned long long) f->real_file_size); + r = ftruncate(f->fd, f->real_file_size); + if (r != 0) + td_verror(td, errno, "ftruncate"); + + break; + } default: log_err("fio: unknown fallocate mode: %d\n", td->o.fallocate_mode); assert(0); @@ -214,18 +226,22 @@ static int extend_file(struct thread_data *td, struct fio_file *f) if (r < 0) { int __e = errno; - if (__e == ENOSPC) { + if (__e == ENOSPC || __e == EDQUOT) { + const char *__e_name; if (td->o.fill_device) break; - log_info("fio: ENOSPC on laying out " - "file, stopping\n"); - break; + if (__e == ENOSPC) + __e_name = "ENOSPC"; + else + __e_name = "EDQUOT"; + log_info("fio: %s on laying out " + "file, stopping\n", __e_name); } td_verror(td, errno, "write"); } else td_verror(td, EIO, "write"); - break; + goto err; } } @@ -287,13 +303,12 @@ static bool pre_read_file(struct thread_data *td, struct fio_file *f) if (bs > left) bs = left; - b = malloc(bs); + b = calloc(1, bs); if (!b) { td_verror(td, errno, "malloc"); ret = false; goto error; } - memset(b, 0, bs); if (lseek(f->fd, f->file_offset, SEEK_SET) < 0) { td_verror(td, errno, "lseek"); @@ -327,6 +342,95 @@ error: return ret; } +/* + * Generic function to prepopulate regular file with data. + * Useful if you want to make sure I/O engine has data to read. + * Leaves f->fd open on success, caller must close. + */ +int generic_prepopulate_file(struct thread_data *td, struct fio_file *f) +{ + int flags; + unsigned long long left, bs; + char *b = NULL; + + /* generic function for regular files only */ + assert(f->filetype == FIO_TYPE_FILE); + + if (read_only) { + log_err("fio: refusing to write a file due to read-only\n"); + return 0; + } + + flags = O_WRONLY; + if (td->o.allow_create) + flags |= O_CREAT; + +#ifdef WIN32 + flags |= _O_BINARY; +#endif + + dprint(FD_FILE, "open file %s, flags %x\n", f->file_name, flags); + f->fd = open(f->file_name, flags, 0644); + if (f->fd < 0) { + int err = errno; + + if (err == ENOENT && !td->o.allow_create) + log_err("fio: file creation disallowed by " + "allow_file_create=0\n"); + else + td_verror(td, err, "open"); + return 1; + } + + left = f->real_file_size; + bs = td->o.max_bs[DDIR_WRITE]; + if (bs > left) + bs = left; + + b = malloc(bs); + if (!b) { + td_verror(td, errno, "malloc"); + goto err; + } + + while (left && !td->terminate) { + ssize_t r; + + if (bs > left) + bs = left; + + fill_io_buffer(td, b, bs, bs); + + r = write(f->fd, b, bs); + + if (r > 0) { + left -= r; + } else { + td_verror(td, errno, "write"); + goto err; + } + } + + if (td->terminate) { + dprint(FD_FILE, "terminate unlink %s\n", f->file_name); + td_io_unlink_file(td, f); + } else if (td->o.create_fsync) { + if (fsync(f->fd) < 0) { + td_verror(td, errno, "fsync"); + goto err; + } + } + + free(b); + return 0; +err: + close(f->fd); + f->fd = -1; + if (b) + free(b); + return 1; +} + unsigned long long get_rand_file_size(struct thread_data *td) { unsigned long long ret, sized; @@ -632,22 +736,11 @@ int generic_open_file(struct thread_data *td, struct fio_file *f) f_out = stderr; } - if (td_trim(td)) - goto skip_flags; if (td->o.odirect) flags |= OS_O_DIRECT; - if (td->o.oatomic) { - if (!FIO_O_ATOMIC) { - td_verror(td, EINVAL, "OS does not support atomic IO"); - return 1; - } - flags |= OS_O_DIRECT | FIO_O_ATOMIC; - } - if (td->o.sync_io) - flags |= O_SYNC; + flags |= td->o.sync_io; if (td->o.create_on_open && td->o.allow_create) flags |= O_CREAT; -skip_flags: if (f->filetype != FIO_TYPE_FILE) flags |= FIO_O_NOATIME; @@ -656,6 +749,11 @@ open_again: if (!read_only) flags |= O_RDWR; + if (td->o.verify_only) { + flags &= ~O_RDWR; + flags |= O_RDONLY; + } + if (f->filetype == FIO_TYPE_FILE && td->o.allow_create) flags |= O_CREAT; @@ -664,7 +762,7 @@ open_again: else from_hash = file_lookup_open(f, flags); } else if (td_read(td)) { - if (f->filetype == FIO_TYPE_CHAR && !read_only) + if (td_ioengine_flagged(td, FIO_RO_NEEDS_RW_OPEN) && !read_only) flags |= O_RDWR; else flags |= O_RDONLY; @@ -805,8 +903,7 @@ static unsigned long long get_fs_free_counts(struct thread_data *td) } else if (f->filetype != FIO_TYPE_FILE) continue; - buf[255] = '\0'; - strncpy(buf, f->file_name, 255); + snprintf(buf, FIO_ARRAY_SIZE(buf), "%s", f->file_name); if (stat(buf, &sb) < 0) { if (errno != ENOENT) @@ -829,8 +926,7 @@ static unsigned long long get_fs_free_counts(struct thread_data *td) continue; fm = calloc(1, sizeof(*fm)); - strncpy(fm->__base, buf, sizeof(fm->__base)); - fm->__base[255] = '\0'; + snprintf(fm->__base, FIO_ARRAY_SIZE(fm->__base), "%s", buf); fm->base = basename(fm->__base); fm->key = sb.st_dev; flist_add(&fm->list, &list); @@ -854,16 +950,37 @@ static unsigned long long get_fs_free_counts(struct thread_data *td) uint64_t get_start_offset(struct thread_data *td, struct fio_file *f) { + bool align = false; struct thread_options *o = &td->o; unsigned long long align_bs; unsigned long long offset; + unsigned long long increment; if (o->file_append && f->filetype == FIO_TYPE_FILE) return f->real_file_size; + if (o->offset_increment_percent) { + assert(!o->offset_increment); + increment = o->offset_increment_percent * f->real_file_size / 100; + align = true; + } else + increment = o->offset_increment; + if (o->start_offset_percent > 0) { + /* calculate the raw offset */ + offset = (f->real_file_size * o->start_offset_percent / 100) + + (td->subjob_number * increment); + + align = true; + } else { + /* start_offset_percent not set */ + offset = o->start_offset + + td->subjob_number * increment; + } + + if (align) { /* - * if offset_align is provided, set initial offset + * if offset_align is provided, use it */ if (fio_option_is_set(o, start_offset_align)) { align_bs = o->start_offset_align; @@ -872,20 +989,54 @@ uint64_t get_start_offset(struct thread_data *td, struct fio_file *f) align_bs = td_min_bs(td); } - /* calculate the raw offset */ - offset = (f->real_file_size * o->start_offset_percent / 100) + - (td->subjob_number * o->offset_increment); - /* * block align the offset at the next available boundary at * ceiling(offset / align_bs) * align_bs */ offset = (offset / align_bs + (offset % align_bs != 0)) * align_bs; + } - } else { - /* start_offset_percent not set */ - offset = o->start_offset + - td->subjob_number * o->offset_increment; + return offset; +} + +/* + * Find longest path component that exists and return its length + */ +int longest_existing_path(char *path) { + char buf[PATH_MAX]; + bool done; + char *buf_pos; + int offset; +#ifdef WIN32 + DWORD dwAttr; +#else + struct stat sb; +#endif + + sprintf(buf, "%s", path); + done = false; + while (!done) { + buf_pos = strrchr(buf, FIO_OS_PATH_SEPARATOR); + if (!buf_pos) { + offset = 0; + break; + } + + *(buf_pos + 1) = '\0'; + +#ifdef WIN32 + dwAttr = GetFileAttributesA(buf); + if (dwAttr != INVALID_FILE_ATTRIBUTES) { + done = true; + } +#else + if (stat(buf, &sb) == 0) + done = true; +#endif + if (done) + offset = buf_pos - buf; + else + *buf_pos = '\0'; } return offset; @@ -895,29 +1046,23 @@ static bool create_work_dirs(struct thread_data *td, const char *fname) { char path[PATH_MAX]; char *start, *end; + int offset; - if (td->o.directory) { - snprintf(path, PATH_MAX, "%s%c%s", td->o.directory, - FIO_OS_PATH_SEPARATOR, fname); - start = strstr(path, fname); - } else { - snprintf(path, PATH_MAX, "%s", fname); - start = path; - } + snprintf(path, PATH_MAX, "%s", fname); + start = path; - end = start; + offset = longest_existing_path(path); + end = start + offset; while ((end = strchr(end, FIO_OS_PATH_SEPARATOR)) != NULL) { - if (end == start) - break; + if (end == start) { + end++; + continue; + } *end = '\0'; errno = 0; -#ifdef CONFIG_HAVE_MKDIR_TWO - if (mkdir(path, 0600) && errno != EEXIST) { -#else - if (mkdir(path) && errno != EEXIST) { -#endif - log_err("fio: failed to create dir (%s): %d\n", - start, errno); + if (fio_mkdir(path, 0700) && errno != EEXIST) { + log_err("fio: failed to create dir (%s): %s\n", + start, strerror(errno)); return false; } *end = FIO_OS_PATH_SEPARATOR; @@ -968,6 +1113,13 @@ int setup_files(struct thread_data *td) if (err) goto err_out; + if (td->o.zone_mode == ZONE_MODE_ZBD) { + err = zbd_init_files(td); + if (err) + goto err_out; + } + zbd_recalc_options_with_zone_granularity(td); + if (o->read_iolog_file) goto done; @@ -1079,6 +1231,8 @@ int setup_files(struct thread_data *td) if (f->io_size == -1ULL) total_size = -1ULL; else { + uint64_t io_size; + if (o->size_percent && o->size_percent != 100) { uint64_t file_size; @@ -1090,17 +1244,26 @@ int setup_files(struct thread_data *td) f->io_size -= (f->io_size % td_min_bs(td)); } - total_size += f->io_size; + + io_size = f->io_size; + if (o->io_size_percent && o->io_size_percent != 100) { + io_size *= o->io_size_percent; + io_size /= 100; + } + + total_size += io_size; } if (f->filetype == FIO_TYPE_FILE && - (f->io_size + f->file_offset) > f->real_file_size && - !td_ioengine_flagged(td, FIO_DISKLESSIO)) { - if (!o->create_on_open) { + (f->io_size + f->file_offset) > f->real_file_size) { + if (!td_ioengine_flagged(td, FIO_DISKLESSIO) && + !o->create_on_open) { need_extend++; extend_size += (f->io_size + f->file_offset); fio_file_set_extend(f); - } else + } else if (!td_ioengine_flagged(td, FIO_DISKLESSIO) || + (td_ioengine_flagged(td, FIO_DISKLESSIO) && + td_ioengine_flagged(td, FIO_FAKEIO))) f->real_file_size = f->io_size + f->file_offset; } } @@ -1130,7 +1293,7 @@ int setup_files(struct thread_data *td) o->size = total_size; if (o->size < td_min_bs(td)) { - log_err("fio: blocksize too large for data set\n"); + log_err("fio: blocksize is larger than data set range\n"); goto err_out; } @@ -1185,6 +1348,43 @@ int setup_files(struct thread_data *td) temp_stall_ts = 0; } + if (err) + goto err_out; + + /* + * Prepopulate files with data. It might be expected to read some + * "real" data instead of zero'ed files (if no writes to file occurred + * prior to a read job). Engine has to provide a way to do that. + */ + if (td->io_ops->prepopulate_file) { + temp_stall_ts = 1; + + for_each_file(td, f, i) { + if (output_format & FIO_OUTPUT_NORMAL) { + log_info("%s: Prepopulating IO file (%s)\n", + o->name, f->file_name); + } + + err = td->io_ops->prepopulate_file(td, f); + if (err) + break; + + err = __file_invalidate_cache(td, f, f->file_offset, + f->io_size); + + /* + * Shut up static checker + */ + if (f->fd != -1) + close(f->fd); + + f->fd = -1; + if (err) + break; + } + temp_stall_ts = 0; + } + if (err) goto err_out; @@ -1200,16 +1400,23 @@ int setup_files(struct thread_data *td) } done: + if (td->o.zone_mode == ZONE_MODE_ZBD) { + err = zbd_setup_files(td); + if (err) + goto err_out; + } + if (o->create_only) td->done = 1; td_restore_runstate(td, old_state); - if (td->o.zone_mode == ZONE_MODE_ZBD) { - err = zbd_init(td); + if (td->o.dp_type != FIO_DP_NONE) { + err = dp_init(td); if (err) goto err_out; } + return 0; err_offset: @@ -1245,16 +1452,15 @@ static void __init_rand_distribution(struct thread_data *td, struct fio_file *f) nranges = (fsize + range_size - 1ULL) / range_size; - seed = jhash(f->file_name, strlen(f->file_name), 0) * td->thread_number; - if (!td->o.rand_repeatable) - seed = td->rand_seeds[4]; + seed = jhash(f->file_name, strlen(f->file_name), 0) * td->thread_number * + td->rand_seeds[FIO_RAND_BLOCK_OFF]; if (td->o.random_distribution == FIO_RAND_DIST_ZIPF) - zipf_init(&f->zipf, nranges, td->o.zipf_theta.u.f, seed); + zipf_init(&f->zipf, nranges, td->o.zipf_theta.u.f, td->o.random_center.u.f, seed); else if (td->o.random_distribution == FIO_RAND_DIST_PARETO) - pareto_init(&f->zipf, nranges, td->o.pareto_h.u.f, seed); + pareto_init(&f->zipf, nranges, td->o.pareto_h.u.f, td->o.random_center.u.f, seed); else if (td->o.random_distribution == FIO_RAND_DIST_GAUSS) - gauss_init(&f->gauss, nranges, td->o.gauss_dev.u.f, seed); + gauss_init(&f->gauss, nranges, td->o.gauss_dev.u.f, td->o.random_center.u.f, seed); } static bool init_rand_distribution(struct thread_data *td) @@ -1263,7 +1469,9 @@ static bool init_rand_distribution(struct thread_data *td) unsigned int i; int state; - if (td->o.random_distribution == FIO_RAND_DIST_RANDOM) + if (td->o.random_distribution == FIO_RAND_DIST_RANDOM || + td->o.random_distribution == FIO_RAND_DIST_ZONED || + td->o.random_distribution == FIO_RAND_DIST_ZONED_ABS) return false; state = td_bump_runstate(td, TD_SETTING_UP); @@ -1277,7 +1485,7 @@ static bool init_rand_distribution(struct thread_data *td) /* * Check if the number of blocks exceeds the randomness capability of - * the selected generator. Tausworthe is 32-bit, the others are fullly + * the selected generator. Tausworthe is 32-bit, the others are fully * 64-bit capable. */ static int check_rand_gen_limits(struct thread_data *td, struct fio_file *f, @@ -1326,6 +1534,9 @@ bool init_random_map(struct thread_data *td) for_each_file(td, f, i) { uint64_t fsize = min(f->real_file_size, f->io_size); + if (td->o.zone_mode == ZONE_MODE_STRIDED) + fsize = td->o.zone_range; + blocks = fsize / (unsigned long long) td->o.rw_min_bs; if (check_rand_gen_limits(td, f, blocks)) @@ -1339,6 +1550,9 @@ bool init_random_map(struct thread_data *td) if (!lfsr_init(&f->lfsr, blocks, seed, 0)) { fio_file_set_lfsr(f); continue; + } else { + log_err("fio: failed initializing LFSR\n"); + return false; } } else if (!td->o.norandommap) { f->io_axmap = axmap_new(blocks); @@ -1375,11 +1589,25 @@ void close_files(struct thread_data *td) } } +void fio_file_free(struct fio_file *f) +{ + if (fio_file_axmap(f)) + axmap_free(f->io_axmap); + if (f->ruhs_info) + sfree(f->ruhs_info); + if (!fio_file_smalloc(f)) { + free(f->file_name); + free(f); + } else { + sfree(f->file_name); + sfree(f); + } +} + void close_and_free_files(struct thread_data *td) { struct fio_file *f; unsigned int i; - bool use_free = td_ioengine_flagged(td, FIO_NOFILEHASH); dprint(FD_FILE, "close files\n"); @@ -1399,21 +1627,9 @@ void close_and_free_files(struct thread_data *td) td_io_unlink_file(td, f); } - zbd_free_zone_info(f); - - if (use_free) - free(f->file_name); - else - sfree(f->file_name); - f->file_name = NULL; - if (fio_file_axmap(f)) { - axmap_free(f->io_axmap); - f->io_axmap = NULL; - } - if (use_free) - free(f); - else - sfree(f); + zbd_close_file(f); + fdp_free_ruhs_info(f); + fio_file_free(f); } td->o.filename = NULL; @@ -1539,6 +1755,8 @@ static struct fio_file *alloc_new_file(struct thread_data *td) f->fd = -1; f->shadow_fd = -1; fio_file_reset(td, f); + if (!td_ioengine_flagged(td, FIO_NOFILEHASH)) + fio_file_set_smalloc(f); return f; } @@ -1815,11 +2033,12 @@ void dup_files(struct thread_data *td, struct thread_data *org) if (!org->files) return; - td->files = malloc(org->files_index * sizeof(f)); + td->files = calloc(org->files_index, sizeof(f)); if (td->o.file_lock_mode != FILE_LOCK_NONE) td->file_locks = malloc(org->files_index); + assert(org->files_index >= org->o.nr_files); for_each_file(org, f, i) { struct fio_file *__f;