diff options
Diffstat (limited to 'net/tap.c')
-rw-r--r-- | net/tap.c | 346 |
1 files changed, 242 insertions, 104 deletions
@@ -23,7 +23,7 @@ * THE SOFTWARE. */ -#include "tap.h" +#include "tap_int.h" #include "config-host.h" @@ -33,15 +33,14 @@ #include <sys/socket.h> #include <net/if.h> -#include "net.h" +#include "net/net.h" #include "clients.h" -#include "monitor.h" -#include "sysemu.h" -#include "qemu-char.h" +#include "monitor/monitor.h" +#include "sysemu/sysemu.h" #include "qemu-common.h" -#include "qemu-error.h" +#include "qemu/error-report.h" -#include "net/tap-linux.h" +#include "net/tap.h" #include "hw/vhost_net.h" @@ -56,10 +55,11 @@ typedef struct TAPState { char down_script[1024]; char down_script_arg[128]; uint8_t buf[TAP_BUFSIZE]; - unsigned int read_poll : 1; - unsigned int write_poll : 1; - unsigned int using_vnet_hdr : 1; - unsigned int has_ufo: 1; + bool read_poll; + bool write_poll; + bool using_vnet_hdr; + bool has_ufo; + bool enabled; VHostNetState *vhost_net; unsigned host_vnet_hdr_len; } TAPState; @@ -73,21 +73,21 @@ static void tap_writable(void *opaque); static void tap_update_fd_handler(TAPState *s) { qemu_set_fd_handler2(s->fd, - s->read_poll ? tap_can_send : NULL, - s->read_poll ? tap_send : NULL, - s->write_poll ? tap_writable : NULL, + s->read_poll && s->enabled ? tap_can_send : NULL, + s->read_poll && s->enabled ? tap_send : NULL, + s->write_poll && s->enabled ? tap_writable : NULL, s); } -static void tap_read_poll(TAPState *s, int enable) +static void tap_read_poll(TAPState *s, bool enable) { - s->read_poll = !!enable; + s->read_poll = enable; tap_update_fd_handler(s); } -static void tap_write_poll(TAPState *s, int enable) +static void tap_write_poll(TAPState *s, bool enable) { - s->write_poll = !!enable; + s->write_poll = enable; tap_update_fd_handler(s); } @@ -95,7 +95,7 @@ static void tap_writable(void *opaque) { TAPState *s = opaque; - tap_write_poll(s, 0); + tap_write_poll(s, false); qemu_flush_queued_packets(&s->nc); } @@ -109,7 +109,7 @@ static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt } while (len == -1 && errno == EINTR); if (len == -1 && errno == EAGAIN) { - tap_write_poll(s, 1); + tap_write_poll(s, true); return 0; } @@ -187,7 +187,7 @@ ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen) static void tap_send_completed(NetClientState *nc, ssize_t len) { TAPState *s = DO_UPCAST(TAPState, nc, nc); - tap_read_poll(s, 1); + tap_read_poll(s, true); } static void tap_send(void *opaque) @@ -210,12 +210,12 @@ static void tap_send(void *opaque) size = qemu_send_packet_async(&s->nc, buf, size, tap_send_completed); if (size == 0) { - tap_read_poll(s, 0); + tap_read_poll(s, false); } } while (size > 0 && qemu_can_send_packet(&s->nc)); } -int tap_has_ufo(NetClientState *nc) +bool tap_has_ufo(NetClientState *nc) { TAPState *s = DO_UPCAST(TAPState, nc, nc); @@ -254,12 +254,10 @@ void tap_set_vnet_hdr_len(NetClientState *nc, int len) s->host_vnet_hdr_len = len; } -void tap_using_vnet_hdr(NetClientState *nc, int using_vnet_hdr) +void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr) { TAPState *s = DO_UPCAST(TAPState, nc, nc); - using_vnet_hdr = using_vnet_hdr != 0; - assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP); assert(!!s->host_vnet_hdr_len == using_vnet_hdr); @@ -291,8 +289,8 @@ static void tap_cleanup(NetClientState *nc) if (s->down_script[0]) launch_script(s->down_script, s->down_script_arg, s->fd); - tap_read_poll(s, 0); - tap_write_poll(s, 0); + tap_read_poll(s, false); + tap_write_poll(s, false); close(s->fd); s->fd = -1; } @@ -338,8 +336,9 @@ static TAPState *net_tap_fd_init(NetClientState *peer, s->fd = fd; s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0; - s->using_vnet_hdr = 0; + s->using_vnet_hdr = false; s->has_ufo = tap_probe_has_ufo(s->fd); + s->enabled = true; tap_set_offload(&s->nc, 0, 0, 0, 0, 0); /* * Make sure host header length is set correctly in tap: @@ -348,7 +347,7 @@ static TAPState *net_tap_fd_init(NetClientState *peer, if (tap_probe_vnet_hdr_len(s->fd, s->host_vnet_hdr_len)) { tap_fd_set_vnet_hdr_len(s->fd, s->host_vnet_hdr_len); } - tap_read_poll(s, 1); + tap_read_poll(s, true); s->vhost_net = NULL; return s; } @@ -559,17 +558,10 @@ int net_init_bridge(const NetClientOptions *opts, const char *name, static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr, const char *setup_script, char *ifname, - size_t ifname_sz) + size_t ifname_sz, int mq_required) { int fd, vnet_hdr_required; - if (tap->has_ifname) { - pstrcpy(ifname, ifname_sz, tap->ifname); - } else { - assert(ifname_sz > 0); - ifname[0] = '\0'; - } - if (tap->has_vnet_hdr) { *vnet_hdr = tap->vnet_hdr; vnet_hdr_required = *vnet_hdr; @@ -578,7 +570,8 @@ static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr, vnet_hdr_required = 0; } - TFR(fd = tap_open(ifname, ifname_sz, vnet_hdr, vnet_hdr_required)); + TFR(fd = tap_open(ifname, ifname_sz, vnet_hdr, vnet_hdr_required, + mq_required)); if (fd < 0) { return -1; } @@ -594,27 +587,118 @@ static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr, return fd; } +#define MAX_TAP_QUEUES 1024 + +static int net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, + const char *model, const char *name, + const char *ifname, const char *script, + const char *downscript, const char *vhostfdname, + int vnet_hdr, int fd) +{ + TAPState *s; + + s = net_tap_fd_init(peer, model, name, fd, vnet_hdr); + if (!s) { + close(fd); + return -1; + } + + if (tap_set_sndbuf(s->fd, tap) < 0) { + return -1; + } + + if (tap->has_fd || tap->has_fds) { + snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd); + } else if (tap->has_helper) { + snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s", + tap->helper); + } else { + snprintf(s->nc.info_str, sizeof(s->nc.info_str), + "ifname=%s,script=%s,downscript=%s", ifname, script, + downscript); + + if (strcmp(downscript, "no") != 0) { + snprintf(s->down_script, sizeof(s->down_script), "%s", downscript); + snprintf(s->down_script_arg, sizeof(s->down_script_arg), + "%s", ifname); + } + } + + if (tap->has_vhost ? tap->vhost : + vhostfdname || (tap->has_vhostforce && tap->vhostforce)) { + int vhostfd; + + if (tap->has_vhostfd) { + vhostfd = monitor_handle_fd_param(cur_mon, vhostfdname); + if (vhostfd == -1) { + return -1; + } + } else { + vhostfd = -1; + } + + s->vhost_net = vhost_net_init(&s->nc, vhostfd, + tap->has_vhostforce && tap->vhostforce); + if (!s->vhost_net) { + error_report("vhost-net requested but could not be initialized"); + return -1; + } + } else if (tap->has_vhostfd || tap->has_vhostfds) { + error_report("vhostfd= is not valid without vhost"); + return -1; + } + + return 0; +} + +static int get_fds(char *str, char *fds[], int max) +{ + char *ptr = str, *this; + size_t len = strlen(str); + int i = 0; + + while (i < max && ptr < str + len) { + this = strchr(ptr, ':'); + + if (this == NULL) { + fds[i] = g_strdup(ptr); + } else { + fds[i] = g_strndup(ptr, this - ptr); + } + + i++; + if (this == NULL) { + break; + } else { + ptr = this + 1; + } + } + + return i; +} + int net_init_tap(const NetClientOptions *opts, const char *name, NetClientState *peer) { const NetdevTapOptions *tap; - - int fd, vnet_hdr = 0; - const char *model; - TAPState *s; - + int fd, vnet_hdr = 0, i = 0, queues; /* for the no-fd, no-helper case */ const char *script = NULL; /* suppress wrong "uninit'd use" gcc warning */ + const char *downscript = NULL; + const char *vhostfdname; char ifname[128]; assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP); tap = opts->tap; + queues = tap->has_queues ? tap->queues : 1; + vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL; if (tap->has_fd) { if (tap->has_ifname || tap->has_script || tap->has_downscript || - tap->has_vnet_hdr || tap->has_helper) { + tap->has_vnet_hdr || tap->has_helper || tap->has_queues || + tap->has_fds) { error_report("ifname=, script=, downscript=, vnet_hdr=, " - "and helper= are invalid with fd="); + "helper=, queues=, and fds= are invalid with fd="); return -1; } @@ -627,13 +711,61 @@ int net_init_tap(const NetClientOptions *opts, const char *name, vnet_hdr = tap_probe_vnet_hdr(fd); - model = "tap"; + if (net_init_tap_one(tap, peer, "tap", name, NULL, + script, downscript, + vhostfdname, vnet_hdr, fd)) { + return -1; + } + } else if (tap->has_fds) { + char *fds[MAX_TAP_QUEUES]; + char *vhost_fds[MAX_TAP_QUEUES]; + int nfds, nvhosts; + + if (tap->has_ifname || tap->has_script || tap->has_downscript || + tap->has_vnet_hdr || tap->has_helper || tap->has_queues || + tap->has_fd) { + error_report("ifname=, script=, downscript=, vnet_hdr=, " + "helper=, queues=, and fd= are invalid with fds="); + return -1; + } + nfds = get_fds(tap->fds, fds, MAX_TAP_QUEUES); + if (tap->has_vhostfds) { + nvhosts = get_fds(tap->vhostfds, vhost_fds, MAX_TAP_QUEUES); + if (nfds != nvhosts) { + error_report("The number of fds passed does not match the " + "number of vhostfds passed"); + return -1; + } + } + + for (i = 0; i < nfds; i++) { + fd = monitor_handle_fd_param(cur_mon, fds[i]); + if (fd == -1) { + return -1; + } + + fcntl(fd, F_SETFL, O_NONBLOCK); + + if (i == 0) { + vnet_hdr = tap_probe_vnet_hdr(fd); + } else if (vnet_hdr != tap_probe_vnet_hdr(fd)) { + error_report("vnet_hdr not consistent across given tap fds"); + return -1; + } + + if (net_init_tap_one(tap, peer, "tap", name, ifname, + script, downscript, + tap->has_vhostfds ? vhost_fds[i] : NULL, + vnet_hdr, fd)) { + return -1; + } + } } else if (tap->has_helper) { if (tap->has_ifname || tap->has_script || tap->has_downscript || - tap->has_vnet_hdr) { + tap->has_vnet_hdr || tap->has_queues || tap->has_fds) { error_report("ifname=, script=, downscript=, and vnet_hdr= " - "are invalid with helper="); + "queues=, and fds= are invalid with helper="); return -1; } @@ -643,74 +775,45 @@ int net_init_tap(const NetClientOptions *opts, const char *name, } fcntl(fd, F_SETFL, O_NONBLOCK); - vnet_hdr = tap_probe_vnet_hdr(fd); - model = "bridge"; - - } else { - script = tap->has_script ? tap->script : DEFAULT_NETWORK_SCRIPT; - fd = net_tap_init(tap, &vnet_hdr, script, ifname, sizeof ifname); - if (fd == -1) { + if (net_init_tap_one(tap, peer, "bridge", name, ifname, + script, downscript, vhostfdname, + vnet_hdr, fd)) { return -1; } - - model = "tap"; - } - - s = net_tap_fd_init(peer, model, name, fd, vnet_hdr); - if (!s) { - close(fd); - return -1; - } - - if (tap_set_sndbuf(s->fd, tap) < 0) { - return -1; - } - - if (tap->has_fd) { - snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd); - } else if (tap->has_helper) { - snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s", - tap->helper); } else { - const char *downscript; - + script = tap->has_script ? tap->script : DEFAULT_NETWORK_SCRIPT; downscript = tap->has_downscript ? tap->downscript : - DEFAULT_NETWORK_DOWN_SCRIPT; + DEFAULT_NETWORK_DOWN_SCRIPT; - snprintf(s->nc.info_str, sizeof(s->nc.info_str), - "ifname=%s,script=%s,downscript=%s", ifname, script, - downscript); - - if (strcmp(downscript, "no") != 0) { - snprintf(s->down_script, sizeof(s->down_script), "%s", downscript); - snprintf(s->down_script_arg, sizeof(s->down_script_arg), "%s", ifname); + if (tap->has_ifname) { + pstrcpy(ifname, sizeof ifname, tap->ifname); + } else { + ifname[0] = '\0'; } - } - - if (tap->has_vhost ? tap->vhost : - tap->has_vhostfd || (tap->has_vhostforce && tap->vhostforce)) { - int vhostfd; - if (tap->has_vhostfd) { - vhostfd = monitor_handle_fd_param(cur_mon, tap->vhostfd); - if (vhostfd == -1) { + for (i = 0; i < queues; i++) { + fd = net_tap_init(tap, &vnet_hdr, i >= 1 ? "no" : script, + ifname, sizeof ifname, queues > 1); + if (fd == -1) { return -1; } - } else { - vhostfd = -1; - } - s->vhost_net = vhost_net_init(&s->nc, vhostfd, - tap->has_vhostforce && tap->vhostforce); - if (!s->vhost_net) { - error_report("vhost-net requested but could not be initialized"); - return -1; + if (queues > 1 && i == 0 && !tap->has_ifname) { + if (tap_fd_get_ifname(fd, ifname)) { + error_report("Fail to get ifname"); + return -1; + } + } + + if (net_init_tap_one(tap, peer, "tap", name, ifname, + i >= 1 ? "no" : script, + i >= 1 ? "no" : downscript, + vhostfdname, vnet_hdr, fd)) { + return -1; + } } - } else if (tap->has_vhostfd) { - error_report("vhostfd= is not valid without vhost"); - return -1; } return 0; @@ -722,3 +825,38 @@ VHostNetState *tap_get_vhost_net(NetClientState *nc) assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP); return s->vhost_net; } + +int tap_enable(NetClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + int ret; + + if (s->enabled) { + return 0; + } else { + ret = tap_fd_enable(s->fd); + if (ret == 0) { + s->enabled = true; + tap_update_fd_handler(s); + } + return ret; + } +} + +int tap_disable(NetClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + int ret; + + if (s->enabled == 0) { + return 0; + } else { + ret = tap_fd_disable(s->fd); + if (ret == 0) { + qemu_purge_queued_packets(nc); + s->enabled = false; + tap_update_fd_handler(s); + } + return ret; + } +} |