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);
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;
}
}
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;
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;
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;
} 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)
continue;
fm = calloc(1, sizeof(*fm));
- strncpy(fm->__base, buf, sizeof(fm->__base) - 1);
+ 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);
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;
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;
}
+static bool create_work_dirs(struct thread_data *td, const char *fname)
+{
+ char path[PATH_MAX];
+ char *start, *end;
+ int offset;
+
+ snprintf(path, PATH_MAX, "%s", fname);
+ start = path;
+
+ offset = longest_existing_path(path);
+ end = start + offset;
+ while ((end = strchr(end, FIO_OS_PATH_SEPARATOR)) != NULL) {
+ if (end == start) {
+ end++;
+ continue;
+ }
+ *end = '\0';
+ errno = 0;
+ 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;
+ end++;
+ }
+ td->flags |= TD_F_DIRS_CREATED;
+ return true;
+}
+
/*
* Open the files and setup files sizes, creating files if necessary.
*/
old_state = td_bump_runstate(td, TD_SETTING_UP);
+ for_each_file(td, f, i) {
+ if (!td_ioengine_flagged(td, FIO_DISKLESSIO) &&
+ strchr(f->file_name, FIO_OS_PATH_SEPARATOR) &&
+ !(td->flags & TD_F_DIRS_CREATED) &&
+ !create_work_dirs(td, f->file_name))
+ goto err_out;
+ }
+
/*
* Find out physical size of files or devices for this thread,
* before we determine I/O size and range of our targets.
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;
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;
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;
}
}
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;
}
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;
}
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.fdp) {
+ err = fdp_init(td);
if (err)
goto err_out;
}
+
return 0;
err_offset:
seed = jhash(f->file_name, strlen(f->file_name), 0) * td->thread_number;
if (!td->o.rand_repeatable)
- seed = td->rand_seeds[4];
+ seed = 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)
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);
/*
* 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,
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))
return false;
if (td->o.random_generator == FIO_RAND_GEN_LFSR) {
- unsigned long seed;
+ uint64_t seed;
seed = td->rand_seeds[FIO_RAND_BLOCK_OFF];
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);
}
}
+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");
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;
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;
}
return true;
}
-static bool create_work_dirs(struct thread_data *td, const char *fname)
-{
- char path[PATH_MAX];
- char *start, *end;
-
- 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;
- }
-
- end = start;
- while ((end = strchr(end, FIO_OS_PATH_SEPARATOR)) != NULL) {
- if (end == start)
- break;
- *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);
- return false;
- }
- *end = FIO_OS_PATH_SEPARATOR;
- end++;
- }
- td->flags |= TD_F_DIRS_CREATED;
- return true;
-}
-
int add_file(struct thread_data *td, const char *fname, int numjob, int inc)
{
int cur_files = td->files_index;
sprintf(file_name + len, "%s", fname);
- if (strchr(fname, FIO_OS_PATH_SEPARATOR) &&
- !(td->flags & TD_F_DIRS_CREATED) &&
- !create_work_dirs(td, fname))
- return 1;
-
/* clean cloned siblings using existing files */
if (numjob && is_already_allocated(file_name) &&
!exists_and_not_regfile(fname))
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;