X-Git-Url: https://git.kernel.dk/?p=fio.git;a=blobdiff_plain;f=engines%2Fnet.c;h=0c90e1cac74f77d80dbdae4280c691bda06ec864;hp=b728df1f7385f751d86ed63e526be0c71782745a;hb=954cd73a9a93102c24afa869fbe67ac38af6e416;hpb=70a7878c00e130affc3e0bd7d59bd83d57c3268e diff --git a/engines/net.c b/engines/net.c index b728df1f..0c90e1ca 100644 --- a/engines/net.c +++ b/engines/net.c @@ -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 { @@ -58,20 +60,27 @@ static int str_hostname_cb(void *data, const char *input); static struct fio_option options[] = { { .name = "hostname", + .lname = "net engine hostname", .type = FIO_OPT_STR_STORE, .cb = str_hostname_cb, .help = "Hostname for net IO engine", + .category = FIO_OPT_C_ENGINE, + .group = FIO_OPT_G_NETIO, }, { .name = "port", + .lname = "net engine port", .type = FIO_OPT_INT, .off1 = offsetof(struct netio_options, port), .minval = 1, .maxval = 65535, .help = "Port to use for TCP or UDP net connections", + .category = FIO_OPT_C_ENGINE, + .group = FIO_OPT_G_NETIO, }, { .name = "protocol", + .lname = "net engine protocol", .alias = "proto", .type = FIO_OPT_STR, .off1 = offsetof(struct netio_options, proto), @@ -91,24 +100,55 @@ static struct fio_option options[] = { .help = "UNIX domain socket", }, }, + .category = FIO_OPT_C_ENGINE, + .group = FIO_OPT_G_NETIO, }, +#ifdef CONFIG_TCP_NODELAY { .name = "nodelay", .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 { .name = "listen", + .lname = "net engine listen", .type = FIO_OPT_STR_SET, .off1 = offsetof(struct netio_options, listen), .help = "Listen for incoming TCP connections", + .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, @@ -146,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; @@ -360,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; @@ -456,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; @@ -479,17 +542,41 @@ static int fio_netio_connect(struct thread_data *td, struct fio_file *f) return 1; } +#ifdef CONFIG_TCP_NODELAY if (o->nodelay && o->proto == FIO_TYPE_TCP) { - optval = 1; - if (setsockopt(f->fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(int)) < 0) { + 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; } } +#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) { @@ -518,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; @@ -539,13 +626,16 @@ static int fio_netio_accept(struct thread_data *td, struct fio_file *f) goto err; } +#ifdef CONFIG_TCP_NODELAY if (o->nodelay && o->proto == FIO_TYPE_TCP) { - optval = 1; - if (setsockopt(f->fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(int)) < 0) { + 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; } } +#endif reset_all_stats(td); td_set_runstate(td, state); @@ -595,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; } @@ -753,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 @@ -767,19 +860,48 @@ 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) { + 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, &opt, sizeof(opt)) < 0) { + 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) { @@ -856,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 @@ -962,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)