Fio 3.15
[fio.git] / ioengines.c
index e2e7280a8edcd0b46a2a2c9daa56914483cca143..aa4ccd2755c96f843d69333448795b0b16061723 100644 (file)
@@ -9,7 +9,6 @@
  * generic io engine that could be used for other projects.
  *
  */
-#include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
@@ -19,6 +18,7 @@
 
 #include "fio.h"
 #include "diskutil.h"
+#include "zbd.h"
 
 static FLIST_HEAD(engine_list);
 
@@ -52,14 +52,12 @@ static bool check_engine_ops(struct ioengine_ops *ops)
 void unregister_ioengine(struct ioengine_ops *ops)
 {
        dprint(FD_IO, "ioengine %s unregistered\n", ops->name);
-       flist_del(&ops->list);
-       INIT_FLIST_HEAD(&ops->list);
+       flist_del_init(&ops->list);
 }
 
 void register_ioengine(struct ioengine_ops *ops)
 {
        dprint(FD_IO, "ioengine %s registered\n", ops->name);
-       INIT_FLIST_HEAD(&ops->list);
        flist_add_tail(&ops->list, &engine_list);
 }
 
@@ -119,29 +117,56 @@ static struct ioengine_ops *dlopen_ioengine(struct thread_data *td,
                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);
 
        /*
         * linux libaio has alias names, so convert to what we want
         */
-       if (!strncmp(engine, "linuxaio", 8) || !strncmp(engine, "aio", 3))
+       if (!strncmp(engine, "linuxaio", 8)) {
+               dprint(FD_IO, "converting ioengine name: %s -> libaio\n", name);
                strcpy(engine, "libaio");
+       }
+
+       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;
 
-       ops = find_ioengine(engine);
+       /*
+        * 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;
@@ -153,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;
 }
 
 /*
@@ -173,10 +194,11 @@ 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);
+               td->io_ops_dlhandle = NULL;
+       }
 
-       free(td->io_ops);
        td->io_ops = NULL;
 }
 
@@ -186,7 +208,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);
@@ -202,7 +224,8 @@ int td_io_prep(struct thread_data *td, struct io_u *io_u)
        if (td->io_ops->prep) {
                int ret = td->io_ops->prep(td, io_u);
 
-               dprint(FD_IO, "->prep(%p)=%d\n", io_u, ret);
+               dprint(FD_IO, "prep: io_u %p: ret=%d\n", io_u, ret);
+
                if (ret)
                        unlock_file(td, io_u->file);
                return ret;
@@ -254,17 +277,26 @@ out:
        return r;
 }
 
-int td_io_queue(struct thread_data *td, struct io_u *io_u)
+enum fio_q_status 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;
+       unsigned long long buflen = io_u->xfer_buflen;
+       enum fio_q_status ret;
 
        dprint_io_u(io_u, "queue");
        fio_ro_check(td, io_u);
 
        assert((io_u->flags & IO_U_F_FLIGHT) == 0);
-       io_u_set(io_u, IO_U_F_FLIGHT);
+       io_u_set(td, io_u, IO_U_F_FLIGHT);
+
+       /*
+        * If overlap checking was enabled in offload mode we
+        * can release this lock that was acquired when we
+        * started the overlap check because the IO_U_F_FLIGHT
+        * flag is now set
+        */
+       if (td_offload_overlap(td))
+               pthread_mutex_unlock(&overlap_check);
 
        assert(fio_file_open(io_u->file));
 
