[PATCH] Simple support for networked IO
authorJens Axboe <jens.axboe@oracle.com>
Tue, 6 Feb 2007 13:43:52 +0000 (14:43 +0100)
committerJens Axboe <jens.axboe@oracle.com>
Tue, 6 Feb 2007 13:43:52 +0000 (14:43 +0100)
Adds a new ioengine, net. Use with ioengine=net, it supports only strict
reading or writing (no mixed reads/writes) to/from a single host.
The filename given must contain the host and port to connect to (or
listen from).

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
12 files changed:
HOWTO
Makefile
Makefile.FreeBSD
Makefile.solaris
README
engines/net.c [new file with mode: 0644]
filesetup.c
fio.c
fio.h
init.c
parse.c
stat.c

diff --git a/HOWTO b/HOWTO
index f4ebac9..38f5dce 100644 (file)
--- a/HOWTO
+++ b/HOWTO
@@ -203,7 +203,9 @@ directory=str       Prefix filenames with this directory. Used to places files
 filename=str   Fio normally makes up a filename based on the job name,
                thread number, and file number. If you want to share
                files between threads in a job or several jobs, specify
-               a filename for each of them to override the default.
+               a filename for each of them to override the default. If
+               the ioengine used is 'net', the filename is the host and
+               port to connect to in the format of =host:port.
 
 rw=str         Type of io pattern. Accepted values are:
 
@@ -278,6 +280,12 @@ ioengine=str       Defines how the job issues io to the file. The following
                                to. This is mainly used to exercise fio
                                itself and for debugging/testing purposes.
 
+                       net     Transfer over the network to given host:port.
+                               'filename' must be set appropriately to
+                               filename=host:port regardless of send
+                               or receive, if the latter only the port
+                               argument is used.
+
 iodepth=int    This defines how many io units to keep in flight against
                the file. The default is 1 for each file defined in this
                job, can be overridden with a larger value for higher
index 3b919d5..fe9a8cd 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -15,6 +15,7 @@ OBJS += engines/sg.o
 OBJS += engines/splice.o
 OBJS += engines/sync.o
 OBJS += engines/null.o
+OBJS += engines/net.o
 
 INSTALL = install
 prefix = /usr/local
index b862820..5c85a0c 100644 (file)
@@ -10,6 +10,7 @@ OBJS += engines/mmap.o
 OBJS += engines/posixaio.o
 OBJS += engines/sync.o
 OBJS += engines/null.o
+OBJS += engines/net.o
 
 all: depend $(PROGS) $(SCRIPTS)
 
index 8d8bec3..4373abf 100644 (file)
@@ -10,6 +10,7 @@ OBJS += engines/mmap.o
 OBJS += engines/posixaio.o
 OBJS += engines/sync.o
 OBJS += engines/null.o
+OBJS += engines/net.o
 
 all: depend $(PROGS) $(SCRIPTS)
 
diff --git a/README b/README
index be32af4..f7d7457 100644 (file)
--- a/README
+++ b/README
@@ -101,11 +101,11 @@ The job file parameters are:
        ioengine=x      'x' may be: aio/libaio/linuxaio for Linux aio,
                        posixaio for POSIX aio, sync for regular read/write io,
                        mmap for mmap'ed io, splice for using splice/vmsplice,
-                       or sgio for direct SG_IO io. The latter only works on
-                       Linux on SCSI (or SCSI-like devices, such as
-                       usb-storage or sata/libata driven) devices. Fio also
-                       has a null io engine, which is mainly used for testing
-                       fio itself.
+                       sgio for direct SG_IO io, or net for network io. sgio
+                       only works on Linux on SCSI (or SCSI-like devices,
+                       such as usb-storage or sata/libata driven) devices.
+                       Fio also has a null io engine, which is mainly used
+                       for testing fio itself.
        iodepth=x       For async io, allow 'x' ios in flight
        overwrite=x     If 'x', layout a write file first.
        nrfiles=x       Spread io load over 'x' number of files per job,
