Update for RDMA io engine's compatibility
[fio.git] / engines / net.c
index 12f49a2a67ca2f1d7241b42efdc45543b77fd868..0c90e1cac74f77d80dbdae4280c691bda06ec864 100644 (file)
@@ -37,6 +37,8 @@ struct netio_options {
        unsigned int listen;
        unsigned int pingpong;
        unsigned int nodelay;
+       unsigned int ttl;
+       char * interface;
 };
 
 struct udp_close_msg {
@@ -62,7 +64,8 @@ static struct fio_option options[] = {
                .type   = FIO_OPT_STR_STORE,
                .cb     = str_hostname_cb,
                .help   = "Hostname for net IO engine",
-               .category = FIO_OPT_C_IO,
+               .category = FIO_OPT_C_ENGINE,
+               .group  = FIO_OPT_G_NETIO,
        },
        {
                .name   = "port",
@@ -72,7 +75,8 @@ static struct fio_option options[] = {
                .minval = 1,
                .maxval = 65535,
                .help   = "Port to use for TCP or UDP net connections",
-               .category = FIO_OPT_C_IO,
+               .category = FIO_OPT_C_ENGINE,
+               .group  = FIO_OPT_G_NETIO,
        },
        {
                .name   = "protocol",
@@ -82,7 +86,6 @@ static struct fio_option options[] = {
                .off1   = offsetof(struct netio_options, proto),
                .help   = "Network protocol to use",
                .def    = "tcp",
-               .category = FIO_OPT_C_IO,
                .posval = {
                          { .ival = "tcp",
                            .oval = FIO_TYPE_TCP,
@@ -97,6 +100,8 @@ static struct fio_option options[] = {
                            .help = "UNIX domain socket",
                          },
                },
+               .category = FIO_OPT_C_ENGINE,
+               .group  = FIO_OPT_G_NETIO,
        },
 #ifdef CONFIG_TCP_NODELAY
        {
@@ -104,6 +109,8 @@ static struct fio_option options[] = {
                .type   = FIO_OPT_BOOL,
                .off1   = offsetof(struct netio_options, nodelay),
                .help   = "Use TCP_NODELAY on TCP connections",
+               .category = FIO_OPT_C_ENGINE,
+               .group  = FIO_OPT_G_NETIO,
        },
 #endif
        {
@@ -112,13 +119,36 @@ static struct fio_option options[] = {
                .type   = FIO_OPT_STR_SET,
                .off1   = offsetof(struct netio_options, listen),
                .help   = "Listen for incoming TCP connections",
-               .category = FIO_OPT_C_IO,
+               .category = FIO_OPT_C_ENGINE,
+               .group  = FIO_OPT_G_NETIO,
        },
        {
                .name   = "pingpong",
                .type   = FIO_OPT_STR_SET,
                .off1   = offsetof(struct netio_options, pingpong),
                .help   = "Ping-pong IO requests",
+               .category = FIO_OPT_C_ENGINE,
+               .group  = FIO_OPT_G_NETIO,
+       },
+       {
+               .name   = "interface",
+               .lname  = "net engine interface",
+               .type   = FIO_OPT_STR_STORE,
+               .off1   = offsetof(struct netio_options, interface),
+               .help   = "Network interface to use",
+               .category = FIO_OPT_C_ENGINE,
+               .group  = FIO_OPT_G_NETIO,
+       },
+       {
+               .name   = "ttl",
+               .lname  = "net engine multicast ttl",
+               .type   = FIO_OPT_INT,
+               .off1   = offsetof(struct netio_options, ttl),
+               .def    = "1",
+               .minval = 0,
+               .help   = "Time-to-live value for outgoing UDP multicast packets",
+               .category = FIO_OPT_C_ENGINE,
+               .group  = FIO_OPT_G_NETIO,
        },
        {
                .name   = NULL,
@@ -156,6 +186,20 @@ static int poll_wait(struct thread_data *td, int fd, short events)
        return -1;
 }
 
+static int fio_netio_is_multicast(const char *mcaddr)
+{
+       in_addr_t addr = inet_network(mcaddr);
+       if (addr == -1)
+               return 0;
+
+       if (inet_network("224.0.0.0") <= addr &&
+           inet_network("239.255.255.255") >= addr)
+               return 1;
+
+       return 0;
+}
+
+
 static int fio_netio_prep(struct thread_data *td, struct io_u *io_u)
 {
        struct netio_options *o = td->eo;
@@ -370,11 +414,20 @@ static int fio_netio_recv(struct thread_data *td, struct io_u *io_u)
 
        do {
                if (o->proto == FIO_TYPE_UDP) {
-                       socklen_t len = sizeof(nd->addr);
-                       struct sockaddr *from = (struct sockaddr *) &nd->addr;
+                       socklen_t l;
+                       socklen_t *len = &l;
+                       struct sockaddr *from;
+
+                       if (o->listen) {
+                               from = (struct sockaddr *) &nd->addr;
+                               *len = sizeof(nd->addr);
+                       } else {
+                               from = NULL;
+                               len = NULL;
+                       }
 
                        ret = recvfrom(io_u->file->fd, io_u->xfer_buf,
-                                       io_u->xfer_buflen, flags, from, &len);
+                                       io_u->xfer_buflen, flags, from, len);
                        if (is_udp_close(io_u, ret)) {
                                td->done = 1;
                                return 0;
@@ -466,7 +519,7 @@ static int fio_netio_connect(struct thread_data *td, struct fio_file *f)
 {
        struct netio_data *nd = td->io_ops->data;
        struct netio_options *o = td->eo;
-       int type, domain, optval;
+       int type, domain;
 
        if (o->proto == FIO_TYPE_TCP) {
                domain = AF_INET;
@@ -491,7 +544,8 @@ static int fio_netio_connect(struct thread_data *td, struct fio_file *f)
 
 #ifdef CONFIG_TCP_NODELAY
        if (o->nodelay && o->proto == FIO_TYPE_TCP) {
-               optval = 1;
+               int optval = 1;
+
                if (setsockopt(f->fd, IPPROTO_TCP, TCP_NODELAY, (void *) &optval, sizeof(int)) < 0) {
                        log_err("fio: cannot set TCP_NODELAY option on socket (%s), disable with 'nodelay=0'\n", strerror(errno));
                        return 1;
@@ -499,9 +553,30 @@ static int fio_netio_connect(struct thread_data *td, struct fio_file *f)
        }
 #endif
 
-       if (o->proto == FIO_TYPE_UDP)
+       if (o->proto == FIO_TYPE_UDP) {
+               if (!fio_netio_is_multicast(td->o.filename))
+                       return 0;
+
+               if (o->interface) {
+                       struct in_addr interface_addr;
+                       if (inet_aton(o->interface, &interface_addr) == 0) {
+                               log_err("fio: interface not valid interface IP\n");
+                               close(f->fd);
+                               return 1;
+                       }
+                       if (setsockopt(f->fd, IPPROTO_IP, IP_MULTICAST_IF, &interface_addr, sizeof(interface_addr)) < 0) {
+                               td_verror(td, errno, "setsockopt IP_MULTICAST_IF");
+                               close(f->fd);
+                               return 1;
+                       }
+               }
+               if (setsockopt(f->fd, IPPROTO_IP, IP_MULTICAST_TTL, &o->ttl, sizeof(o->ttl)) < 0) {
+                       td_verror(td, errno, "setsockopt IP_MULTICAST_TTL");
+                       close(f->fd);
+                       return 1;
+               }
                return 0;
-       else if (o->proto == FIO_TYPE_TCP) {
+       else if (o->proto == FIO_TYPE_TCP) {
                socklen_t len = sizeof(nd->addr);
 
                if (connect(f->fd, (struct sockaddr *) &nd->addr, len) < 0) {
@@ -530,7 +605,7 @@ static int fio_netio_accept(struct thread_data *td, struct fio_file *f)
        struct netio_data *nd = td->io_ops->data;
        struct netio_options *o = td->eo;
        socklen_t socklen = sizeof(nd->addr);
-       int state, optval;
+       int state;
 
        if (o->proto == FIO_TYPE_UDP) {
                f->fd = nd->listenfd;
@@ -553,7 +628,8 @@ static int fio_netio_accept(struct thread_data *td, struct fio_file *f)
 
 #ifdef CONFIG_TCP_NODELAY
        if (o->nodelay && o->proto == FIO_TYPE_TCP) {
-               optval = 1;
+               int optval = 1;
+
                if (setsockopt(f->fd, IPPROTO_TCP, TCP_NODELAY, (void *) &optval, sizeof(int)) < 0) {
                        log_err("fio: cannot set TCP_NODELAY option on socket (%s), disable with 'nodelay=0'\n", strerror(errno));
                        return 1;
@@ -609,7 +685,7 @@ static int fio_netio_udp_recv_open(struct thread_data *td, struct fio_file *f)
 
        ret = recvfrom(f->fd, (void *) &msg, sizeof(msg), MSG_WAITALL, to, &len);
        if (ret < 0) {
-               td_verror(td, errno, "sendto udp link open");
+               td_verror(td, errno, "recvfrom udp link open");
                return ret;
        }
 
@@ -767,8 +843,11 @@ static int fio_netio_setup_listen_inet(struct thread_data *td, short port)
 {
        struct netio_data *nd = td->io_ops->data;
        struct netio_options *o = td->eo;
+       struct ip_mreq mr;
+       struct sockaddr_in sin;
        int fd, opt, type;
 
+       memset(&sin, 0, sizeof(sin));
        if (o->proto == FIO_TYPE_TCP)
                type = SOCK_STREAM;
        else
@@ -783,17 +862,46 @@ static int fio_netio_setup_listen_inet(struct thread_data *td, short port)
        opt = 1;
        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &opt, sizeof(opt)) < 0) {
                td_verror(td, errno, "setsockopt");
+               close(fd);
                return 1;
        }
 #ifdef SO_REUSEPORT
        if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (void *) &opt, sizeof(opt)) < 0) {
                td_verror(td, errno, "setsockopt");
+               close(fd);
                return 1;
        }
 #endif
 
+       if (td->o.filename){
+               if(o->proto != FIO_TYPE_UDP ||
+                  !fio_netio_is_multicast(td->o.filename)) {
+                       log_err("fio: hostname not valid for non-multicast inbound network IO\n");
+                       close(fd);
+                       return 1;
+               }
+
+               inet_aton(td->o.filename, &sin.sin_addr);
+
+               mr.imr_multiaddr = sin.sin_addr;
+               if (o->interface) {
+                       if (inet_aton(o->interface, &mr.imr_interface) == 0) {
+                               log_err("fio: interface not valid interface IP\n");
+                               close(fd);
+                               return 1;
+                       }
+               } else {
+                       mr.imr_interface.s_addr = htonl(INADDR_ANY);
+               }
+               if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) {
+                       td_verror(td, errno, "setsockopt IP_ADD_MEMBERSHIP");
+                       close(fd);
+                       return 1;
+               }
+       }
+
        nd->addr.sin_family = AF_INET;
-       nd->addr.sin_addr.s_addr = htonl(INADDR_ANY);
+       nd->addr.sin_addr.s_addr = sin.sin_addr.s_addr ? sin.sin_addr.s_addr : htonl(INADDR_ANY);
        nd->addr.sin_port = htons(port);
 
        if (bind(fd, (struct sockaddr *) &nd->addr, sizeof(nd->addr)) < 0) {
@@ -870,11 +978,6 @@ static int fio_netio_init(struct thread_data *td)
                o->listen = td_read(td);
        }
 
-       if (o->proto != FIO_TYPE_UNIX && o->listen && td->o.filename) {
-               log_err("fio: hostname not valid for inbound network IO\n");
-               return 1;
-       }
-
        if (o->listen)
                ret = fio_netio_setup_listen(td);
        else
@@ -976,7 +1079,7 @@ static struct ioengine_ops ioengine_rw = {
        .options                = options,
        .option_struct_size     = sizeof(struct netio_options),
        .flags                  = FIO_SYNCIO | FIO_DISKLESSIO | FIO_UNIDIR |
-                                 FIO_PIPEIO,
+                                 FIO_PIPEIO | FIO_BIT_BASED,
 };
 
 static int str_hostname_cb(void *data, const char *input)