X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=ioengines.c;h=9638d8043753627f60ffc223b8308ca6f61d57af;hp=f4eae04db6df5d072103191f05b187fcb6f19e80;hb=78d4a262a0758d47631bfd44672bbd5f9eff9b42;hpb=9db01ef976006c002b05fa4e4ec589eb029aac5b diff --git a/ioengines.c b/ioengines.c index f4eae04d..9638d804 100644 --- a/ioengines.c +++ b/ioengines.c @@ -22,39 +22,31 @@ static FLIST_HEAD(engine_list); -static int check_engine_ops(struct ioengine_ops *ops) +static bool check_engine_ops(struct ioengine_ops *ops) { if (ops->version != FIO_IOOPS_VERSION) { log_err("bad ioops version %d (want %d)\n", ops->version, FIO_IOOPS_VERSION); - return 1; + return true; } if (!ops->queue) { log_err("%s: no queue handler\n", ops->name); - return 1; + return true; } /* * sync engines only need a ->queue() */ if (ops->flags & FIO_SYNCIO) - return 0; + return false; - if (!ops->event) { - log_err("%s: no event handler\n", ops->name); - return 1; - } - if (!ops->getevents) { - log_err("%s: no getevents handler\n", ops->name); - return 1; - } - if (!ops->queue) { - log_err("%s: no queue handler\n", ops->name); - return 1; + if (!ops->event || !ops->getevents) { + log_err("%s: no event/getevents handler\n", ops->name); + return true; } - return 0; + return false; } void unregister_ioengine(struct ioengine_ops *ops) @@ -107,23 +99,35 @@ static struct ioengine_ops *dlopen_ioengine(struct thread_data *td, ops = dlsym(dlhandle, engine_lib); if (!ops) ops = dlsym(dlhandle, "ioengine"); + + /* + * For some external engines (like C++ ones) it is not that trivial + * to provide a non-static ionengine structure that we can reference. + * Instead we call a method which allocates the required ioengine + * structure. + */ + if (!ops) { + get_ioengine_t get_ioengine = dlsym(dlhandle, "get_ioengine"); + + if (get_ioengine) + get_ioengine(&ops); + } + if (!ops) { td_vmsg(td, -1, dlerror(), "dlsym"); dlclose(dlhandle); return NULL; } - ops->dlhandle = dlhandle; + td->io_ops_dlhandle = dlhandle; return ops; } -struct ioengine_ops *load_ioengine(struct thread_data *td, const char *name) +static struct ioengine_ops *__load_ioengine(const char *name) { - struct ioengine_ops *ops, *ret; - char engine[16]; - - dprint(FD_IO, "load ioengine %s\n", name); + char engine[64]; + engine[sizeof(engine) - 1] = '\0'; strncpy(engine, name, sizeof(engine) - 1); /* @@ -132,10 +136,37 @@ struct ioengine_ops *load_ioengine(struct thread_data *td, const char *name) if (!strncmp(engine, "linuxaio", 8) || !strncmp(engine, "aio", 3)) strcpy(engine, "libaio"); - ops = find_ioengine(engine); + dprint(FD_IO, "load ioengine %s\n", engine); + return find_ioengine(engine); +} + +struct ioengine_ops *load_ioengine(struct thread_data *td) +{ + struct ioengine_ops *ops = NULL; + const char *name; + + /* + * Use ->ioengine_so_path if an external ioengine path is specified. + * In this case, ->ioengine is "external" which also means the prefix + * for external ioengines "external:" is properly used. + */ + name = td->o.ioengine_so_path ?: td->o.ioengine; + + /* + * Try to load ->ioengine first, and if failed try to dlopen(3) either + * ->ioengine or ->ioengine_so_path. This is redundant for an external + * ioengine with prefix, and also leaves the possibility of unexpected + * behavior (e.g. if the "external" ioengine exists), but we do this + * so as not to break job files not using the prefix. + */ + ops = __load_ioengine(td->o.ioengine); if (!ops) ops = dlopen_ioengine(td, name); + /* + * If ops is NULL, we failed to load ->ioengine, and also failed to + * dlopen(3) either ->ioengine or ->ioengine_so_path as a path. + */ if (!ops) { log_err("fio: engine %s not loadable\n", name); return NULL; @@ -147,11 +178,7 @@ struct ioengine_ops *load_ioengine(struct thread_data *td, const char *name) if (check_engine_ops(ops)) return NULL; - ret = malloc(sizeof(*ret)); - memcpy(ret, ops, sizeof(*ret)); - ret->data = NULL; - - return ret; + return ops; } /* @@ -167,10 +194,9 @@ void free_ioengine(struct thread_data *td) td->eo = NULL; } - if (td->io_ops->dlhandle) - dlclose(td->io_ops->dlhandle); + if (td->io_ops_dlhandle) + dlclose(td->io_ops_dlhandle); - free(td->io_ops); td->io_ops = NULL; } @@ -180,7 +206,7 @@ void close_ioengine(struct thread_data *td) if (td->io_ops->cleanup) { td->io_ops->cleanup(td); - td->io_ops->data = NULL; + td->io_ops_data = NULL; } free_ioengine(td); @@ -206,7 +232,7 @@ int td_io_prep(struct thread_data *td, struct io_u *io_u) } int td_io_getevents(struct thread_data *td, unsigned int min, unsigned int max, - struct timespec *t) + const struct timespec *t) { int r = 0; @@ -250,13 +276,15 @@ out: int td_io_queue(struct thread_data *td, struct io_u *io_u) { + const enum fio_ddir ddir = acct_ddir(io_u); + unsigned long buflen = io_u->xfer_buflen; int ret; dprint_io_u(io_u, "queue"); fio_ro_check(td, io_u); assert((io_u->flags & IO_U_F_FLIGHT) == 0); - io_u->flags |= IO_U_F_FLIGHT; + io_u_set(td, io_u, IO_U_F_FLIGHT); assert(fio_file_open(io_u->file)); @@ -268,7 +296,7 @@ int td_io_queue(struct thread_data *td, struct io_u *io_u) io_u->error = 0; io_u->resid = 0; - if (td->io_ops->flags & FIO_SYNCIO) { + if (td_ioengine_flagged(td, FIO_SYNCIO)) { if (fio_fill_issue_time(td)) fio_gettime(&io_u->issue_time, NULL); @@ -277,16 +305,33 @@ int td_io_queue(struct thread_data *td, struct io_u *io_u) */ if (td->o.read_iolog_file) memcpy(&td->last_issue, &io_u->issue_time, - sizeof(struct timeval)); + sizeof(io_u->issue_time)); } - if (ddir_rw(acct_ddir(io_u))) - td->io_issues[acct_ddir(io_u)]++; + if (ddir_rw(ddir)) { + td->io_issues[ddir]++; + td->io_issue_bytes[ddir] += buflen; + td->rate_io_issue_bytes[ddir] += buflen; + } ret = td->io_ops->queue(td, io_u); unlock_file(td, io_u->file); + if (ret == FIO_Q_BUSY && ddir_rw(ddir)) { + td->io_issues[ddir]--; + td->io_issue_bytes[ddir] -= buflen; + td->rate_io_issue_bytes[ddir] -= buflen; + io_u_clear(td, io_u, IO_U_F_FLIGHT); + } + + /* + * If an error was seen and the io engine didn't propagate it + * back to 'td', do so. + */ + if (io_u->error && !td->error) + td_verror(td, io_u->error, "td_io_queue"); + /* * Add warning for O_DIRECT so that users have an easier time * spotting potentially bad alignment. If this triggers for the first @@ -297,10 +342,11 @@ int td_io_queue(struct thread_data *td, struct io_u *io_u) td->o.odirect) { log_info("fio: first direct IO errored. File system may not " - "support direct IO, or iomem_align= is bad.\n"); + "support direct IO, or iomem_align= is bad, or " + "invalid block size. Try setting direct=0.\n"); } - if (!td->io_ops->commit || ddir_trim(io_u->ddir)) { + if (!td->io_ops->commit || io_u->ddir == DDIR_TRIM) { io_u_mark_submit(td, 1); io_u_mark_complete(td, 1); } @@ -313,10 +359,10 @@ int td_io_queue(struct thread_data *td, struct io_u *io_u) } else if (ret == FIO_Q_QUEUED) { int r; - if (ddir_rw(io_u->ddir)) { - td->io_u_queued++; + td->io_u_queued++; + + if (ddir_rw(io_u->ddir)) td->ts.total_io_u[io_u->ddir]++; - } if (td->io_u_queued >= td->o.iodepth_batch) { r = td_io_commit(td); @@ -325,7 +371,7 @@ int td_io_queue(struct thread_data *td, struct io_u *io_u) } } - if ((td->io_ops->flags & FIO_SYNCIO) == 0) { + if (!td_ioengine_flagged(td, FIO_SYNCIO)) { if (fio_fill_issue_time(td)) fio_gettime(&io_u->issue_time, NULL); @@ -334,7 +380,7 @@ int td_io_queue(struct thread_data *td, struct io_u *io_u) */ if (td->o.read_iolog_file) memcpy(&td->last_issue, &io_u->issue_time, - sizeof(struct timeval)); + sizeof(io_u->issue_time)); } return ret; @@ -346,10 +392,13 @@ int td_io_init(struct thread_data *td) if (td->io_ops->init) { ret = td->io_ops->init(td); - if (ret && td->o.iodepth > 1) { - log_err("fio: io engine init failed. Perhaps try" - " reducing io depth?\n"); - } + if (ret) + log_err("fio: io engine %s init failed.%s\n", + td->io_ops->name, + td->o.iodepth > 1 ? + " Perhaps try reducing io depth?" : ""); + else + td->io_ops_init = 1; if (!td->error) td->error = ret; } @@ -417,43 +466,62 @@ int td_io_open_file(struct thread_data *td, struct fio_file *f) } } - if (td->io_ops->flags & FIO_DISKLESSIO) + if (td_ioengine_flagged(td, FIO_DISKLESSIO)) goto done; if (td->o.invalidate_cache && file_invalidate_cache(td, f)) goto err; - if (td->o.fadvise_hint && - (f->filetype == FIO_TYPE_BD || f->filetype == FIO_TYPE_FILE)) { + if (td->o.fadvise_hint != F_ADV_NONE && + (f->filetype == FIO_TYPE_BLOCK || f->filetype == FIO_TYPE_FILE)) { int flags; - if (td_random(td)) + if (td->o.fadvise_hint == F_ADV_TYPE) { + if (td_random(td)) + flags = POSIX_FADV_RANDOM; + else + flags = POSIX_FADV_SEQUENTIAL; + } else if (td->o.fadvise_hint == F_ADV_RANDOM) flags = POSIX_FADV_RANDOM; - else + else if (td->o.fadvise_hint == F_ADV_SEQUENTIAL) flags = POSIX_FADV_SEQUENTIAL; + else { + log_err("fio: unknown fadvise type %d\n", + td->o.fadvise_hint); + flags = POSIX_FADV_NORMAL; + } if (posix_fadvise(f->fd, f->file_offset, f->io_size, flags) < 0) { td_verror(td, errno, "fadvise"); goto err; } } +#ifdef FIO_HAVE_WRITE_HINT + if (fio_option_is_set(&td->o, write_hint) && + (f->filetype == FIO_TYPE_BLOCK || f->filetype == FIO_TYPE_FILE)) { + uint64_t hint = td->o.write_hint; + int cmd; -#ifdef FIO_OS_DIRECTIO - /* - * Some OS's have a distinct call to mark the file non-buffered, - * instead of using O_DIRECT (Solaris) - */ - if (td->o.odirect) { - int ret = fio_set_odirect(f->fd); + /* + * For direct IO, we just need/want to set the hint on + * the file descriptor. For buffered IO, we need to set + * it on the inode. + */ + if (td->o.odirect) + cmd = F_SET_FILE_RW_HINT; + else + cmd = F_SET_RW_HINT; - if (ret) { - td_verror(td, ret, "fio_set_odirect"); - log_err("fio: the file system does not seem to support direct IO\n"); + if (fcntl(f->fd, cmd, &hint) < 0) { + td_verror(td, errno, "fcntl write hint"); goto err; } } #endif + if (td->o.odirect && !OS_O_DIRECT && fio_set_directio(td, f)) + goto err; + done: log_file(td, f, FIO_LOG_OPEN_FILE); return 0; @@ -475,89 +543,48 @@ int td_io_close_file(struct thread_data *td, struct fio_file *f) fio_file_set_closing(f); disk_util_dec(f->du); - unlock_file_all(td, f); - return put_file(td, f); -} + if (td->o.file_lock_mode != FILE_LOCK_NONE) + unlock_file_all(td, f); -int td_io_get_file_size(struct thread_data *td, struct fio_file *f) -{ - if (!td->io_ops->get_file_size) - return 0; - - return td->io_ops->get_file_size(td, f); + return put_file(td, f); } -static int do_sync_file_range(struct thread_data *td, struct fio_file *f) +int td_io_unlink_file(struct thread_data *td, struct fio_file *f) { - off64_t offset, nbytes; + if (td->io_ops->unlink_file) + return td->io_ops->unlink_file(td, f); + else { + int ret; - offset = f->first_write; - nbytes = f->last_write - f->first_write; + ret = unlink(f->file_name); + if (ret < 0) + return errno; - if (!nbytes) return 0; - - return sync_file_range(f->fd, offset, nbytes, td->o.sync_file_range); -} - -int do_io_u_sync(struct thread_data *td, struct io_u *io_u) -{ - int ret; - - if (io_u->ddir == DDIR_SYNC) { - ret = fsync(io_u->file->fd); - } else if (io_u->ddir == DDIR_DATASYNC) { -#ifdef CONFIG_FDATASYNC - ret = fdatasync(io_u->file->fd); -#else - ret = io_u->xfer_buflen; - io_u->error = EINVAL; -#endif - } else if (io_u->ddir == DDIR_SYNC_FILE_RANGE) - ret = do_sync_file_range(td, io_u->file); - else { - ret = io_u->xfer_buflen; - io_u->error = EINVAL; } - - if (ret < 0) - io_u->error = errno; - - return ret; } -int do_io_u_trim(struct thread_data *td, struct io_u *io_u) +int td_io_get_file_size(struct thread_data *td, struct fio_file *f) { -#ifndef FIO_HAVE_TRIM - io_u->error = EINVAL; - return 0; -#else - struct fio_file *f = io_u->file; - int ret; - - ret = os_trim(f->fd, io_u->offset, io_u->xfer_buflen); - if (!ret) - return io_u->xfer_buflen; + if (!td->io_ops->get_file_size) + return 0; - io_u->error = ret; - return 0; -#endif + return td->io_ops->get_file_size(td, f); } int fio_show_ioengine_help(const char *engine) { struct flist_head *entry; - struct thread_data td; + struct ioengine_ops *io_ops; char *sep; int ret = 1; if (!engine || !*engine) { log_info("Available IO engines:\n"); flist_for_each(entry, &engine_list) { - td.io_ops = flist_entry(entry, struct ioengine_ops, - list); - log_info("\t%s\n", td.io_ops->name); + io_ops = flist_entry(entry, struct ioengine_ops, list); + log_info("\t%s\n", io_ops->name); } return 0; } @@ -567,20 +594,16 @@ int fio_show_ioengine_help(const char *engine) sep++; } - memset(&td, 0, sizeof(td)); - - td.io_ops = load_ioengine(&td, engine); - if (!td.io_ops) { + io_ops = __load_ioengine(engine); + if (!io_ops) { log_info("IO engine %s not found\n", engine); return 1; } - if (td.io_ops->options) - ret = show_cmd_help(td.io_ops->options, sep); + if (io_ops->options) + ret = show_cmd_help(io_ops->options, sep); else - log_info("IO engine %s has no options\n", td.io_ops->name); - - free_ioengine(&td); + log_info("IO engine %s has no options\n", io_ops->name); return ret; }