@@ -276,7 +308,9 @@ 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) ||
+               (td_ioengine_flagged(td, FIO_ASYNCIO_SYNC_TRIM) && 
+               io_u->ddir == DDIR_TRIM)) {
                if (fio_fill_issue_time(td))
                        fio_gettime(&io_u->issue_time, NULL);
 
@@ -285,16 +319,19 @@ 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(ddir)) {
-               td->io_issues[ddir]++;
-               td->io_issue_bytes[ddir] += buflen;
+               if (!(io_u->flags & IO_U_F_VER_LIST)) {
+                       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);
+       zbd_queue_io_u(io_u, ret);
 
        unlock_file(td, io_u->file);
 
@@ -302,6 +339,7 @@ int td_io_queue(struct thread_data *td, struct io_u *io_u)
                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);
        }
 
        /*
@@ -321,36 +359,41 @@ 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. Try "
-                        "setting direct=0.\n");
+                        "support direct IO, or iomem_align= is bad, or "
+                        "invalid block size. Try setting direct=0.\n");
        }
 
-       if (!td->io_ops->commit || io_u->ddir == DDIR_TRIM) {
+       if (zbd_unaligned_write(io_u->error) &&
+           td->io_issues[io_u->ddir & 1] == 1 &&
+           td->o.zone_mode != ZONE_MODE_ZBD) {
+               log_info("fio: first I/O failed. If %s is a zoned block device, consider --zonemode=zbd\n",
+                        io_u->file->file_name);
+       }
+
+       if (!td->io_ops->commit) {
                io_u_mark_submit(td, 1);
                io_u_mark_complete(td, 1);
+               zbd_put_io_u(io_u);
        }
 
        if (ret == FIO_Q_COMPLETED) {
-               if (ddir_rw(io_u->ddir)) {
+               if (ddir_rw(io_u->ddir) || ddir_sync(io_u->ddir)) {
                        io_u_mark_depth(td, 1);
                        td->ts.total_io_u[io_u->ddir]++;
                }
        } else if (ret == FIO_Q_QUEUED) {
-               int r;
-
                td->io_u_queued++;
 
-               if (ddir_rw(io_u->ddir))
+               if (ddir_rw(io_u->ddir) || ddir_sync(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);
-                       if (r < 0)
-                               return r;
-               }
+               if (td->io_u_queued >= td->o.iodepth_batch)
+                       td_io_commit(td);
        }
 
-       if ((td->io_ops->flags & FIO_SYNCIO) == 0) {
+       if (!td_ioengine_flagged(td, FIO_SYNCIO) &&
+               (!td_ioengine_flagged(td, FIO_ASYNCIO_SYNC_TRIM) ||
+                io_u->ddir != DDIR_TRIM)) {
                if (fio_fill_issue_time(td))
                        fio_gettime(&io_u->issue_time, NULL);
 
@@ -359,7 +402,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;
@@ -371,28 +414,28 @@ 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;
        }
 
-       if (!ret && (td->io_ops->flags & FIO_NOIO))
-               td->flags |= TD_F_NOIO;
-
        return ret;
 }
 
-int td_io_commit(struct thread_data *td)
+void td_io_commit(struct thread_data *td)
 {
        int ret;
 
        dprint(FD_IO, "calling ->commit(), depth %d\n", td->cur_depth);
 
        if (!td->cur_depth || !td->io_u_queued)
-               return 0;
+               return;
 
        io_u_mark_depth(td, td->io_u_queued);
 
@@ -407,14 +450,21 @@ int td_io_commit(struct thread_data *td)
         */
        td->io_u_in_flight += td->io_u_queued;
        td->io_u_queued = 0;
-
-       return 0;
 }
 
 int td_io_open_file(struct thread_data *td, struct fio_file *f)
 {
+       if (fio_file_closing(f)) {
+               /*
+                * Open translates to undo closing.
+                */
+               fio_file_clear_closing(f);
+               get_file(f);
+               return 0;
+       }
        assert(!fio_file_open(f));
        assert(f->fd == -1);
+       assert(td->io_ops->open_file);
 
        if (td->io_ops->open_file(td, f)) {
                if (td->error == EINVAL && td->o.odirect)
@@ -445,54 +495,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;
-
-               if (posix_fadvise(f->fd, f->file_offset, f->io_size, flags) < 0) {
-                       td_verror(td, errno, "fadvise");
-                       goto err;
+               else {
+                       log_err("fio: unknown fadvise type %d\n",
+                                                       td->o.fadvise_hint);
+                       flags = POSIX_FADV_NORMAL;
                }
-       }
-#ifdef FIO_HAVE_STREAMID
-       if (td->o.fadvise_stream &&
-           (f->filetype == FIO_TYPE_BD || f->filetype == FIO_TYPE_FILE)) {
-               off_t stream = td->o.fadvise_stream;
 
-               if (posix_fadvise(f->fd, stream, f->io_size, POSIX_FADV_STREAMID) < 0) {
-                       td_verror(td, errno, "fadvise streamid");
-                       goto err;
+               if (posix_fadvise(f->fd, f->file_offset, f->io_size, flags) < 0) {
+                       if (!fio_did_warn(FIO_WARN_FADVISE))
+                               log_err("fio: fadvise hint failed\n");
                }
        }
-#endif
+#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;
@@ -513,11 +571,6 @@ int td_io_close_file(struct thread_data *td, struct fio_file *f)
         */
        fio_file_set_closing(f);
 
-       disk_util_dec(f->du);
-
-       if (td->o.file_lock_mode != FILE_LOCK_NONE)
-               unlock_file_all(td, f);
-
        return put_file(td, f);
 }
 
@@ -525,89 +578,38 @@ int td_io_unlink_file(struct thread_data *td, struct fio_file *f)
 {
        if (td->io_ops->unlink_file)
                return td->io_ops->unlink_file(td, f);
-       else
-               return unlink(f->file_name);
-}
-
-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);
-}
-
-static int do_sync_file_range(const struct thread_data *td,
-                             struct fio_file *f)
-{
-       off64_t offset, nbytes;
+       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(const 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(const 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;
        }
@@ -617,20 +619,20 @@ int fio_show_ioengine_help(const char *engine)
                sep++;
        }
 
-       memset(&td, 0, sizeof(td));
+       memset(&td, 0, sizeof(struct thread_data));
+       td.o.ioengine = (char *)engine;
+       io_ops = load_ioengine(&td);
 
-       td.io_ops = load_ioengine(&td, engine);
-       if (!td.io_ops) {
+       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);
+               log_info("IO engine %s has no options\n", io_ops->name);
 
        free_ioengine(&td);
-
        return ret;
 }