From: Jens Axboe Date: Tue, 6 Feb 2007 13:43:52 +0000 (+0100) Subject: [PATCH] Simple support for networked IO X-Git-Tag: fio-1.12~136 X-Git-Url: https://git.kernel.dk/?p=fio.git;a=commitdiff_plain;h=ed92ac0ce9ce1cc64697272d307d4fa7d18ed64c;ds=sidebyside [PATCH] Simple support for networked IO 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 --- diff --git a/HOWTO b/HOWTO index f4ebac94..38f5dcea 100644 --- 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 diff --git a/Makefile b/Makefile index 3b919d55..fe9a8cd3 100644 --- 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 diff --git a/Makefile.FreeBSD b/Makefile.FreeBSD index b8628205..5c85a0c3 100644 --- a/Makefile.FreeBSD +++ b/Makefile.FreeBSD @@ -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/Makefile.solaris b/Makefile.solaris index 8d8bec3d..4373abfb 100644 --- a/Makefile.solaris +++ b/Makefile.solaris @@ -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 be32af4e..f7d74577 100644 --- 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 index 00000000..8ef78110 --- /dev/null +++ b/engines/net.c @@ -0,0 +1,273 @@ +/* + * Transfer data over the net. Pretty basic setup, will only support + * 1 file per thread/job. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/filesetup.c b/filesetup.c index 2d8193e4..610981b4 100644 --- a/filesetup.c +++ b/filesetup.c @@ -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 cc54c837..bce13a72 100644 --- 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 c6fb6a11..d635e99d 100644 --- 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 32e22dbe..7f2747a4 100644 --- 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 29e2ff1c..cab6ca72 100644 --- 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 8ea1b8ad..a98539fc 100644 --- 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; /*