summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorRichard Henderson <richard.henderson@linaro.org>2026-09-11 12:04:42 -1000
committerRichard Henderson <richard.henderson@linaro.org>2026-09-11 12:04:42 -1000
commitd43c2d5f89db70359a7b3a7e2ad7098fcc0165ef (patch)
tree129a46718ec957afb906aba41603247766d2ba71 /net
parentc7d073cf3682a9a18cd0a0342a2340d7f33dcec4 (diff)
parent2349ee69899a62bf38d68bd4d63d9c82861abd98 (diff)
downloadqemu-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.c14
-rw-r--r--net/tap-solaris.c69
-rw-r--r--net/tap.c446
3 files changed, 420 insertions, 109 deletions
diff --git a/net/net.c b/net/net.c
index 1161183e18..d7fa637ce5 100644
--- a/net/net.c
+++ b/net/net.c
@@ -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 *)&ifr;
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,
diff --git a/net/tap.c b/net/tap.c
index 57ffb09885..d71e3dd117 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -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)