ioengines: don't call zbd_put_io_u() for engines not implementing commit
[fio.git] / ioengines.c
index 1821916112cc13d3276d44fa6d14b11569b3d04c..3561bb4e6ed0da7289e1079a2d0ffbc7e8f9aa1d 100644 (file)
 #include <dlfcn.h>
 #include <fcntl.h>
 #include <assert.h>
+#include <sys/types.h>
+#include <dirent.h>
 
 #include "fio.h"
 #include "diskutil.h"
+#include "zbd.h"
 
 static FLIST_HEAD(engine_list);
 
-static bool check_engine_ops(struct ioengine_ops *ops)
+static bool check_engine_ops(struct thread_data *td, struct ioengine_ops *ops)
 {
        if (ops->version != FIO_IOOPS_VERSION) {
                log_err("bad ioops version %d (want %d)\n", ops->version,
@@ -40,6 +43,16 @@ static bool check_engine_ops(struct ioengine_ops *ops)
        if (ops->flags & FIO_SYNCIO)
                return false;
 
+       /*
+        * async engines aren't reliable with offload
+        */
+       if ((td->o.io_submit_mode == IO_MODE_OFFLOAD) &&
+           (ops->flags & FIO_NO_OFFLOAD)) {
+               log_err("%s: can't be used with offloaded submit. Use a sync "
+                       "engine\n", ops->name);
+               return true;
+       }
+
        if (!ops->event || !ops->getevents) {
                log_err("%s: no event/getevents handler\n", ops->name);
                return true;
@@ -74,19 +87,46 @@ static struct ioengine_ops *find_ioengine(const char *name)
        return NULL;
 }
 
+#ifdef CONFIG_DYNAMIC_ENGINES
+static void *dlopen_external(struct thread_data *td, const char *engine)
+{
+       char engine_path[PATH_MAX];
+       void *dlhandle;
+
+       sprintf(engine_path, "%s/fio-%s.so", FIO_EXT_ENG_DIR, engine);
+
+       dprint(FD_IO, "dlopen external %s\n", engine_path);
+       dlhandle = dlopen(engine_path, RTLD_LAZY);
+       if (!dlhandle)
+               log_info("Engine %s not found; Either name is invalid, was not built, or fio-engine-%s package is missing.\n",
+                        engine, engine);
+
+       return dlhandle;
+}
+#else
+#define dlopen_external(td, engine) (NULL)
+#endif
+
 static struct ioengine_ops *dlopen_ioengine(struct thread_data *td,
                                            const char *engine_lib)
 {
        struct ioengine_ops *ops;
        void *dlhandle;
 
-       dprint(FD_IO, "dload engine %s\n", engine_lib);
+       if (!strncmp(engine_lib, "linuxaio", 8) ||
+           !strncmp(engine_lib, "aio", 3))
+               engine_lib = "libaio";
+
+       dprint(FD_IO, "dlopen engine %s\n", engine_lib);
 
        dlerror();
        dlhandle = dlopen(engine_lib, RTLD_LAZY);
        if (!dlhandle) {
-               td_vmsg(td, -1, dlerror(), "dlopen");
-               return NULL;
+               dlhandle = dlopen_external(td, engine_lib);
+               if (!dlhandle) {
+                       td_vmsg(td, -1, dlerror(), "dlopen");
+                       return NULL;
+               }
        }
 
        /*
@@ -116,23 +156,19 @@ static struct ioengine_ops *dlopen_ioengine(struct thread_data *td,
                return NULL;
        }
 
-       td->io_ops_dlhandle = dlhandle;
+       ops->dlhandle = dlhandle;
        return ops;
 }
 
-static struct ioengine_ops *__load_ioengine(const char *name)
+static struct ioengine_ops *__load_ioengine(const char *engine)
 {
-       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)) {
-               dprint(FD_IO, "converting ioengine name: %s -> libaio\n", name);
-               strcpy(engine, "libaio");
+               dprint(FD_IO, "converting ioengine name: %s -> libaio\n",
+                      engine);
+               engine = "libaio";
        }
 
        dprint(FD_IO, "load ioengine %s\n", engine);
@@ -159,7 +195,9 @@ struct ioengine_ops *load_ioengine(struct thread_data *td)
         * so as not to break job files not using the prefix.
         */
        ops = __load_ioengine(td->o.ioengine);
-       if (!ops)
+
+       /* We do re-dlopen existing handles, for reference counting */
+       if (!ops || ops->dlhandle)
                ops = dlopen_ioengine(td, name);
 
        /*
@@ -174,7 +212,7 @@ struct ioengine_ops *load_ioengine(struct thread_data *td)
        /*
         * Check that the required methods are there.
         */
-       if (check_engine_ops(ops))
+       if (check_engine_ops(td, ops))
                return NULL;
 
        return ops;
@@ -193,9 +231,10 @@ void free_ioengine(struct thread_data *td)
                td->eo = NULL;
        }
 
-       if (td->io_ops_dlhandle) {
-               dlclose(td->io_ops_dlhandle);
-               td->io_ops_dlhandle = NULL;
+       if (td->io_ops->dlhandle) {
+               dprint(FD_IO, "dlclose ioengine %s\n", td->io_ops->name);
+               dlclose(td->io_ops->dlhandle);
+               td->io_ops->dlhandle = NULL;
        }
 
        td->io_ops = NULL;
@@ -288,6 +327,17 @@ enum fio_q_status td_io_queue(struct thread_data *td, struct io_u *io_u)
        assert((io_u->flags & IO_U_F_FLIGHT) == 0);
        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)) {
+               int res = pthread_mutex_unlock(&overlap_check);
+               assert(res == 0);
+       }
+
        assert(fio_file_open(io_u->file));
 
        /*
@@ -298,7 +348,9 @@ enum fio_q_status td_io_queue(struct thread_data *td, struct io_u *io_u)
        io_u->error = 0;
        io_u->resid = 0;
 
-       if (td_ioengine_flagged(td, 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);
 
@@ -310,6 +362,7 @@ enum fio_q_status td_io_queue(struct thread_data *td, struct io_u *io_u)
                                        sizeof(io_u->issue_time));
        }
 
+
        if (ddir_rw(ddir)) {
                if (!(io_u->flags & IO_U_F_VER_LIST)) {
                        td->io_issues[ddir]++;
@@ -319,10 +372,7 @@ enum fio_q_status td_io_queue(struct thread_data *td, struct io_u *io_u)
        }
 
        ret = td->io_ops->queue(td, io_u);
-       if (ret != FIO_Q_BUSY && io_u->post_submit) {
-               io_u->post_submit(io_u, io_u->error == 0);
-               io_u->post_submit = NULL;
-       }
+       zbd_queue_io_u(td, io_u, ret);
 
        unlock_file(td, io_u->file);
 
@@ -354,27 +404,38 @@ enum fio_q_status td_io_queue(struct thread_data *td, struct io_u *io_u)
                         "invalid block size. Try setting direct=0.\n");
        }
 
+       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);
        }
 
        if (ret == FIO_Q_COMPLETED) {
-               if (ddir_rw(io_u->ddir) || ddir_sync(io_u->ddir)) {
+               if (ddir_rw(io_u->ddir) ||
+                   (ddir_sync(io_u->ddir) && td->runstate != TD_FSYNCING)) {
                        io_u_mark_depth(td, 1);
                        td->ts.total_io_u[io_u->ddir]++;
                }
        } else if (ret == FIO_Q_QUEUED) {
                td->io_u_queued++;
 
-               if (ddir_rw(io_u->ddir) || ddir_sync(io_u->ddir))
+               if (ddir_rw(io_u->ddir) ||
+                   (ddir_sync(io_u->ddir) && td->runstate != TD_FSYNCING))
                        td->ts.total_io_u[io_u->ddir]++;
 
                if (td->io_u_queued >= td->o.iodepth_batch)
                        td_io_commit(td);
        }
 
-       if (!td_ioengine_flagged(td, 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);
 
@@ -578,6 +639,34 @@ int td_io_get_file_size(struct thread_data *td, struct fio_file *f)
        return td->io_ops->get_file_size(td, f);
 }
 
+#ifdef CONFIG_DYNAMIC_ENGINES
+/* Load all dynamic engines in FIO_EXT_ENG_DIR for enghelp command */
+static void
+fio_load_dynamic_engines(struct thread_data *td)
+{
+       DIR *dirhandle = NULL;
+       struct dirent *dirent = NULL;
+       char engine_path[PATH_MAX];
+
+       dirhandle = opendir(FIO_EXT_ENG_DIR);
+       if (!dirhandle)
+               return;
+
+       while ((dirent = readdir(dirhandle)) != NULL) {
+               if (!strcmp(dirent->d_name, ".") ||
+                   !strcmp(dirent->d_name, ".."))
+                       continue;
+
+               sprintf(engine_path, "%s/%s", FIO_EXT_ENG_DIR, dirent->d_name);
+               dlopen_ioengine(td, engine_path);
+       }
+
+       closedir(dirhandle);
+}
+#else
+#define fio_load_dynamic_engines(td) do { } while (0)
+#endif
+
 int fio_show_ioengine_help(const char *engine)
 {
        struct flist_head *entry;
@@ -586,8 +675,11 @@ int fio_show_ioengine_help(const char *engine)
        char *sep;
        int ret = 1;
 
+       memset(&td, 0, sizeof(struct thread_data));
+
        if (!engine || !*engine) {
                log_info("Available IO engines:\n");
+               fio_load_dynamic_engines(&td);
                flist_for_each(entry, &engine_list) {
                        io_ops = flist_entry(entry, struct ioengine_ops, list);
                        log_info("\t%s\n", io_ops->name);
@@ -600,7 +692,6 @@ int fio_show_ioengine_help(const char *engine)
                sep++;
        }
 
-       memset(&td, 0, sizeof(struct thread_data));
        td.o.ioengine = (char *)engine;
        io_ops = load_ioengine(&td);