X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/9fbee91a131a05e443d7108d7fbdf3ca91020290..da34e65cb4025728566d6504a99916f6e7e1dd6a:/net/tap.c diff --git a/net/tap.c b/net/tap.c index 2d5099b9be..3ddf9ecd7d 100644 --- a/net/tap.c +++ b/net/tap.c @@ -23,12 +23,11 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "tap_int.h" -#include "config-host.h" #include -#include #include #include #include @@ -37,6 +36,7 @@ #include "clients.h" #include "monitor/monitor.h" #include "sysemu/sysemu.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/error-report.h" @@ -59,19 +59,18 @@ typedef struct TAPState { unsigned host_vnet_hdr_len; } TAPState; -static int launch_script(const char *setup_script, const char *ifname, int fd); +static void launch_script(const char *setup_script, const char *ifname, + int fd, Error **errp); -static int tap_can_send(void *opaque); static void tap_send(void *opaque); static void tap_writable(void *opaque); static void tap_update_fd_handler(TAPState *s) { - qemu_set_fd_handler2(s->fd, - 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); + qemu_set_fd_handler(s->fd, + s->read_poll && s->enabled ? tap_send : NULL, + s->write_poll && s->enabled ? tap_writable : NULL, + s); } static void tap_read_poll(TAPState *s, bool enable) @@ -165,13 +164,6 @@ static ssize_t tap_receive(NetClientState *nc, const uint8_t *buf, size_t size) return tap_write_packet(s, iov, 1); } -static int tap_can_send(void *opaque) -{ - TAPState *s = opaque; - - return qemu_can_send_packet(&s->nc); -} - #ifndef __sun__ ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen) { @@ -189,8 +181,9 @@ static void tap_send(void *opaque) { TAPState *s = opaque; int size; + int packets = 0; - do { + while (true) { uint8_t *buf = s->buf; size = tap_read_packet(s->fd, s->buf, sizeof(s->buf)); @@ -206,8 +199,22 @@ 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, false); + break; + } else if (size < 0) { + break; } - } while (size > 0 && qemu_can_send_packet(&s->nc)); + + /* + * When the host keeps receiving more packets while tap_send() is + * running we can hog the QEMU global mutex. Limit the number of + * packets that are processed per tap_send() callback to prevent + * stalling the guest. + */ + packets++; + if (packets >= 50) { + break; + } + } } static bool tap_has_ufo(NetClientState *nc) @@ -259,6 +266,20 @@ static void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr) s->using_vnet_hdr = using_vnet_hdr; } +static int tap_set_vnet_le(NetClientState *nc, bool is_le) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + + return tap_fd_set_vnet_le(s->fd, is_le); +} + +static int tap_set_vnet_be(NetClientState *nc, bool is_be) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + + return tap_fd_set_vnet_be(s->fd, is_be); +} + static void tap_set_offload(NetClientState *nc, int csum, int tso4, int tso6, int ecn, int ufo) { @@ -273,6 +294,7 @@ static void tap_set_offload(NetClientState *nc, int csum, int tso4, static void tap_cleanup(NetClientState *nc) { TAPState *s = DO_UPCAST(TAPState, nc, nc); + Error *err = NULL; if (s->vhost_net) { vhost_net_cleanup(s->vhost_net); @@ -281,8 +303,12 @@ static void tap_cleanup(NetClientState *nc) qemu_purge_queued_packets(nc); - if (s->down_script[0]) - launch_script(s->down_script, s->down_script_arg, s->fd); + if (s->down_script[0]) { + launch_script(s->down_script, s->down_script_arg, s->fd, &err); + if (err) { + error_report_err(err); + } + } tap_read_poll(s, false); tap_write_poll(s, false); @@ -320,6 +346,8 @@ static NetClientInfo net_tap_info = { .using_vnet_hdr = tap_using_vnet_hdr, .set_offload = tap_set_offload, .set_vnet_hdr_len = tap_set_vnet_hdr_len, + .set_vnet_le = tap_set_vnet_le, + .set_vnet_be = tap_set_vnet_be, }; static TAPState *net_tap_fd_init(NetClientState *peer, @@ -353,7 +381,8 @@ static TAPState *net_tap_fd_init(NetClientState *peer, return s; } -static int launch_script(const char *setup_script, const char *ifname, int fd) +static void launch_script(const char *setup_script, const char *ifname, + int fd, Error **errp) { int pid, status; char *args[3]; @@ -361,14 +390,16 @@ static int launch_script(const char *setup_script, const char *ifname, int fd) /* try to launch network script */ pid = fork(); + if (pid < 0) { + error_setg_errno(errp, errno, "could not launch network script %s", + setup_script); + return; + } if (pid == 0) { int open_max = sysconf(_SC_OPEN_MAX), i; - for (i = 0; i < open_max; i++) { - if (i != STDIN_FILENO && - i != STDOUT_FILENO && - i != STDERR_FILENO && - i != fd) { + for (i = 3; i < open_max; i++) { + if (i != fd) { close(i); } } @@ -378,17 +409,17 @@ static int launch_script(const char *setup_script, const char *ifname, int fd) *parg = NULL; execv(setup_script, args); _exit(1); - } else if (pid > 0) { + } else { while (waitpid(pid, &status, 0) != pid) { /* loop */ } if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { - return 0; + return; } + error_setg(errp, "network script %s failed with status %d", + setup_script, status); } - fprintf(stderr, "%s: could not launch network script\n", setup_script); - return -1; } static int recv_fd(int c) @@ -425,7 +456,8 @@ static int recv_fd(int c) return len; } -static int net_bridge_run_helper(const char *helper, const char *bridge) +static int net_bridge_run_helper(const char *helper, const char *bridge, + Error **errp) { sigset_t oldmask, mask; int pid, status; @@ -438,22 +470,24 @@ static int net_bridge_run_helper(const char *helper, const char *bridge) sigprocmask(SIG_BLOCK, &mask, &oldmask); if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { + error_setg_errno(errp, errno, "socketpair() failed"); return -1; } /* try to launch bridge helper */ pid = fork(); + if (pid < 0) { + error_setg_errno(errp, errno, "Can't fork bridge helper"); + return -1; + } if (pid == 0) { int open_max = sysconf(_SC_OPEN_MAX), i; char fd_buf[6+10]; char br_buf[6+IFNAMSIZ] = {0}; char helper_cmd[PATH_MAX + sizeof(fd_buf) + sizeof(br_buf) + 15]; - for (i = 0; i < open_max; i++) { - if (i != STDIN_FILENO && - i != STDOUT_FILENO && - i != STDERR_FILENO && - i != sv[1]) { + for (i = 3; i < open_max; i++) { + if (i != sv[1]) { close(i); } } @@ -493,14 +527,16 @@ static int net_bridge_run_helper(const char *helper, const char *bridge) } _exit(1); - } else if (pid > 0) { + } else { int fd; + int saved_errno; close(sv[1]); do { fd = recv_fd(sv[0]); } while (fd == -1 && errno == EINTR); + saved_errno = errno; close(sv[0]); @@ -509,47 +545,40 @@ static int net_bridge_run_helper(const char *helper, const char *bridge) } sigprocmask(SIG_SETMASK, &oldmask, NULL); if (fd < 0) { - fprintf(stderr, "failed to recv file descriptor\n"); + error_setg_errno(errp, saved_errno, + "failed to recv file descriptor"); return -1; } - - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { - return fd; + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + error_setg(errp, "bridge helper failed"); + return -1; } + return fd; } - fprintf(stderr, "failed to launch bridge helper\n"); - return -1; } int net_init_bridge(const NetClientOptions *opts, const char *name, - NetClientState *peer) + NetClientState *peer, Error **errp) { const NetdevBridgeOptions *bridge; const char *helper, *br; - TAPState *s; int fd, vnet_hdr; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_BRIDGE); - bridge = opts->bridge; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_BRIDGE); + bridge = opts->u.bridge.data; helper = bridge->has_helper ? bridge->helper : DEFAULT_BRIDGE_HELPER; br = bridge->has_br ? bridge->br : DEFAULT_BRIDGE_INTERFACE; - fd = net_bridge_run_helper(helper, br); + fd = net_bridge_run_helper(helper, br, errp); if (fd == -1) { return -1; } fcntl(fd, F_SETFL, O_NONBLOCK); - vnet_hdr = tap_probe_vnet_hdr(fd); - s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr); - if (!s) { - close(fd); - return -1; - } snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s,br=%s", helper, br); @@ -559,8 +588,9 @@ 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, int mq_required) + size_t ifname_sz, int mq_required, Error **errp) { + Error *err = NULL; int fd, vnet_hdr_required; if (tap->has_vnet_hdr) { @@ -572,17 +602,20 @@ static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr, } TFR(fd = tap_open(ifname, ifname_sz, vnet_hdr, vnet_hdr_required, - mq_required)); + mq_required, errp)); if (fd < 0) { return -1; } if (setup_script && setup_script[0] != '\0' && - strcmp(setup_script, "no") != 0 && - launch_script(setup_script, ifname, fd)) { - close(fd); - return -1; + strcmp(setup_script, "no") != 0) { + launch_script(setup_script, ifname, fd, &err); + if (err) { + error_propagate(errp, err); + close(fd); + return -1; + } } return fd; @@ -590,22 +623,20 @@ static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr, #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) +static void 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, Error **errp) { - TAPState *s; + Error *err = NULL; + TAPState *s = net_tap_fd_init(peer, model, name, fd, vnet_hdr); + int vhostfd; - 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; + tap_set_sndbuf(s->fd, tap, &err); + if (err) { + error_propagate(errp, err); + return; } if (tap->has_fd || tap->has_fds) { @@ -627,29 +658,36 @@ static int net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, if (tap->has_vhost ? tap->vhost : vhostfdname || (tap->has_vhostforce && tap->vhostforce)) { - int vhostfd; + VhostNetOptions options; + + options.backend_type = VHOST_BACKEND_TYPE_KERNEL; + options.net_backend = &s->nc; - if (tap->has_vhostfd || tap->has_vhostfds) { - vhostfd = monitor_handle_fd_param(cur_mon, vhostfdname); + if (vhostfdname) { + vhostfd = monitor_fd_param(cur_mon, vhostfdname, &err); if (vhostfd == -1) { - return -1; + error_propagate(errp, err); + return; } } else { - vhostfd = -1; + vhostfd = open("/dev/vhost-net", O_RDWR); + if (vhostfd < 0) { + error_setg_errno(errp, errno, + "tap: open vhost char device failed"); + return; + } } + options.opaque = (void *)(uintptr_t)vhostfd; - s->vhost_net = vhost_net_init(&s->nc, vhostfd, - tap->has_vhostforce && tap->vhostforce); + s->vhost_net = vhost_net_init(&options); if (!s->vhost_net) { - error_report("vhost-net requested but could not be initialized"); - return -1; + error_setg(errp, + "vhost-net requested but could not be initialized"); + return; } - } else if (tap->has_vhostfd || tap->has_vhostfds) { - error_report("vhostfd= is not valid without vhost"); - return -1; + } else if (vhostfdname) { + error_setg(errp, "vhostfd= is not valid without vhost"); } - - return 0; } static int get_fds(char *str, char *fds[], int max) @@ -679,25 +717,26 @@ static int get_fds(char *str, char *fds[], int max) } int net_init_tap(const NetClientOptions *opts, const char *name, - NetClientState *peer) + NetClientState *peer, Error **errp) { const NetdevTapOptions *tap; 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; + Error *err = NULL; const char *vhostfdname; char ifname[128]; - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP); - tap = opts->tap; + assert(opts->type == NET_CLIENT_OPTIONS_KIND_TAP); + tap = opts->u.tap.data; queues = tap->has_queues ? tap->queues : 1; vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL; /* QEMU vlans does not support multiqueue tap, in this case peer is set. * For -netdev, peer is always NULL. */ if (peer && (tap->has_queues || tap->has_fds || tap->has_vhostfds)) { - error_report("Multiqueue tap cannot be used with QEMU vlans"); + error_setg(errp, "Multiqueue tap cannot be used with QEMU vlans"); return -1; } @@ -705,14 +744,15 @@ int net_init_tap(const NetClientOptions *opts, const char *name, if (tap->has_ifname || tap->has_script || tap->has_downscript || tap->has_vnet_hdr || tap->has_helper || tap->has_queues || tap->has_fds || tap->has_vhostfds) { - error_report("ifname=, script=, downscript=, vnet_hdr=, " - "helper=, queues=, fds=, and vhostfds= " - "are invalid with fd="); + error_setg(errp, "ifname=, script=, downscript=, vnet_hdr=, " + "helper=, queues=, fds=, and vhostfds= " + "are invalid with fd="); return -1; } - fd = monitor_handle_fd_param(cur_mon, tap->fd); + fd = monitor_fd_param(cur_mon, tap->fd, &err); if (fd == -1) { + error_propagate(errp, err); return -1; } @@ -720,9 +760,11 @@ int net_init_tap(const NetClientOptions *opts, const char *name, vnet_hdr = tap_probe_vnet_hdr(fd); - if (net_init_tap_one(tap, peer, "tap", name, NULL, - script, downscript, - vhostfdname, vnet_hdr, fd)) { + net_init_tap_one(tap, peer, "tap", name, NULL, + script, downscript, + vhostfdname, vnet_hdr, fd, &err); + if (err) { + error_propagate(errp, err); return -1; } } else if (tap->has_fds) { @@ -733,9 +775,9 @@ int net_init_tap(const NetClientOptions *opts, const char *name, if (tap->has_ifname || tap->has_script || tap->has_downscript || tap->has_vnet_hdr || tap->has_helper || tap->has_queues || tap->has_vhostfd) { - error_report("ifname=, script=, downscript=, vnet_hdr=, " - "helper=, queues=, and vhostfd= " - "are invalid with fds="); + error_setg(errp, "ifname=, script=, downscript=, vnet_hdr=, " + "helper=, queues=, and vhostfd= " + "are invalid with fds="); return -1; } @@ -743,15 +785,16 @@ int net_init_tap(const NetClientOptions *opts, const char *name, 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"); + error_setg(errp, "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]); + fd = monitor_fd_param(cur_mon, fds[i], &err); if (fd == -1) { + error_propagate(errp, err); return -1; } @@ -760,26 +803,30 @@ int net_init_tap(const NetClientOptions *opts, const char *name, 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"); + error_setg(errp, + "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)) { + net_init_tap_one(tap, peer, "tap", name, ifname, + script, downscript, + tap->has_vhostfds ? vhost_fds[i] : NULL, + vnet_hdr, fd, &err); + if (err) { + error_propagate(errp, err); return -1; } } } else if (tap->has_helper) { if (tap->has_ifname || tap->has_script || tap->has_downscript || tap->has_vnet_hdr || tap->has_queues || tap->has_vhostfds) { - error_report("ifname=, script=, downscript=, and vnet_hdr= " - "queues=, and vhostfds= are invalid with helper="); + error_setg(errp, "ifname=, script=, downscript=, vnet_hdr=, " + "queues=, and vhostfds= are invalid with helper="); return -1; } - fd = net_bridge_run_helper(tap->helper, DEFAULT_BRIDGE_INTERFACE); + fd = net_bridge_run_helper(tap->helper, DEFAULT_BRIDGE_INTERFACE, + errp); if (fd == -1) { return -1; } @@ -787,14 +834,17 @@ int net_init_tap(const NetClientOptions *opts, const char *name, fcntl(fd, F_SETFL, O_NONBLOCK); vnet_hdr = tap_probe_vnet_hdr(fd); - if (net_init_tap_one(tap, peer, "bridge", name, ifname, - script, downscript, vhostfdname, - vnet_hdr, fd)) { + net_init_tap_one(tap, peer, "bridge", name, ifname, + script, downscript, vhostfdname, + vnet_hdr, fd, &err); + if (err) { + error_propagate(errp, err); + close(fd); return -1; } } else { if (tap->has_vhostfds) { - error_report("vhostfds= is invalid if fds= wasn't specified"); + error_setg(errp, "vhostfds= is invalid if fds= wasn't specified"); return -1; } script = tap->has_script ? tap->script : DEFAULT_NETWORK_SCRIPT; @@ -809,22 +859,26 @@ int net_init_tap(const NetClientOptions *opts, const char *name, for (i = 0; i < queues; i++) { fd = net_tap_init(tap, &vnet_hdr, i >= 1 ? "no" : script, - ifname, sizeof ifname, queues > 1); + ifname, sizeof ifname, queues > 1, errp); if (fd == -1) { return -1; } if (queues > 1 && i == 0 && !tap->has_ifname) { if (tap_fd_get_ifname(fd, ifname)) { - error_report("Fail to get ifname"); + error_setg(errp, "Fail to get ifname"); + close(fd); return -1; } } - if (net_init_tap_one(tap, peer, "tap", name, ifname, - i >= 1 ? "no" : script, - i >= 1 ? "no" : downscript, - vhostfdname, vnet_hdr, fd)) { + net_init_tap_one(tap, peer, "tap", name, ifname, + i >= 1 ? "no" : script, + i >= 1 ? "no" : downscript, + vhostfdname, vnet_hdr, fd, &err); + if (err) { + error_propagate(errp, err); + close(fd); return -1; } }