diff --git a/engines/net.c b/engines/net.c
new file mode 100644 (file)
index 0000000..8ef7811
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * Transfer data over the net. Pretty basic setup, will only support
+ * 1 file per thread/job.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "../fio.h"
+#include "../os.h"
+
+struct net_data {
+       int send_to_net;
+       struct io_u *last_io_u;
+};
+
+static int fio_netio_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 struct io_u *fio_netio_event(struct thread_data *td, int event)
+{
+       struct net_data *nd = td->io_ops->data;
+
+       assert(event == 0);
+
+       return nd->last_io_u;
+}
+
+static int fio_netio_prep(struct thread_data *td, struct io_u *io_u)
+{
+       struct net_data *nd = td->io_ops->data;
+       struct fio_file *f = io_u->file;
+
+       if (nd->send_to_net) {
+               if (io_u->ddir == DDIR_READ) {
+                       td_verror(td, EINVAL);
+                       return 1;
+               }
+       } else {
+               if (io_u->ddir == DDIR_WRITE) {
+                       td_verror(td, EINVAL);
+                       return 1;
+               }
+       }
+
+       if (io_u->ddir == DDIR_SYNC)
+               return 0;
+       if (io_u->offset == f->last_completed_pos)
+               return 0;
+
+       td_verror(td, EINVAL);
+       return 1;
+}
+
+static int fio_netio_queue(struct thread_data *td, struct io_u *io_u)
+{
+       struct net_data *nd = td->io_ops->data;
+       struct fio_file *f = io_u->file;
+       unsigned int ret = 0;
+
+       if (io_u->ddir == DDIR_WRITE)
+               ret = write(f->fd, io_u->buf, io_u->buflen);
+       else if (io_u->ddir == DDIR_READ)
+               ret = read(f->fd, io_u->buf, io_u->buflen);
+
+       if (ret != io_u->buflen) {
+               if (ret > 0) {
+                       io_u->resid = io_u->buflen - ret;
+                       io_u->error = EIO;
+               } else
+                       io_u->error = errno;
+       }
+
+       if (!io_u->error)
+               nd->last_io_u = io_u;
+
+       return io_u->error;
+}
+
+static int fio_netio_setup_connect(struct thread_data *td, const char *host,
+                                  const char *port)
+{
+       struct sockaddr_in addr;
+       struct fio_file *f;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_port = htons(atoi(port));
+
+       if (inet_aton(host, &addr.sin_addr) != 1) {
+               struct hostent *hent = gethostbyname(host);
+
+               if (!hent) {
+                       td_vmsg(td, errno, "gethostbyname");
+                       return 1;
+               }
+
+               memcpy(&addr.sin_addr, hent->h_addr, 4);
+       }
+
+       f = &td->files[0];
+
+       f->fd = socket(AF_INET, SOCK_STREAM, 0);
+       if (f->fd < 0) {
+               td_vmsg(td, errno, "socket");
+               return 1;
+       }
+
+       if (connect(f->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               td_vmsg(td, errno, "connect");
+               return 1;
+       }
+
+       return 0;
+
+}
+
+static int fio_netio_setup_listen(struct thread_data *td, const char *port)
+{
+       struct sockaddr_in addr;
+       socklen_t socklen;
+       int fd, opt;
+
+       fd = socket(AF_INET, SOCK_STREAM, 0);
+       if (fd < 0) {
+               td_vmsg(td, errno, "socket");
+               return 1;
+       }
+
+       opt = 1;
+       if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
+               td_vmsg(td, errno, "setsockopt");
+               return 1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = htonl(INADDR_ANY);
+       addr.sin_port = htons(atoi(port));
+
+       if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               td_vmsg(td, errno, "bind");
+               return 1;
+       }
+       if (listen(fd, 1) < 0) {
+               td_vmsg(td, errno, "listen");
+               return 1;
+       }
+
+       socklen = sizeof(addr);
+       td->files[0].fd = accept(fd, (struct sockaddr *) &addr, &socklen);
+       if (td->files[0].fd < 0) {
+               td_vmsg(td, errno, "accept");
+               return 1;
+       }
+
+       return 0;
+}
+
+static int fio_netio_setup(struct thread_data *td)
+{
+       char host[64], port[64], buf[128];
+       struct net_data *nd;
+       char *sep;
+       int ret;
+
+       /*
+        * work around for late init call
+        */
+       if (td->io_ops->init(td))
+               return 1;
+
+       nd = td->io_ops->data;
+
+       if (td->iomix) {
+               log_err("fio: network connections must be read OR write\n");
+               return 1;
+       }
+       if (td->nr_files > 1) {
+               log_err("fio: only one file supported for network\n");
+               return 1;
+       }
+
+       strcpy(buf, td->filename);
+
+       sep = strchr(buf, ':');
+       if (!sep) {
+               log_err("fio: bad network host:port <<%s>>\n", td->filename);
+               return 1;
+       }
+
+       *sep = '\0';
+       sep++;
+       strcpy(host, buf);
+       strcpy(port, sep);
+
+       if (td->ddir == READ) {
+               nd->send_to_net = 0;
+               ret = fio_netio_setup_listen(td, port);
+       } else {
+               nd->send_to_net = 1;
+               ret = fio_netio_setup_connect(td, host, port);
+       }
+
+       if (!ret) {
+               td->io_size = td->total_file_size;
+               td->total_io_size = td->io_size;
+               td->files[0].real_file_size = td->io_size;
+       }
+
+       return ret;
+}
+
+static void fio_netio_cleanup(struct thread_data *td)
+{
+       if (td->io_ops->data) {
+               free(td->io_ops->data);
+               td->io_ops->data = NULL;
+       }
+}
+
+static int fio_netio_init(struct thread_data *td)
+{
+       struct net_data *nd;
+
+       if (td->io_ops->data)
+               return 0;
+
+       nd  = malloc(sizeof(*nd));
+       nd->last_io_u = NULL;
+       td->io_ops->data = nd;
+       return 0;
+}
+
+static struct ioengine_ops ioengine = {
+       .name           = "net",
+       .version        = FIO_IOOPS_VERSION,
+       .init           = fio_netio_init,
+       .prep           = fio_netio_prep,
+       .queue          = fio_netio_queue,
+       .getevents      = fio_netio_getevents,
+       .event          = fio_netio_event,
+       .cleanup        = fio_netio_cleanup,
+       .setup          = fio_netio_setup,
+       .flags          = FIO_SYNCIO | FIO_NETIO,
+};
+
+static void fio_init fio_netio_register(void)
+{
+       register_ioengine(&ioengine);
+}
+
+static void fio_exit fio_netio_unregister(void)
+{
+       unregister_ioengine(&ioengine);
+}
index 2d8193e..610981b 100644 (file)
@@ -330,6 +330,8 @@ static int setup_file(struct thread_data *td, struct fio_file *f)
 {
        int flags = 0;
 
+       if (td->io_ops->flags & FIO_NETIO)
+               return 0;
        if (td->odirect)
                flags |= OS_O_DIRECT;
        if (td->sync_io)
diff --git a/fio.c b/fio.c
index cc54c83..bce13a7 100644 (file)
--- a/fio.c
+++ b/fio.c
@@ -841,7 +841,6 @@ static void run_threads(void)
 
        signal(SIGINT, sig_handler);
        signal(SIGALRM, sig_handler);
-       signal(SIGSEGV, sig_handler);
 
        todo = thread_number;
        nr_running = 0;
diff --git a/fio.h b/fio.h
index c6fb6a1..d635e99 100644 (file)
--- a/fio.h
+++ b/fio.h
@@ -143,6 +143,7 @@ enum fio_ioengine_flags {
        FIO_CPUIO       = 1 << 1,
        FIO_MMAPIO      = 1 << 2,
        FIO_RAWIO       = 1 << 3,
+       FIO_NETIO       = 1 << 4,
 };
 
 struct fio_file {
diff --git a/init.c b/init.c
index 32e22db..7f2747a 100644 (file)
--- a/init.c
+++ b/init.c
@@ -81,7 +81,7 @@ static struct fio_option options[] = {
                .help   = "IO engine to use",
                .def    = "sync",
                .posval = { "sync", "libaio", "posixaio", "mmap", "splice",
-                               "sg", "null", },
+                               "sg", "null", "net", },
        },
        {
                .name   = "iodepth",
diff --git a/parse.c b/parse.c
index 29e2ff1..cab6ca7 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -312,7 +312,7 @@ static int handle_option(struct fio_option *o, const char *ptr, void *data)
         * Do this before parsing the first round, to check if we should
         * copy set 1 options to set 2.
         */
-       if (ptr) {
+       if (ptr && (o->type != FIO_OPT_STR_STORE)) {
                ptr2 = strchr(ptr, ',');
                if (!ptr2)
                        ptr2 = strchr(ptr, ':');
diff --git a/stat.c b/stat.c
index 8ea1b8a..a98539f 100644 (file)
--- a/stat.c
+++ b/stat.c
@@ -205,7 +205,7 @@ void init_disk_util(struct thread_data *td)
        dev_t dev;
        char *p;
 
-       if (!td->do_disk_util)
+       if (!td->do_disk_util || (td->io_ops->flags & FIO_NETIO))
                return;
 
        /*