X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=filesetup.c;h=0437a776d9c0d4b55564bc659cc04d0828c105c0;hp=fc9f3067226ee48276071af7343659a939cef43e;hb=c139f3b446083d12d981f426cdbb105947b30b5c;hpb=04d6530f6ecd50520e99732b0b6bb90f71ff131a diff --git a/filesetup.c b/filesetup.c index fc9f3067..0437a776 100644 --- a/filesetup.c +++ b/filesetup.c @@ -24,18 +24,87 @@ static int root_warn; static FLIST_HEAD(filename_list); +/* + * List entry for filename_list + */ +struct file_name { + struct flist_head list; + char *filename; +}; + static inline void clear_error(struct thread_data *td) { td->error = 0; td->verror[0] = '\0'; } +static int native_fallocate(struct thread_data *td, struct fio_file *f) +{ + bool success; + + success = fio_fallocate(f, 0, f->real_file_size); + dprint(FD_FILE, "native fallocate of file %s size %llu was " + "%ssuccessful\n", f->file_name, + (unsigned long long) f->real_file_size, + !success ? "un": ""); + + if (success) + return false; + + if (errno == ENOSYS) + dprint(FD_FILE, "native fallocate is not implemented\n"); + + return true; +} + +static void fallocate_file(struct thread_data *td, struct fio_file *f) +{ + int r; + + if (td->o.fill_device) + return; + + switch (td->o.fallocate_mode) { + case FIO_FALLOCATE_NATIVE: + native_fallocate(td, f); + break; + case FIO_FALLOCATE_NONE: + break; +#ifdef CONFIG_POSIX_FALLOCATE + case FIO_FALLOCATE_POSIX: + dprint(FD_FILE, "posix_fallocate file %s size %llu\n", + f->file_name, + (unsigned long long) f->real_file_size); + + r = posix_fallocate(f->fd, 0, f->real_file_size); + if (r > 0) + log_err("fio: posix_fallocate fails: %s\n", strerror(r)); + break; +#endif /* CONFIG_POSIX_FALLOCATE */ +#ifdef CONFIG_LINUX_FALLOCATE + case FIO_FALLOCATE_KEEP_SIZE: + dprint(FD_FILE, "fallocate(FALLOC_FL_KEEP_SIZE) " + "file %s size %llu\n", f->file_name, + (unsigned long long) f->real_file_size); + + r = fallocate(f->fd, FALLOC_FL_KEEP_SIZE, 0, f->real_file_size); + if (r != 0) + td_verror(td, errno, "fallocate"); + + break; +#endif /* CONFIG_LINUX_FALLOCATE */ + default: + log_err("fio: unknown fallocate mode: %d\n", td->o.fallocate_mode); + assert(0); + } +} + /* * Leaves f->fd open on success, caller must close */ static int extend_file(struct thread_data *td, struct fio_file *f) { - int r, new_layout = 0, unlink_file = 0, flags; + int new_layout = 0, unlink_file = 0, flags; unsigned long long left; unsigned int bs; char *b = NULL; @@ -92,44 +161,11 @@ static int extend_file(struct thread_data *td, struct fio_file *f) return 1; } -#ifdef CONFIG_POSIX_FALLOCATE - if (!td->o.fill_device) { - switch (td->o.fallocate_mode) { - case FIO_FALLOCATE_NONE: - break; - case FIO_FALLOCATE_POSIX: - dprint(FD_FILE, "posix_fallocate file %s size %llu\n", - f->file_name, - (unsigned long long) f->real_file_size); - - r = posix_fallocate(f->fd, 0, f->real_file_size); - if (r > 0) { - log_err("fio: posix_fallocate fails: %s\n", - strerror(r)); - } - break; -#ifdef CONFIG_LINUX_FALLOCATE - case FIO_FALLOCATE_KEEP_SIZE: - dprint(FD_FILE, - "fallocate(FALLOC_FL_KEEP_SIZE) " - "file %s size %llu\n", f->file_name, - (unsigned long long) f->real_file_size); - - r = fallocate(f->fd, FALLOC_FL_KEEP_SIZE, 0, - f->real_file_size); - if (r != 0) - td_verror(td, errno, "fallocate"); - - break; -#endif /* CONFIG_LINUX_FALLOCATE */ - default: - log_err("fio: unknown fallocate mode: %d\n", - td->o.fallocate_mode); - assert(0); - } - } -#endif /* CONFIG_POSIX_FALLOCATE */ + fallocate_file(td, f); + /* + * If our jobs don't require regular files initially, we're done. + */ if (!new_layout) goto done; @@ -148,11 +184,20 @@ static int extend_file(struct thread_data *td, struct fio_file *f) } } - b = malloc(td->o.max_bs[DDIR_WRITE]); - 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) { - bs = td->o.max_bs[DDIR_WRITE]; + ssize_t r; + if (bs > left) bs = left; @@ -210,39 +255,51 @@ err: return 1; } -static int pre_read_file(struct thread_data *td, struct fio_file *f) +static bool pre_read_file(struct thread_data *td, struct fio_file *f) { - int ret = 0, r, did_open = 0, old_runstate; + int r, did_open = 0, old_runstate; unsigned long long left; unsigned int bs; + bool ret = true; char *b; - if (td_ioengine_flagged(td, FIO_PIPEIO)) - return 0; + if (td_ioengine_flagged(td, FIO_PIPEIO) || + td_ioengine_flagged(td, FIO_NOIO)) + return true; + + if (f->filetype == FIO_TYPE_CHAR) + return true; if (!fio_file_open(f)) { if (td->io_ops->open_file(td, f)) { log_err("fio: cannot pre-read, failed to open file\n"); - return 1; + return false; } did_open = 1; } old_runstate = td_bump_runstate(td, TD_PRE_READING); + left = f->io_size; bs = td->o.max_bs[DDIR_READ]; + if (bs > left) + bs = left; + b = malloc(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"); log_err("fio: failed to lseek pre-read file\n"); - ret = 1; + ret = false; goto error; } - left = f->io_size; - while (left && !td->terminate) { if (bs > left) bs = left; @@ -370,16 +427,38 @@ static int get_file_size(struct thread_data *td, struct fio_file *f) if (f->filetype == FIO_TYPE_FILE) ret = file_size(td, f); - else if (f->filetype == FIO_TYPE_BD) + else if (f->filetype == FIO_TYPE_BLOCK) ret = bdev_size(td, f); else if (f->filetype == FIO_TYPE_CHAR) ret = char_size(td, f); else - f->real_file_size = -1; + f->real_file_size = -1ULL; + /* + * Leave ->real_file_size with 0 since it could be expectation + * of initial setup for regular files. + */ if (ret) return ret; + /* + * If ->real_file_size is -1, a conditional for the message + * "offset extends end" is always true, but it makes no sense, + * so just return the same value here. + */ + if (f->real_file_size == -1ULL) { + log_info("%s: failed to get file size of %s\n", td->o.name, + f->file_name); + return 1; + } + + if (td->o.start_offset && f->file_offset == 0) + dprint(FD_FILE, "offset of file %s not initialized yet\n", + f->file_name); + /* + * ->file_offset normally hasn't been initialized yet, so this + * is basically always false. + */ if (f->file_offset > f->real_file_size) { log_err("%s: offset extends end (%llu > %llu)\n", td->o.name, (unsigned long long) f->file_offset, @@ -409,20 +488,22 @@ static int __file_invalidate_cache(struct thread_data *td, struct fio_file *f, if (len == -1ULL || off == -1ULL) return 0; - dprint(FD_IO, "invalidate cache %s: %llu/%llu\n", f->file_name, off, - len); - if (td->io_ops->invalidate) { + dprint(FD_IO, "invalidate %s cache %s\n", td->io_ops->name, + f->file_name); ret = td->io_ops->invalidate(td, f); if (ret < 0) - errval = ret; + errval = -ret; } else if (f->filetype == FIO_TYPE_FILE) { + dprint(FD_IO, "declare unneeded cache %s: %llu/%llu\n", + f->file_name, off, len); ret = posix_fadvise(f->fd, off, len, POSIX_FADV_DONTNEED); if (ret) errval = ret; - } else if (f->filetype == FIO_TYPE_BD) { + } else if (f->filetype == FIO_TYPE_BLOCK) { int retry_count = 0; + dprint(FD_IO, "drop page cache %s\n", f->file_name); ret = blockdev_invalidate_cache(f); while (ret < 0 && errno == EAGAIN && retry_count++ < 25) { /* @@ -444,8 +525,11 @@ static int __file_invalidate_cache(struct thread_data *td, struct fio_file *f, } if (ret < 0) errval = errno; - } else if (f->filetype == FIO_TYPE_CHAR || f->filetype == FIO_TYPE_PIPE) + } else if (f->filetype == FIO_TYPE_CHAR || + f->filetype == FIO_TYPE_PIPE) { + dprint(FD_IO, "invalidate not supported %s\n", f->file_name); ret = 0; + } /* * Cache flushing isn't a fatal condition, and we know it will @@ -454,7 +538,8 @@ static int __file_invalidate_cache(struct thread_data *td, struct fio_file *f, * continue on our way. */ if (errval) - log_info("fio: cache invalidation of %s failed: %s\n", f->file_name, strerror(errval)); + log_info("fio: cache invalidation of %s failed: %s\n", + f->file_name, strerror(errval)); return 0; @@ -486,7 +571,7 @@ int generic_close_file(struct thread_data fio_unused *td, struct fio_file *f) f->shadow_fd = -1; } - f->engine_data = 0; + f->engine_pos = 0; return ret; } @@ -498,9 +583,6 @@ int file_lookup_open(struct fio_file *f, int flags) __f = lookup_file_hash(f->file_name); if (__f) { dprint(FD_FILE, "found file in hash %s\n", f->file_name); - /* - * racy, need the __f->lock locked - */ f->lock = __f->lock; from_hash = 1; } else { @@ -597,7 +679,8 @@ open_again: f->fd = dup(STDIN_FILENO); else from_hash = file_lookup_open(f, flags); - } else { //td trim + } else if (td_trim(td)) { + assert(!td_rw(td)); /* should have matched above */ flags |= O_RDWR; from_hash = file_lookup_open(f, flags); } @@ -652,6 +735,10 @@ open_again: return 0; } +/* + * This function i.e. get_file_size() is the default .get_file_size + * implementation of majority of I/O engines. + */ int generic_get_file_size(struct thread_data *td, struct fio_file *f) { return get_file_size(td, f); @@ -667,7 +754,7 @@ static int get_file_sizes(struct thread_data *td) int err = 0; for_each_file(td, f, i) { - dprint(FD_FILE, "get file size for %p/%d/%p\n", f, i, + dprint(FD_FILE, "get file size for %p/%d/%s\n", f, i, f->file_name); if (td_io_get_file_size(td, f)) { @@ -679,6 +766,13 @@ static int get_file_sizes(struct thread_data *td) clear_error(td); } + /* + * There are corner cases where we end up with -1 for + * ->real_file_size due to unsupported file type, etc. + * We then just set to size option value divided by number + * of files, similar to the way file ->io_size is set. + * stat(2) failure doesn't set ->real_file_size to -1. + */ if (f->real_file_size == -1ULL && td->o.size) f->real_file_size = td->o.size / td->o.nr_files; } @@ -709,7 +803,7 @@ static unsigned long long get_fs_free_counts(struct thread_data *td) struct stat sb; char buf[256]; - if (f->filetype == FIO_TYPE_BD || f->filetype == FIO_TYPE_CHAR) { + if (f->filetype == FIO_TYPE_BLOCK || f->filetype == FIO_TYPE_CHAR) { if (f->real_file_size != -1ULL) ret += f->real_file_size; continue; @@ -765,12 +859,40 @@ 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) { struct thread_options *o = &td->o; + unsigned long long align_bs; + unsigned long long offset; if (o->file_append && f->filetype == FIO_TYPE_FILE) return f->real_file_size; - return td->o.start_offset + - td->subjob_number * td->o.offset_increment; + if (o->start_offset_percent > 0) { + /* + * if offset_align is provided, set initial offset + */ + if (fio_option_is_set(o, start_offset_align)) { + align_bs = o->start_offset_align; + } else { + /* else take the minimum block size */ + 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; } /* @@ -795,7 +917,9 @@ int setup_files(struct thread_data *td) goto done; /* - * if ioengine defines a setup() method, it's responsible for + * Find out physical size of files or devices for this thread, + * before we determine I/O size and range of our targets. + * If ioengine defines a setup() method, it's responsible for * opening the files and setting f->real_file_size to indicate * the valid range for that file. */ @@ -836,7 +960,7 @@ int setup_files(struct thread_data *td) /* * Calculate per-file size and potential extra size for the - * first files, if needed. + * first files, if needed (i.e. if we don't have a fixed size). */ if (!o->file_size_low && o->nr_files) { uint64_t all_fs; @@ -858,11 +982,18 @@ int setup_files(struct thread_data *td) for_each_file(td, f, i) { f->file_offset = get_start_offset(td, f); + /* + * Update ->io_size depending on options specified. + * ->file_size_low being 0 means filesize option isn't set. + * Non zero ->file_size_low equals ->file_size_high means + * filesize option is set in a fixed size format. + * Non zero ->file_size_low not equals ->file_size_high means + * filesize option is set in a range format. + */ if (!o->file_size_low) { /* - * no file size range given, file size is equal to - * total size divided by number of files. If that is - * zero, set it to the real file size. If the size + * no file size or range given, file size is equal to + * total size divided by number of files. If the size * doesn't divide nicely with the min blocksize, * make the first files bigger. */ @@ -872,8 +1003,22 @@ int setup_files(struct thread_data *td) f->io_size += bs; } - if (!f->io_size) + /* + * We normally don't come here for regular files, but + * if the result is 0 for a regular file, set it to the + * real file size. This could be size of the existing + * one if it already exists, but otherwise will be set + * to 0. A new file won't be created because + * ->io_size + ->file_offset equals ->real_file_size. + */ + if (!f->io_size) { + if (f->file_offset > f->real_file_size) + goto err_offset; f->io_size = f->real_file_size - f->file_offset; + if (!f->io_size) + log_info("fio: file %s may be ignored\n", + f->file_name); + } } else if (f->real_file_size < o->file_size_low || f->real_file_size > o->file_size_high) { if (f->file_offset > o->file_size_low) @@ -895,7 +1040,14 @@ int setup_files(struct thread_data *td) total_size = -1ULL; else { if (o->size_percent) { - f->io_size = (f->io_size * o->size_percent) / 100; + uint64_t file_size; + + file_size = f->io_size + f->file_offset; + f->io_size = (file_size * + o->size_percent) / 100; + if (f->io_size > (file_size - f->file_offset)) + f->io_size = file_size - f->file_offset; + f->io_size -= (f->io_size % td_min_bs(td)); } total_size += f->io_size; @@ -907,9 +1059,9 @@ int setup_files(struct thread_data *td) if (!o->create_on_open) { need_extend++; extend_size += (f->io_size + f->file_offset); + fio_file_set_extend(f); } else f->real_file_size = f->io_size + f->file_offset; - fio_file_set_extend(f); } } @@ -943,14 +1095,21 @@ int setup_files(struct thread_data *td) } /* - * See if we need to extend some files + * See if we need to extend some files, typically needed when our + * target regular files don't exist yet, but our jobs require them + * initially due to read I/Os. */ if (need_extend) { temp_stall_ts = 1; - if (output_format & FIO_OUTPUT_NORMAL) - log_info("%s: Laying out IO file(s) (%u file(s) /" - " %lluMB)\n", o->name, need_extend, - extend_size >> 20); + if (output_format & FIO_OUTPUT_NORMAL) { + log_info("%s: Laying out IO file%s (%u file%s / %s%lluMiB)\n", + o->name, + need_extend > 1 ? "s" : "", + need_extend, + need_extend > 1 ? "s" : "", + need_extend > 1 ? "total " : "", + extend_size >> 20); + } for_each_file(td, f, i) { unsigned long long old_len = -1ULL, extend_len = -1ULL; @@ -997,8 +1156,8 @@ int setup_files(struct thread_data *td) * stored entries. */ if (!o->read_iolog_file) { - if (o->io_limit) - td->total_io_size = o->io_limit * o->loops; + if (o->io_size) + td->total_io_size = o->io_size * o->loops; else td->total_io_size = o->size * o->loops; } @@ -1016,7 +1175,7 @@ err_out: return 1; } -int pre_read_files(struct thread_data *td) +bool pre_read_files(struct thread_data *td) { struct fio_file *f; unsigned int i; @@ -1024,10 +1183,11 @@ int pre_read_files(struct thread_data *td) dprint(FD_FILE, "pre_read files\n"); for_each_file(td, f, i) { - pre_read_file(td, f); + if (!pre_read_file(td, f)) + return false; } - return 1; + return true; } static int __init_rand_distribution(struct thread_data *td, struct fio_file *f) @@ -1098,7 +1258,7 @@ static int check_rand_gen_limits(struct thread_data *td, struct fio_file *f, if (!fio_option_is_set(&td->o, random_generator)) { log_info("fio: Switching to tausworthe64. Use the " "random_generator= option to get rid of this " - " warning.\n"); + "warning.\n"); td->o.random_generator = FIO_RAND_GEN_TAUSWORTHE64; return 0; } @@ -1178,6 +1338,7 @@ 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"); @@ -1197,13 +1358,19 @@ void close_and_free_files(struct thread_data *td) td_io_unlink_file(td, f); } - sfree(f->file_name); + 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; } - sfree(f); + if (use_free) + free(f); + else + sfree(f); } td->o.filename = NULL; @@ -1229,12 +1396,12 @@ static void get_file_type(struct fio_file *f) /* \\.\ is the device namespace in Windows, where every file is * a block device */ if (strncmp(f->file_name, "\\\\.\\", 4) == 0) - f->filetype = FIO_TYPE_BD; + f->filetype = FIO_TYPE_BLOCK; #endif if (!stat(f->file_name, &sb)) { if (S_ISBLK(sb.st_mode)) - f->filetype = FIO_TYPE_BD; + f->filetype = FIO_TYPE_BLOCK; else if (S_ISCHR(sb.st_mode)) f->filetype = FIO_TYPE_CHAR; else if (S_ISFIFO(sb.st_mode)) @@ -1242,12 +1409,14 @@ static void get_file_type(struct fio_file *f) } } -static bool __is_already_allocated(const char *fname) +static bool __is_already_allocated(const char *fname, bool set) { struct flist_head *entry; + bool ret; - if (flist_empty(&filename_list)) - return false; + ret = file_bloom_exists(fname, set); + if (!ret) + return ret; flist_for_each(entry, &filename_list) { struct file_name *fn; @@ -1266,7 +1435,7 @@ static bool is_already_allocated(const char *fname) bool ret; fio_file_hash_lock(); - ret = __is_already_allocated(fname); + ret = __is_already_allocated(fname, false); fio_file_hash_unlock(); return ret; @@ -1280,7 +1449,7 @@ static void set_already_allocated(const char *fname) fn->filename = strdup(fname); fio_file_hash_lock(); - if (!__is_already_allocated(fname)) { + if (!__is_already_allocated(fname, true)) { flist_add_tail(&fn->list, &filename_list); fn = NULL; } @@ -1292,7 +1461,6 @@ static void set_already_allocated(const char *fname) } } - static void free_already_allocated(void) { struct flist_head *entry, *tmp; @@ -1316,9 +1484,11 @@ static struct fio_file *alloc_new_file(struct thread_data *td) { struct fio_file *f; - f = smalloc(sizeof(*f)); + if (td_ioengine_flagged(td, FIO_NOFILEHASH)) + f = calloc(1, sizeof(*f)); + else + f = smalloc(sizeof(*f)); if (!f) { - log_err("fio: smalloc OOM\n"); assert(0); return NULL; } @@ -1349,6 +1519,42 @@ bool exists_and_not_regfile(const char *filename) return true; } +static int 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 1; + } + *end = FIO_OS_PATH_SEPARATOR; + end++; + } + td->flags |= TD_F_DIRS_CREATED; + return 0; +} + int add_file(struct thread_data *td, const char *fname, int numjob, int inc) { int cur_files = td->files_index; @@ -1364,6 +1570,11 @@ int add_file(struct thread_data *td, const char *fname, int numjob, int inc) 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)) @@ -1400,11 +1611,12 @@ int add_file(struct thread_data *td, const char *fname, int numjob, int inc) if (td->io_ops && td_ioengine_flagged(td, FIO_DISKLESSIO)) f->real_file_size = -1ULL; - f->file_name = smalloc_strdup(file_name); - if (!f->file_name) { - log_err("fio: smalloc OOM\n"); + if (td_ioengine_flagged(td, FIO_NOFILEHASH)) + f->file_name = strdup(file_name); + else + f->file_name = smalloc_strdup(file_name); + if (!f->file_name) assert(0); - } get_file_type(f); @@ -1426,7 +1638,8 @@ int add_file(struct thread_data *td, const char *fname, int numjob, int inc) if (f->filetype == FIO_TYPE_FILE) td->nr_normal_files++; - set_already_allocated(file_name); + if (td->o.numjobs > 1) + set_already_allocated(file_name); if (inc) td->o.nr_files++; @@ -1549,7 +1762,7 @@ static int recurse_dir(struct thread_data *td, const char *dirname) if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")) continue; - sprintf(full_path, "%s%s%s", dirname, FIO_OS_PATH_SEPARATOR, dir->d_name); + sprintf(full_path, "%s%c%s", dirname, FIO_OS_PATH_SEPARATOR, dir->d_name); if (lstat(full_path, &sb) == -1) { if (errno != ENOENT) { @@ -1606,11 +1819,12 @@ void dup_files(struct thread_data *td, struct thread_data *org) __f = alloc_new_file(td); if (f->file_name) { - __f->file_name = smalloc_strdup(f->file_name); - if (!__f->file_name) { - log_err("fio: smalloc OOM\n"); + if (td_ioengine_flagged(td, FIO_NOFILEHASH)) + __f->file_name = strdup(f->file_name); + else + __f->file_name = smalloc_strdup(f->file_name); + if (!__f->file_name) assert(0); - } __f->filetype = f->filetype; } @@ -1666,16 +1880,16 @@ void fio_file_reset(struct thread_data *td, struct fio_file *f) lfsr_reset(&f->lfsr, td->rand_seeds[FIO_RAND_BLOCK_OFF]); } -int fio_files_done(struct thread_data *td) +bool fio_files_done(struct thread_data *td) { struct fio_file *f; unsigned int i; for_each_file(td, f, i) if (!fio_file_done(f)) - return 0; + return false; - return 1; + return true; } /* free memory used in initialization phase only */ @@ -1683,3 +1897,32 @@ void filesetup_mem_free(void) { free_already_allocated(); } + +/* + * This function is for platforms which support direct I/O but not O_DIRECT. + */ +int fio_set_directio(struct thread_data *td, struct fio_file *f) +{ +#ifdef FIO_OS_DIRECTIO + int ret = fio_set_odirect(f); + + if (ret) { + td_verror(td, ret, "fio_set_directio"); +#if defined(__sun__) + if (ret == ENOTTY) { /* ENOTTY suggests RAW device or ZFS */ + log_err("fio: doing directIO to RAW devices or ZFS not supported\n"); + } else { + log_err("fio: the file system does not seem to support direct IO\n"); + } +#else + log_err("fio: the file system does not seem to support direct IO\n"); +#endif + return -1; + } + + return 0; +#else + log_err("fio: direct IO is not supported on this host operating system\n"); + return -1; +#endif +}