Add strong madvise() hint for cache pruning
[fio.git] / engines / sg.c
index 6c997bd47cad75523582ea77bc7da454396c69f0..57c3834add235362acdd18ee672c176456ae9e16 100644 (file)
@@ -1,5 +1,7 @@
 /*
- * scsi generic sg v3 io engine
+ * sg engine
+ *
+ * IO engine that uses the Linux SG v3 interface to talk to SCSI devices
  *
  */
 #include <stdio.h>
@@ -10,7 +12,6 @@
 #include <sys/poll.h>
 
 #include "../fio.h"
-#include "../os.h"
 
 #ifdef FIO_HAVE_SGIO
 
@@ -22,7 +23,11 @@ struct sgio_cmd {
 struct sgio_data {
        struct sgio_cmd *cmds;
        struct io_u **events;
+       struct pollfd *pfds;
+       int *fd_flags;
+       void *sgbuf;
        unsigned int bs;
+       int type_checked;
 };
 
 static void sgio_hdr_init(struct sgio_data *sd, struct sg_io_hdr *hdr,
@@ -45,21 +50,6 @@ static void sgio_hdr_init(struct sgio_data *sd, struct sg_io_hdr *hdr,
        }
 }
 
-static int fio_sgio_ioctl_getevents(struct thread_data *td, int fio_unused min,
-                                   int max, struct timespec fio_unused *t)
-{
-       assert(max <= 1);
-
-       /*
-        * we can only have one finished io_u for sync io, since the depth
-        * is always 1
-        */
-       if (list_empty(&td->io_u_busylist))
-               return 0;
-
-       return 1;
-}
-
 static int pollin_events(struct pollfd *pfds, int fds)
 {
        int i;
@@ -71,38 +61,30 @@ static int pollin_events(struct pollfd *pfds, int fds)
        return 0;
 }
 
-static int fio_sgio_getevents(struct thread_data *td, int min, int max,
-                             struct timespec fio_unused *t)
+static int fio_sgio_getevents(struct thread_data *td, unsigned int min,
+                             unsigned int max, struct timespec fio_unused *t)
 {
-       /*
-        * normally hard coding &td->files[0] is a bug that needs to be fixed,
-        * but it's ok here as all files should point to the same device.
-        */
-       struct fio_file *f = &td->files[0];
        struct sgio_data *sd = td->io_ops->data;
-       int left = max, ret, events, i, r = 0, *fl;
-       struct pollfd *pfds;
-       void *buf;
+       int left = max, ret, r = 0;
+       void *buf = sd->sgbuf;
+       unsigned int i, events;
+       struct fio_file *f;
 
        /*
         * Fill in the file descriptors
         */
-       pfds = malloc(sizeof(struct pollfd) * td->nr_files);
-       fl = malloc(sizeof(int) * td->nr_files);
-
        for_each_file(td, f, i) {
                /*
                 * don't block for min events == 0
                 */
                if (!min) {
-                       fl[i] = fcntl(f->fd, F_GETFL);
-                       fcntl(f->fd, F_SETFL, fl[i] | O_NONBLOCK);
+                       sd->fd_flags[i] = fcntl(f->fd, F_GETFL);
+                       fcntl(f->fd, F_SETFL, sd->fd_flags[i] | O_NONBLOCK);
                }
-               pfds[i].fd = f->fd;
-               pfds[i].events = POLLIN;
+               sd->pfds[i].fd = f->fd;
+               sd->pfds[i].events = POLLIN;
        }
 
-       buf = malloc(max * sizeof(struct sg_io_hdr));
        while (left) {
                void *p;
 
@@ -110,16 +92,16 @@ static int fio_sgio_getevents(struct thread_data *td, int min, int max,
                        if (!min)
                                break;
 
-                       ret = poll(pfds, td->nr_files, -1);
+                       ret = poll(sd->pfds, td->o.nr_files, -1);
                        if (ret < 0) {
-                               td_verror(td, errno);
                                if (!r)
-                                       r = -1;
+                                       r = -errno;
+                               td_verror(td, errno, "poll");
                                break;
                        } else if (!ret)
                                continue;
 
-                       if (pollin_events(pfds, td->nr_files))
+                       if (pollin_events(sd->pfds, td->o.nr_files))
                                break;
                } while (1);
 
@@ -134,8 +116,8 @@ re_read:
                        if (ret < 0) {
                                if (errno == EAGAIN)
                                        continue;
-                               td_verror(td, errno);
-                               r = -1;
+                               r = -errno;
+                               td_verror(td, errno, "read");
                                break;
                        } else if (ret) {
                                p += ret;
@@ -162,12 +144,9 @@ re_read:
 
        if (!min) {
                for_each_file(td, f, i)
-                       fcntl(f->fd, F_SETFL, fl[i]);
+                       fcntl(f->fd, F_SETFL, sd->fd_flags[i]);
        }
 
-       free(buf);
-       free(pfds);
-       free(fl);
        return r;
 }
 
@@ -176,10 +155,15 @@ static int fio_sgio_ioctl_doio(struct thread_data *td,
 {
        struct sgio_data *sd = td->io_ops->data;
        struct sg_io_hdr *hdr = &io_u->hdr;
+       int ret;
 
        sd->events[0] = io_u;
 
-       return ioctl(f->fd, SG_IO, hdr);
+       ret = ioctl(f->fd, SG_IO, hdr);
+       if (ret < 0)
+               return ret;
+
+       return FIO_Q_COMPLETED;
 }
 
 static int fio_sgio_rw_doio(struct fio_file *f, struct io_u *io_u, int sync)
@@ -189,22 +173,23 @@ static int fio_sgio_rw_doio(struct fio_file *f, struct io_u *io_u, int sync)
 
        ret = write(f->fd, hdr, sizeof(*hdr));
        if (ret < 0)
-               return errno;
+               return ret;
 
        if (sync) {
                ret = read(f->fd, hdr, sizeof(*hdr));
                if (ret < 0)
-                       return errno;
+                       return ret;
+               return FIO_Q_COMPLETED;
        }
 
-       return 0;
+       return FIO_Q_QUEUED;
 }
 
 static int fio_sgio_doio(struct thread_data *td, struct io_u *io_u, int sync)
 {
        struct fio_file *f = io_u->file;
 
-       if (td->filetype == FIO_TYPE_BD)
+       if (f->filetype == FIO_TYPE_BD)
                return fio_sgio_ioctl_doio(td, f, io_u);
 
        return fio_sgio_rw_doio(f, io_u, sync);
@@ -255,9 +240,14 @@ static int fio_sgio_prep(struct thread_data *td, struct io_u *io_u)
 static int fio_sgio_queue(struct thread_data *td, struct io_u *io_u)
 {
        struct sg_io_hdr *hdr = &io_u->hdr;
-       int ret;
+       int ret, do_sync = 0;
+
+       fio_ro_check(td, io_u);
+
+       if (td->o.sync_io || td->o.odirect || ddir_sync(io_u->ddir))
+               do_sync = 1;
 
-       ret = fio_sgio_doio(td, io_u, io_u->ddir == DDIR_SYNC);
+       ret = fio_sgio_doio(td, io_u, do_sync);
 
        if (ret < 0)
                io_u->error = errno;
@@ -266,7 +256,12 @@ static int fio_sgio_queue(struct thread_data *td, struct io_u *io_u)
                io_u->error = EIO;
        }
 
-       return io_u->error;
+       if (io_u->error) {
+               td_verror(td, io_u->error, "xfer");
+               return FIO_Q_COMPLETED;
+       }
+
+       return ret;
 }
 
 static struct io_u *fio_sgio_event(struct thread_data *td, int event)
@@ -285,7 +280,7 @@ static int fio_sgio_get_bs(struct thread_data *td, unsigned int *bs)
        int ret;
 
        io_u = __get_io_u(td);
-       io_u->file = &td->files[0];
+       io_u->file = td->files[0];
        assert(io_u);
 
        hdr = &io_u->hdr;
@@ -310,66 +305,95 @@ static int fio_sgio_get_bs(struct thread_data *td, unsigned int *bs)
 
 static void fio_sgio_cleanup(struct thread_data *td)
 {
-       if (td->io_ops->data) {
-               free(td->io_ops->data);
-               td->io_ops->data = NULL;
+       struct sgio_data *sd = td->io_ops->data;
+
+       if (sd) {
+               free(sd->events);
+               free(sd->cmds);
+               free(sd->fd_flags);
+               free(sd->pfds);
+               free(sd->sgbuf);
+               free(sd);
        }
 }
 
 static int fio_sgio_init(struct thread_data *td)
 {
-       struct fio_file *f = &td->files[0];
        struct sgio_data *sd;
-       unsigned int bs;
-       int ret;
 
        sd = malloc(sizeof(*sd));
        memset(sd, 0, sizeof(*sd));
-       sd->cmds = malloc(td->iodepth * sizeof(struct sgio_cmd));
-       memset(sd->cmds, 0, td->iodepth * sizeof(struct sgio_cmd));
-       sd->events = malloc(td->iodepth * sizeof(struct io_u *));
-       memset(sd->events, 0, td->iodepth * sizeof(struct io_u *));
+       sd->cmds = malloc(td->o.iodepth * sizeof(struct sgio_cmd));
+       memset(sd->cmds, 0, td->o.iodepth * sizeof(struct sgio_cmd));
+       sd->events = malloc(td->o.iodepth * sizeof(struct io_u *));
+       memset(sd->events, 0, td->o.iodepth * sizeof(struct io_u *));
+       sd->pfds = malloc(sizeof(struct pollfd) * td->o.nr_files);
+       memset(sd->pfds, 0, sizeof(struct pollfd) * td->o.nr_files);
+       sd->fd_flags = malloc(sizeof(int) * td->o.nr_files);
+       memset(sd->fd_flags, 0, sizeof(int) * td->o.nr_files);
+       sd->sgbuf = malloc(sizeof(struct sg_io_hdr) * td->o.iodepth);
+       memset(sd->sgbuf, 0, sizeof(struct sg_io_hdr) * td->o.iodepth);
+
        td->io_ops->data = sd;
 
-       if (td->filetype == FIO_TYPE_BD) {
+       /*
+        * we want to do it, regardless of whether odirect is set or not
+        */
+       td->o.override_sync = 1;
+       return 0;
+}
+
+static int fio_sgio_type_check(struct thread_data *td, struct fio_file *f)
+{
+       struct sgio_data *sd = td->io_ops->data;
+       unsigned int bs;
+
+       if (f->filetype == FIO_TYPE_BD) {
                if (ioctl(f->fd, BLKSSZGET, &bs) < 0) {
-                       td_verror(td, errno);
-                       goto err;
+                       td_verror(td, errno, "ioctl");
+                       return 1;
                }
-       } else if (td->filetype == FIO_TYPE_CHAR) {
-               int version;
+       } else if (f->filetype == FIO_TYPE_CHAR) {
+               int version, ret;
 
                if (ioctl(f->fd, SG_GET_VERSION_NUM, &version) < 0) {
-                       td_verror(td, errno);
-                       goto err;
+                       td_verror(td, errno, "ioctl");
+                       return 1;
                }
 
                ret = fio_sgio_get_bs(td, &bs);
                if (ret)
-                       goto err;
+                       return 1;
        } else {
-               log_err("ioengine sgio only works on block devices\n");
-               goto err;
+               log_err("ioengine sg only works on block devices\n");
+               return 1;
        }
 
        sd->bs = bs;
 
-       if (td->filetype == FIO_TYPE_BD)
-               td->io_ops->getevents = fio_sgio_ioctl_getevents;
-       else
-               td->io_ops->getevents = fio_sgio_getevents;
+       if (f->filetype == FIO_TYPE_BD) {
+               td->io_ops->getevents = NULL;
+               td->io_ops->event = NULL;
+       }
+
+       return 0;
+}
+
+static int fio_sgio_open(struct thread_data *td, struct fio_file *f)
+{
+       struct sgio_data *sd = td->io_ops->data;
+       int ret;
+
+       ret = generic_open_file(td, f);
+       if (ret)
+               return ret;
+
+       if (sd && !sd->type_checked && fio_sgio_type_check(td, f)) {
+               ret = generic_close_file(td, f);
+               return 1;
+       }
 
-       /*
-        * we want to do it, regardless of whether odirect is set or not
-        */
-       td->override_sync = 1;
        return 0;
-err:
-       free(sd->events);
-       free(sd->cmds);
-       free(sd);
-       td->io_ops->data = NULL;
-       return 1;
 }
 
 static struct ioengine_ops ioengine = {
@@ -381,6 +405,9 @@ static struct ioengine_ops ioengine = {
        .getevents      = fio_sgio_getevents,
        .event          = fio_sgio_event,
        .cleanup        = fio_sgio_cleanup,
+       .open_file      = fio_sgio_open,
+       .close_file     = generic_close_file,
+       .get_file_size  = generic_get_file_size,
        .flags          = FIO_SYNCIO | FIO_RAWIO,
 };
 
@@ -393,12 +420,12 @@ static struct ioengine_ops ioengine = {
  */
 static int fio_sgio_init(struct thread_data fio_unused *td)
 {
-       fprintf(stderr, "fio: sgio not available\n");
+       fprintf(stderr, "fio: ioengine sg not available\n");
        return 1;
 }
 
 static struct ioengine_ops ioengine = {
-       .name           = "sgio",
+       .name           = "sg",
        .version        = FIO_IOOPS_VERSION,
        .init           = fio_sgio_init,
 };