diff options
| author | Richard Henderson <richard.henderson@linaro.org> | 2026-09-11 12:04:42 -1000 |
|---|---|---|
| committer | Richard Henderson <richard.henderson@linaro.org> | 2026-09-11 12:04:42 -1000 |
| commit | d43c2d5f89db70359a7b3a7e2ad7098fcc0165ef (patch) | |
| tree | 129a46718ec957afb906aba41603247766d2ba71 /net | |
| parent | c7d073cf3682a9a18cd0a0342a2340d7f33dcec4 (diff) | |
| parent | 2349ee69899a62bf38d68bd4d63d9c82861abd98 (diff) | |
| download | qemu-master.tar.gz qemu-master.zip | |
Merge tag 'for_upstream' of https://git.kernel.org/pub/scm/virt/kvm/mst/qemu into stagingHEADstagingmaster
pci, vhost, virtio, iommu, apci: features, fixes
new vhost-user-media device
virtio-net now supports live-TAP local migration
cxl now supports CFMW restrictions and per-window options
intel_iommu now supports concurrent page fault handling with PRI
pci now batches memory transactions around mapping updates
vhost-user-blk now allows toggling inflight-migration at runtime
virtio-rtc now supports smeared UTC clock type
intel_iommu now exposes SMPWC when SVM is enabled
new reviewer for CXL patches
Fixes, cleanups all over the place
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
# -----BEGIN PGP SIGNATURE-----
#
# iQFDBAABCgAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAmqkXRUPHG1zdEByZWRo
# YXQuY29tAAoJECgfDbjSjVRpoWsH/jeMRFjgYu5Es05ha87JW6NJW6j/Ggw3F8e5
# kup/SsoeqX+pK1VqXkKimWRf0iXsnweTqviVR88KMmMBP9UNdwgRBM7XflplYiOv
# UOsjZGZAKjOKnyjbQyNihYY4CfaJn2VYkxJBRxweS3/OEK4JV2wmFcWEjFuZEdJT
# SXpYXAPu0e8XTPVk/+bhi6kIYHhsuu1sWhcsoRDGdw5ZrGvwTwF8cS4SWaYHrWc1
# wjvC39P3Hg+Zvw6vDkPP2Amos2s6iheEgsPI0628YYvER+YV641YD48xEXqTG44e
# FGwCihJ97rYYGQEwL+1FL4ZLKcp7HwacqxkJYg+GHbKj1Iog9OQ=
# =9H6A
# -----END PGP SIGNATURE-----
# gpg: Signature made Fri 11 Sep 2026 09:57:09 AM HST
# gpg: using RSA key 5D09FD0871C8F85B94CA8A0D281F0DB8D28D5469
# gpg: issuer "mst@redhat.com"
# gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" [unknown]
# gpg: aka "Michael S. Tsirkin <mst@redhat.com>" [unknown]
# gpg: WARNING: The key's User ID is not certified with a trusted signature!
# gpg: There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 0270 606B 6F3C DF3D 0B17 0970 C350 3912 AFBE 8E67
# Subkey fingerprint: 5D09 FD08 71C8 F85B 94CA 8A0D 281F 0DB8 D28D 5469
* tag 'for_upstream' of https://git.kernel.org/pub/scm/virt/kvm/mst/qemu: (74 commits)
MAINTAINERS: add Junjie Cao as CXL reviewer
vhost-user-blk: use GET_VRING_BASE_SKIP_DRAIN when inflight-migration is on
vhost-user-blk: move inflight_needed higher
vhost-user-blk: make inflight-migration prop mutable on running vm
vhost-user: use skip_drain with GET_VRING_BASE_SKIP_DRAIN message
vhost-user: add GET_VRING_BASE_SKIP_DRAIN message
vhost-user: add skip_drain param to do_vhost_virtqueue_stop
intel_iommu: Expose SMPWC when SVM is enabled
virtio-net: validate IHL in virtio_net_rsc_extract_unit4 before use
net/tap-solaris: Fix resource leaks on error paths
hw/hotplug: Constify HotplugHandler
hw/hotplug: Reduce some HotplugHandler variables scope
hw/hotplug: Mark various HotplugHandlerClass variables as const
hw/acpi: Make AcpiGedState const in build_ged_aml()
pci: load a device's config inside one memory transaction
pci: batch BAR remapping into one memory transaction
intel_iommu: Always write all the flags passed to vtd_set_flag_in_pte
intel_iommu: Only set dirty bit when PTE exposes write permission
intel_iommu: Support concurrent page fault handling with PRI
virtio-rtc: Report smeared UTC clock type
...
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Diffstat (limited to 'net')
| -rw-r--r-- | net/net.c | 14 | ||||
| -rw-r--r-- | net/tap-solaris.c | 69 | ||||
| -rw-r--r-- | net/tap.c | 446 |
3 files changed, 420 insertions, 109 deletions
@@ -261,13 +261,13 @@ static ssize_t qemu_deliver_packet_iov(NetClientState *sender, int iovcnt, void *opaque); -static void qemu_net_client_setup(NetClientState *nc, - NetClientInfo *info, - NetClientState *peer, - const char *model, - const char *name, - NetClientDestructor *destructor, - bool is_datapath) +void qemu_net_client_setup(NetClientState *nc, + NetClientInfo *info, + NetClientState *peer, + const char *model, + const char *name, + NetClientDestructor *destructor, + bool is_datapath) { nc->info = info; nc->model = g_strdup(model); diff --git a/net/tap-solaris.c b/net/tap-solaris.c index 8704b1084b..2c6c7af15e 100644 --- a/net/tap-solaris.c +++ b/net/tap-solaris.c @@ -60,8 +60,6 @@ ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen) */ static int tap_alloc(char *dev, size_t dev_size, Error **errp) { - /* FIXME leaks like a sieve on error paths */ - /* FIXME suspicious: many errors are reported, then ignored */ int tap_fd, if_fd, ppa = -1; static int ip_fd = 0; char *ptr; @@ -102,45 +100,61 @@ static int tap_alloc(char *dev, size_t dev_size, Error **errp) strioc_ppa.ic_timout = 0; strioc_ppa.ic_len = sizeof(ppa); strioc_ppa.ic_dp = (char *)&ppa; - if ((ppa = ioctl (tap_fd, I_STR, &strioc_ppa)) < 0) + if ((ppa = ioctl (tap_fd, I_STR, &strioc_ppa)) < 0) { error_report("Can't assign new interface"); + goto fail_tap_fd; + } if_fd = RETRY_ON_EINTR(open("/dev/tap", O_RDWR, 0)); if (if_fd < 0) { error_setg_file_open(errp, errno, "/dev/tap"); - return -1; + goto fail_tap_fd; } if(ioctl(if_fd, I_PUSH, "ip") < 0){ error_setg(errp, "Can't push IP module"); - return -1; + goto fail_if_fd; } - if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0) + if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0) { error_report("Can't get flags"); + goto fail_if_fd; + } - snprintf (actual_name, 32, "tap%d", ppa); + snprintf(actual_name, 32, "tap%d", ppa); pstrcpy(ifr.lifr_name, sizeof(ifr.lifr_name), actual_name); ifr.lifr_ppa = ppa; - /* Assign ppa according to the unit number returned by tun device */ - - if (ioctl (if_fd, SIOCSLIFNAME, &ifr) < 0) + if (ioctl(if_fd, SIOCSLIFNAME, &ifr) < 0) { error_report("Can't set PPA %d", ppa); - if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) <0) + goto fail_if_fd; + } + + if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0) { error_report("Can't get flags"); + goto fail_if_fd; + } + /* Push arp module to if_fd */ - if (ioctl (if_fd, I_PUSH, "arp") < 0) + if (ioctl(if_fd, I_PUSH, "arp") < 0) { error_report("Can't push ARP module (2)"); + goto fail_if_fd; + } /* Push arp module to ip_fd */ - if (ioctl (ip_fd, I_POP, NULL) < 0) + if (ioctl(ip_fd, I_POP, NULL) < 0) { error_report("I_POP failed"); - if (ioctl (ip_fd, I_PUSH, "arp") < 0) + goto fail_if_fd; + } + if (ioctl(ip_fd, I_PUSH, "arp") < 0) { error_report("Can't push ARP module (3)"); + goto fail_if_fd; + } /* Open arp_fd */ arp_fd = RETRY_ON_EINTR(open("/dev/tap", O_RDWR, 0)); - if (arp_fd < 0) + if (arp_fd < 0) { error_report("Can't open %s", "/dev/tap"); + goto fail_if_fd; + } /* Set ifname to arp */ strioc_if.ic_cmd = SIOCSLIFNAME; @@ -149,32 +163,45 @@ static int tap_alloc(char *dev, size_t dev_size, Error **errp) strioc_if.ic_dp = (char *)𝔦 if (ioctl(arp_fd, I_STR, &strioc_if) < 0){ error_report("Can't set ifname to arp"); + goto fail_arp_fd; } if((ip_muxid = ioctl(ip_fd, I_LINK, if_fd)) < 0){ error_setg(errp, "Can't link TAP device to IP"); - return -1; + goto fail_arp_fd; } - if ((arp_muxid = ioctl (ip_fd, link_type, arp_fd)) < 0) + if ((arp_muxid = ioctl(ip_fd, link_type, arp_fd)) < 0) { error_report("Can't link TAP device to ARP"); + goto fail_ip_muxid; + } - close (if_fd); + close(if_fd); memset(&ifr, 0x0, sizeof(ifr)); pstrcpy(ifr.lifr_name, sizeof(ifr.lifr_name), actual_name); ifr.lifr_ip_muxid = ip_muxid; ifr.lifr_arp_muxid = arp_muxid; - if (ioctl (ip_fd, SIOCSLIFMUXID, &ifr) < 0) + if (ioctl(ip_fd, SIOCSLIFMUXID, &ifr) < 0) { - ioctl (ip_fd, I_PUNLINK , arp_muxid); - ioctl (ip_fd, I_PUNLINK, ip_muxid); + ioctl(ip_fd, I_PUNLINK, arp_muxid); + ioctl(ip_fd, I_PUNLINK, ip_muxid); error_report("Can't set multiplexor id"); } snprintf(dev, dev_size, "tap%d", ppa); return tap_fd; + +fail_ip_muxid: + ioctl(ip_fd, I_PUNLINK, ip_muxid); +fail_arp_fd: + close(arp_fd); +fail_if_fd: + close(if_fd); +fail_tap_fd: + close(tap_fd); + return -1; } int tap_open(char *ifname, int ifname_size, int *vnet_hdr, @@ -36,13 +36,19 @@ #include "net/net.h" #include "clients.h" #include "monitor/monitor.h" +#include "system/runstate.h" #include "system/system.h" +#include "migration/misc.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/sockets.h" #include "hw/virtio/vhost.h" +#include "hw/core/vmstate-if.h" +#include "migration/vmstate.h" +#include "qom/object.h" +#include "qom/compat-properties.h" #include "net/tap.h" #include "net/util.h" @@ -68,9 +74,17 @@ static const int kernel_feature_bits[] = { VHOST_INVALID_FEATURE_BIT }; -typedef struct TAPState { +OBJECT_DECLARE_SIMPLE_TYPE(TAPState, TAP_NETDEV) + +static const VMStateDescription vmstate_tap; + +struct TAPState { + Object parent_obj; + NetClientState nc; int fd; + int vhostfd; + uint32_t vhost_busyloop_timeout; char down_script[1024]; char down_script_arg[128]; uint8_t buf[NET_BUFSIZE]; @@ -84,7 +98,12 @@ typedef struct TAPState { VHostNetState *vhost_net; unsigned host_vnet_hdr_len; Notifier exit; -} TAPState; + + int queue_index; + bool enable_poll_on_resume; + VMChangeStateEntry *vmstate; + bool permit_local_migration; +}; static void launch_script(const char *setup_script, const char *ifname, int fd, Error **errp); @@ -92,19 +111,39 @@ static void launch_script(const char *setup_script, const char *ifname, static void tap_send(void *opaque); static void tap_writable(void *opaque); -static char *tap_parse_script(const char *script_arg, const char *default_path) +static bool tap_is_explicit_no_script(const char *script_arg_name, + const char *script_arg_value) { - g_autofree char *res = g_strdup(script_arg); + if (!script_arg_value) { + return false; + } - if (!res) { - res = get_relocated_path(default_path); + if (script_arg_value[0] == '\0') { + return true; } - if (res[0] == '\0' || strcmp(res, "no") == 0) { + if (strcmp(script_arg_value, "no") == 0) { + warn_report("'%s=no' is deprecated; use '%s=' instead", + script_arg_name, script_arg_name); + return true; + } + + return false; +} + +static char *tap_parse_script(const char *script_arg_name, + const char *script_arg_value, + const char *default_path) +{ + if (tap_is_explicit_no_script(script_arg_name, script_arg_value)) { return NULL; } - return g_steal_pointer(&res); + if (!script_arg_value) { + return get_relocated_path(default_path); + } + + return g_strdup(script_arg_value); } static void tap_update_fd_handler(TAPState *s) @@ -117,10 +156,31 @@ static void tap_update_fd_handler(TAPState *s) static void tap_read_poll(TAPState *s, bool enable) { + if (enable && runstate_check(RUN_STATE_FINISH_MIGRATE)) { + s->enable_poll_on_resume = true; + return; + } s->read_poll = enable; tap_update_fd_handler(s); } +static void tap_vm_state_change(void *opaque, bool running, RunState state) +{ + TAPState *s = opaque; + + if (running) { + if (s->enable_poll_on_resume) { + tap_read_poll(s, true); + s->enable_poll_on_resume = false; + } + } else if (state == RUN_STATE_FINISH_MIGRATE) { + if (s->read_poll) { + s->enable_poll_on_resume = true; + tap_read_poll(s, false); + } + } +} + static void tap_write_poll(TAPState *s, bool enable) { s->write_poll = enable; @@ -153,7 +213,7 @@ static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt static ssize_t tap_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); const struct iovec *iovp = iov; g_autofree struct iovec *iov_copy = NULL; struct virtio_net_hdr hdr = { }; @@ -189,7 +249,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); + TAPState *s = container_of(nc, TAPState, nc); tap_read_poll(s, true); } @@ -249,7 +309,7 @@ static void tap_send(void *opaque) static bool tap_has_ufo(NetClientState *nc) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); assert(nc->info->type == NET_CLIENT_DRIVER_TAP); @@ -258,7 +318,7 @@ static bool tap_has_ufo(NetClientState *nc) static bool tap_has_uso(NetClientState *nc) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); assert(nc->info->type == NET_CLIENT_DRIVER_TAP); @@ -267,7 +327,7 @@ static bool tap_has_uso(NetClientState *nc) static bool tap_has_tunnel(NetClientState *nc) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); assert(nc->info->type == NET_CLIENT_DRIVER_TAP); return s->has_tunnel; @@ -275,7 +335,7 @@ static bool tap_has_tunnel(NetClientState *nc) static bool tap_has_vnet_hdr(NetClientState *nc) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); assert(nc->info->type == NET_CLIENT_DRIVER_TAP); @@ -289,7 +349,7 @@ static bool tap_has_vnet_hdr_len(NetClientState *nc, int len) static void tap_set_vnet_hdr_len(NetClientState *nc, int len) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); assert(nc->info->type == NET_CLIENT_DRIVER_TAP); @@ -300,21 +360,21 @@ static void tap_set_vnet_hdr_len(NetClientState *nc, int len) static int tap_set_vnet_le(NetClientState *nc, bool is_le) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, 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); + TAPState *s = container_of(nc, TAPState, nc); return tap_fd_set_vnet_be(s->fd, is_be); } static void tap_set_offload(NetClientState *nc, const NetOffloads *ol) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); if (s->fd < 0) { return; } @@ -335,7 +395,7 @@ static void tap_exit_notify(Notifier *notifier, void *data) static void tap_cleanup(NetClientState *nc) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); if (s->vhost_net) { vhost_net_cleanup(s->vhost_net); @@ -351,22 +411,29 @@ static void tap_cleanup(NetClientState *nc) s->exit.notify = NULL; } + if (s->vmstate) { + qemu_del_vm_change_state_handler(s->vmstate); + s->vmstate = NULL; + } + tap_read_poll(s, false); tap_write_poll(s, false); close(s->fd); s->fd = -1; + + vmstate_unregister(VMSTATE_IF(s), &vmstate_tap, s); } static void tap_poll(NetClientState *nc, bool enable) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); tap_read_poll(s, enable); tap_write_poll(s, enable); } static bool tap_set_steering_ebpf(NetClientState *nc, int prog_fd) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); assert(nc->info->type == NET_CLIENT_DRIVER_TAP); return tap_fd_set_steering_ebpf(s->fd, prog_fd) == 0; @@ -374,7 +441,7 @@ static bool tap_set_steering_ebpf(NetClientState *nc, int prog_fd) int tap_get_fd(NetClientState *nc) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); assert(nc->info->type == NET_CLIENT_DRIVER_TAP); return s->fd; } @@ -386,11 +453,140 @@ int tap_get_fd(NetClientState *nc) */ static VHostNetState *tap_get_vhost_net(NetClientState *nc) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); assert(nc->info->type == NET_CLIENT_DRIVER_TAP); return s->vhost_net; } +static bool tap_is_wait_incoming(NetClientState *nc) +{ + TAPState *s = container_of(nc, TAPState, nc); + assert(nc->info->type == NET_CLIENT_DRIVER_TAP); + return s->fd == -1; +} + +static bool tap_pre_load(void *opaque, Error **errp) +{ + ERRP_GUARD(); + TAPState *s = opaque; + + if (s->fd != -1) { + error_setg(errp, + "TAP is already initialized and cannot receive " + "incoming fd"); + error_append_hint(errp, + "Migration parameter 'local' must be set" + " before creating the TAP device."); + return false; + } + + return true; +} + +static bool tap_setup_vhost(TAPState *s, Error **errp); + +static bool tap_post_load(void *opaque, int version_id, Error **errp) +{ + ERRP_GUARD(); + TAPState *s = opaque; + + tap_read_poll(s, true); + + if (s->fd < 0) { + error_setg(errp, "FD was not loaded during incoming migration"); + return false; + } + + if (!tap_setup_vhost(s, errp)) { + error_prepend(errp, + "Failed to setup vhost during TAP post-load: "); + return false; + } + + return true; +} + +static bool tap_needed(void *opaque) +{ + TAPState *s = opaque; + + return s->permit_local_migration && migrate_local(); +} + +static const VMStateDescription vmstate_tap = { + .name = "net-tap", + .priority = MIG_PRI_BACKEND, + .pre_load_errp = tap_pre_load, + .post_load_errp = tap_post_load, + .needed = tap_needed, + .fields = (const VMStateField[]) { + VMSTATE_FD(fd, TAPState), + VMSTATE_BOOL(using_vnet_hdr, TAPState), + VMSTATE_BOOL(has_ufo, TAPState), + VMSTATE_BOOL(has_uso, TAPState), + VMSTATE_BOOL(has_tunnel, TAPState), + VMSTATE_BOOL(enabled, TAPState), + VMSTATE_UINT32(host_vnet_hdr_len, TAPState), + VMSTATE_END_OF_LIST() + } +}; + +static char *tap_vmstate_if_get_id(VMStateIf *obj) +{ + TAPState *s = TAP_NETDEV(obj); + char *res = g_strdup_printf("%s/%d", s->nc.name, s->queue_index); + return res; +} + +static bool tap_get_permit_local_migration_prop(Object *obj, Error **errp) +{ + TAPState *s = TAP_NETDEV(obj); + return s->permit_local_migration; +} + +static void tap_set_permit_local_migration_prop(Object *obj, bool value, + Error **errp) +{ + TAPState *s = TAP_NETDEV(obj); + s->permit_local_migration = value; +} + +static void tap_instance_init(Object *obj) +{ + TAPState *s = TAP_NETDEV(obj); + s->permit_local_migration = false; +} + +static void tap_class_init(ObjectClass *klass, const void *data) +{ + VMStateIfClass *vc = VMSTATE_IF_CLASS(klass); + + vc->get_id = tap_vmstate_if_get_id; + + object_class_property_add_bool(klass, "x-permit-local-migration", + tap_get_permit_local_migration_prop, + tap_set_permit_local_migration_prop); +} + +static const TypeInfo tap_netdev_info = { + .name = TYPE_TAP_NETDEV, + .parent = TYPE_OBJECT, + .instance_size = sizeof(TAPState), + .instance_init = tap_instance_init, + .instance_post_init = object_apply_compat_props, + .class_init = tap_class_init, + .interfaces = (const InterfaceInfo[]) { + { TYPE_VMSTATE_IF }, + { } + }, +}; + +static void tap_net_client_destructor(NetClientState *nc) +{ + TAPState *s = container_of(nc, TAPState, nc); + object_unref(OBJECT(s)); +} + /* fd support */ static NetClientInfo net_tap_info = { @@ -410,22 +606,46 @@ static NetClientInfo net_tap_info = { .set_vnet_le = tap_set_vnet_le, .set_vnet_be = tap_set_vnet_be, .set_steering_ebpf = tap_set_steering_ebpf, + .is_wait_incoming = tap_is_wait_incoming, .get_vhost_net = tap_get_vhost_net, }; +static TAPState *new_tap(NetClientState *peer, + const char *model, + const char *name, + int queue_index, + bool has_permit_local_migration, + bool permit_local_migration) +{ + TAPState *s = TAP_NETDEV(object_new(TYPE_TAP_NETDEV)); + + qemu_net_client_setup(&s->nc, &net_tap_info, peer, model, name, + tap_net_client_destructor, true); + + s->queue_index = queue_index; + + if (has_permit_local_migration) { + s->permit_local_migration = permit_local_migration; + } + + vmstate_register(VMSTATE_IF(s), VMSTATE_INSTANCE_ID_ANY, &vmstate_tap, s); + + return s; +} + static TAPState *net_tap_fd_init(NetClientState *peer, const char *model, const char *name, int fd, - int vnet_hdr) + int vnet_hdr, + int queue_index, + bool has_permit_local_migration, + bool permit_local_migration) { NetOffloads ol = {}; - NetClientState *nc; - TAPState *s; - - nc = qemu_new_net_client(&net_tap_info, peer, model, name); - - s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = new_tap(peer, model, name, queue_index, + has_permit_local_migration, + permit_local_migration); s->fd = fd; s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0; @@ -662,7 +882,7 @@ int net_init_bridge(const Netdev *netdev, const char *name, close(fd); return -1; } - s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr); + s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr, 0, true, false); qemu_set_info_str(&s->nc, "helper=%s,br=%s", helper, br); @@ -702,18 +922,56 @@ static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr, return fd; } +static bool tap_setup_vhost(TAPState *s, Error **errp) +{ + VhostNetOptions options; + + if (s->vhostfd == -1) { + return true; + } + + options.backend_type = VHOST_BACKEND_TYPE_KERNEL; + options.net_backend = &s->nc; + options.busyloop_timeout = s->vhost_busyloop_timeout; + options.opaque = (void *)(uintptr_t)s->vhostfd; + options.nvqs = 2; + options.feature_bits = kernel_feature_bits; + options.get_acked_features = NULL; + options.save_acked_features = NULL; + options.max_tx_queue_size = 0; + options.is_vhost_user = false; + + s->vhost_net = vhost_net_init(&options); + if (!s->vhost_net) { + error_setg(errp, + "vhost-net requested but could not be initialized"); + return false; + } + + /* vhostfd ownership is passed to s->vhost_net */ + s->vhostfd = -1; + + return true; +} + static bool net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, const char *name, const char *ifname, const char *script, const char *downscript, int vhostfd, - int vnet_hdr, int fd, Error **errp) + int vnet_hdr, int fd, int queue_index, + Error **errp) { TAPState *s = net_tap_fd_init(peer, tap->helper ? "bridge" : "tap", - name, fd, vnet_hdr); + name, fd, vnet_hdr, queue_index, + tap->has_x_permit_local_migration, + tap->x_permit_local_migration); bool sndbuf_required = tap->has_sndbuf; int sndbuf = (tap->has_sndbuf && tap->sndbuf) ? MIN(tap->sndbuf, INT_MAX) : INT_MAX; + s->enable_poll_on_resume = false; + s->vmstate = qemu_add_vm_change_state_handler(tap_vm_state_change, s); + if (!tap_set_sndbuf(fd, sndbuf, sndbuf_required ? errp : NULL) && sndbuf_required) { goto failed; @@ -725,7 +983,7 @@ static bool net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, qemu_set_info_str(&s->nc, "helper=%s", tap->helper); } else { qemu_set_info_str(&s->nc, "ifname=%s,script=%s,downscript=%s", ifname, - script ?: "no", downscript ?: "no"); + script ?: "", downscript ?: ""); if (downscript) { snprintf(s->down_script, sizeof(s->down_script), "%s", downscript); @@ -736,42 +994,10 @@ static bool net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, } } - if (tap->has_vhost ? tap->vhost : - (vhostfd != -1) || (tap->has_vhostforce && tap->vhostforce)) { - VhostNetOptions options; - - options.backend_type = VHOST_BACKEND_TYPE_KERNEL; - options.net_backend = &s->nc; - if (tap->has_poll_us) { - options.busyloop_timeout = tap->poll_us; - } else { - options.busyloop_timeout = 0; - } - - if (vhostfd == -1) { - vhostfd = open("/dev/vhost-net", O_RDWR); - if (vhostfd < 0) { - error_setg_file_open(errp, errno, "/dev/vhost-net"); - goto failed; - } - if (!qemu_set_blocking(vhostfd, false, errp)) { - goto failed; - } - } - options.opaque = (void *)(uintptr_t)vhostfd; - options.nvqs = 2; - options.feature_bits = kernel_feature_bits; - options.get_acked_features = NULL; - options.save_acked_features = NULL; - options.max_tx_queue_size = 0; - options.is_vhost_user = false; - - s->vhost_net = vhost_net_init(&options); - if (!s->vhost_net) { - error_setg(errp, - "vhost-net requested but could not be initialized"); - goto failed; - } + s->vhostfd = vhostfd; + s->vhost_busyloop_timeout = tap->has_poll_us ? tap->poll_us : 0; + if (!tap_setup_vhost(s, errp)) { + return false; } return true; @@ -843,14 +1069,31 @@ static int tap_parse_fds_and_queues(const NetdevTapOptions *tap, int **fds, static bool tap_parse_vhost_fds(const NetdevTapOptions *tap, int **vhost_fds, int queues, Error **errp) { - if (!(tap->vhostfd || tap->vhostfds)) { + bool need_vhost = tap->has_vhost ? tap->vhost : + ((tap->vhostfd || tap->vhostfds) || + (tap->has_vhostforce && tap->vhostforce)); + + if (!need_vhost) { *vhost_fds = NULL; return true; } - if (net_parse_fds(tap->vhostfd ?: tap->vhostfds, - vhost_fds, queues, errp) < 0) { - return false; + if (tap->vhostfd || tap->vhostfds) { + if (net_parse_fds(tap->vhostfd ?: tap->vhostfds, + vhost_fds, queues, errp) < 0) { + return false; + } + } else { + *vhost_fds = g_new(int, queues); + for (int i = 0; i < queues; i++) { + int vhostfd = open("/dev/vhost-net", O_RDWR); + if (vhostfd < 0) { + error_setg_file_open(errp, errno, "/dev/vhost-net"); + net_free_fds(*vhost_fds, i); + return false; + } + (*vhost_fds)[i] = vhostfd; + } } if (!unblock_fds(*vhost_fds, queues, errp)) { @@ -869,6 +1112,7 @@ int net_init_tap(const Netdev *netdev, const char *name, /* for the no-fd, no-helper case */ char ifname[128]; int *fds = NULL, *vhost_fds = NULL; + bool incoming_fds; assert(netdev->type == NET_CLIENT_DRIVER_TAP); tap = &netdev->u.tap; @@ -891,6 +1135,23 @@ int net_init_tap(const Netdev *netdev, const char *name, return -1; } + incoming_fds = tap->x_permit_local_migration && migrate_local() && + runstate_check(RUN_STATE_INMIGRATE); + + if (incoming_fds && + (tap->fd || tap->fds || tap->helper || tap->br || tap->ifname || + tap->has_sndbuf || tap->has_vnet_hdr || + !tap_is_explicit_no_script("script", tap->script) || + !tap_is_explicit_no_script("downscript", tap->downscript))) { + error_setg(errp, "Local incoming migration of TAP device (-incoming, " + "migration parameter @local is set, " + "TAP parameter @x-permit-local-migration is set) " + "is incompatible with " + "fd=, fds=, helper=, br=, ifname=, sndbuf= and vnet_hdr=, " + "and requires explicit empty script= and downscript="); + return -1; + } + queues = tap_parse_fds_and_queues(tap, &fds, errp); if (queues < 0) { return -1; @@ -909,7 +1170,22 @@ int net_init_tap(const Netdev *netdev, const char *name, goto fail; } - if (fds) { + if (incoming_fds) { + for (i = 0; i < queues; i++) { + TAPState *s = new_tap(peer, "tap", name, i, + tap->has_x_permit_local_migration, + tap->x_permit_local_migration); + qemu_set_info_str(&s->nc, "incoming"); + + s->fd = -1; + if (vhost_fds) { + s->vhostfd = vhost_fds[i]; + s->vhost_busyloop_timeout = tap->has_poll_us ? tap->poll_us : 0; + } else { + s->vhostfd = -1; + } + } + } else if (fds) { for (i = 0; i < queues; i++) { if (i == 0) { vnet_hdr = tap_probe_vnet_hdr(fds[i], errp); @@ -925,15 +1201,16 @@ int net_init_tap(const Netdev *netdev, const char *name, if (!net_init_tap_one(tap, peer, name, ifname, NULL, NULL, vhost_fds ? vhost_fds[i] : -1, - vnet_hdr, fds[i], errp)) { + vnet_hdr, fds[i], i, errp)) { goto fail; } } } else { g_autofree char *script = - tap_parse_script(tap->script, DEFAULT_NETWORK_SCRIPT); + tap_parse_script("script", tap->script, DEFAULT_NETWORK_SCRIPT); g_autofree char *downscript = - tap_parse_script(tap->downscript, DEFAULT_NETWORK_DOWN_SCRIPT); + tap_parse_script("downscript", tap->downscript, + DEFAULT_NETWORK_DOWN_SCRIPT); if (tap->ifname) { pstrcpy(ifname, sizeof ifname, tap->ifname); @@ -959,7 +1236,7 @@ int net_init_tap(const Netdev *netdev, const char *name, i >= 1 ? NULL : script, i >= 1 ? NULL : downscript, vhost_fds ? vhost_fds[i] : -1, - vnet_hdr, fd, errp)) { + vnet_hdr, fd, i, errp)) { goto fail; } } @@ -976,7 +1253,7 @@ fail: int tap_enable(NetClientState *nc) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); int ret; if (s->enabled) { @@ -993,7 +1270,7 @@ int tap_enable(NetClientState *nc) int tap_disable(NetClientState *nc) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); + TAPState *s = container_of(nc, TAPState, nc); int ret; if (s->enabled == 0) { @@ -1008,3 +1285,10 @@ int tap_disable(NetClientState *nc) return ret; } } + +static void tap_register_types(void) +{ + type_register_static(&tap_netdev_info); +} + +type_init(tap_register_types) |
