mirror of
https://gitlab.com/qemu-project/qemu.git
synced 2026-10-28 14:04:39 +08:00
virtio-net: support local migration of backend
Next commit will introduce live-migration (with fd-passing) for TAP net backend. So, now we prepare virtio-net for it Add virtio-net option local-migration, which is true by default, but false for older machine types, which doesn't support the feature. We introduce interface for live-migrating backends: 1. ->is_wait_incoming() handler, so that virtio-net knows, that backend is not fully intialized, as it waits for incoming migration stream. 2. MIG_PRI_BACKEND priority: backends should migrate with higher priority than virtio-net, so that we can do final preparations here in post-load handlers and be sure, that backends are already prepared. Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> Reviewed-by: Ben Chaney <bchaney@akamai.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Message-ID: <20260819180201.1970193-14-vsementsov@yandex-team.ru>
This commit is contained in:
1 parent
f6e309e025
commit
bb71b1bffe
4 files changed
+93
-1
No files matched your search
+88
-1
@@ -40,6 +40,7 @@
|
||||
#include "migration/misc.h"
|
||||
#include "standard-headers/linux/ethtool.h"
|
||||
#include "system/system.h"
|
||||
#include "system/runstate.h"
|
||||
#include "system/replay.h"
|
||||
#include "trace.h"
|
||||
#include "monitor/qdev.h"
|
||||
@@ -3079,7 +3080,17 @@ static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue)
|
||||
n->multiqueue = multiqueue;
|
||||
virtio_net_change_num_queues(n, max * 2 + 1);
|
||||
|
||||
virtio_net_set_queue_pairs(n);
|
||||
/*
|
||||
* virtio_net_set_multiqueue() called from set_features(0) on early
|
||||
* reset, when peer may wait for incoming (and is not initialized
|
||||
* yet).
|
||||
* Don't worry about it: virtio_net_set_queue_pairs() will be called
|
||||
* later from virtio_net_post_load_device(), and anyway will be
|
||||
* no-op for local incoming migration with live backend passing.
|
||||
*/
|
||||
if (!n->peers_wait_incoming) {
|
||||
virtio_net_set_queue_pairs(n);
|
||||
}
|
||||
}
|
||||
|
||||
static int virtio_net_pre_load_queues(VirtIODevice *vdev, uint32_t n)
|
||||
@@ -3108,6 +3119,17 @@ static void virtio_net_get_features(VirtIODevice *vdev, uint64_t *features,
|
||||
|
||||
virtio_add_feature_ex(features, VIRTIO_NET_F_MAC);
|
||||
|
||||
if (n->peers_wait_incoming) {
|
||||
/*
|
||||
* Excessive feature set is OK for early initialization when
|
||||
* we wait for local incoming migration: actual guest-negotiated
|
||||
* features will come with migration stream anyway. And we are sure
|
||||
* that we support same host-features as source, because the backend
|
||||
* is the same (the same TAP device, for example).
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
if (!peer_has_vnet_hdr(n)) {
|
||||
virtio_clear_feature_ex(features, VIRTIO_NET_F_CSUM);
|
||||
virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_TSO4);
|
||||
@@ -3204,6 +3226,7 @@ static int virtio_net_post_load_device(void *opaque, int version_id)
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(n);
|
||||
int i, link_down;
|
||||
bool has_tunnel_hdr = virtio_has_tunnel_hdr(vdev->guest_features_ex);
|
||||
Error *local_err = NULL;
|
||||
|
||||
trace_virtio_net_post_load_device();
|
||||
virtio_net_set_mrg_rx_bufs(n, n->mergeable_rx_bufs,
|
||||
@@ -3261,6 +3284,20 @@ static int virtio_net_post_load_device(void *opaque, int version_id)
|
||||
}
|
||||
|
||||
virtio_net_commit_rss_config(n);
|
||||
|
||||
/*
|
||||
* If live-migration is enabled for some backend, than backend
|
||||
* has already been migrated at higher priority (MIG_PRI_BACKEND)
|
||||
* and virtio_net_vnet_post_load() has already called
|
||||
* peer_test_vnet_hdr(). Recompute host_features so that virtio-net
|
||||
* reflects the capabilities of the restored backend.
|
||||
*/
|
||||
virtio_net_get_features(vdev, &vdev->host_features, &local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3411,6 +3448,14 @@ static int virtio_net_vnet_post_load(void *opaque, int version_id)
|
||||
{
|
||||
struct VirtIONetMigTmp *tmp = opaque;
|
||||
|
||||
/*
|
||||
* If live-migration is enabled for some backend, than backend
|
||||
* has already been migrated at higher priority (MIG_PRI_BACKEND),
|
||||
* so n->has_vnet_hdr can be refreshed from the live backend right
|
||||
* here.
|
||||
*/
|
||||
peer_test_vnet_hdr(tmp->parent);
|
||||
|
||||
if (tmp->has_vnet_hdr && !peer_has_vnet_hdr(tmp->parent)) {
|
||||
error_report("virtio-net: saved image requires vnet_hdr=on");
|
||||
return -EINVAL;
|
||||
@@ -3890,6 +3935,42 @@ static bool failover_hide_primary_device(DeviceListener *listener,
|
||||
return qatomic_read(&n->failover_primary_hidden);
|
||||
}
|
||||
|
||||
static bool virtio_net_check_peers_wait_incoming(VirtIONet *n, bool *waiting,
|
||||
Error **errp)
|
||||
{
|
||||
bool has_waiting = false;
|
||||
bool has_not_waiting = false;
|
||||
|
||||
for (int i = 0; i < n->max_queue_pairs; i++) {
|
||||
NetClientState *peer = n->nic->ncs[i].peer;
|
||||
if (!peer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->info->is_wait_incoming &&
|
||||
peer->info->is_wait_incoming(peer)) {
|
||||
has_waiting = true;
|
||||
} else {
|
||||
has_not_waiting = true;
|
||||
}
|
||||
|
||||
if (has_waiting && has_not_waiting) {
|
||||
error_setg(errp, "Mixed peer states: some peers wait for incoming "
|
||||
"migration while others don't");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_waiting && !runstate_check(RUN_STATE_INMIGRATE)) {
|
||||
error_setg(errp, "Peers wait for incoming, but it's not an incoming "
|
||||
"migration.");
|
||||
return false;
|
||||
}
|
||||
|
||||
*waiting = has_waiting;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void virtio_net_device_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||
@@ -4027,6 +4108,12 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
|
||||
n->nic->ncs[i].do_not_pad = true;
|
||||
}
|
||||
|
||||
if (!virtio_net_check_peers_wait_incoming(n, &n->peers_wait_incoming,
|
||||
errp)) {
|
||||
virtio_cleanup(vdev);
|
||||
return;
|
||||
}
|
||||
|
||||
peer_test_vnet_hdr(n);
|
||||
if (peer_has_vnet_hdr(n)) {
|
||||
n->host_hdr_len = sizeof(struct virtio_net_hdr);
|
||||
|
||||
@@ -230,6 +230,7 @@ struct VirtIONet {
|
||||
struct EBPFRSSContext ebpf_rss;
|
||||
uint32_t nr_ebpf_rss_fds;
|
||||
char **ebpf_rss_fds;
|
||||
bool peers_wait_incoming;
|
||||
};
|
||||
|
||||
size_t virtio_net_handle_ctrl_iov(VirtIODevice *vdev,
|
||||
|
||||
@@ -169,6 +169,8 @@ typedef enum {
|
||||
|
||||
MIG_PRI_LOW, /* Must happen after default */
|
||||
MIG_PRI_DEFAULT,
|
||||
MIG_PRI_BACKEND, /* Must happen before emulated devices, */
|
||||
/* e.g. virtio-net */
|
||||
MIG_PRI_IOMMU, /* Must happen before PCI devices */
|
||||
MIG_PRI_PCI_BUS, /* Must happen before IOMMU */
|
||||
MIG_PRI_VIRTIO_MEM, /* Must happen before IOMMU */
|
||||
|
||||
@@ -82,6 +82,7 @@ typedef void (SocketReadStateFinalize)(SocketReadState *rs);
|
||||
typedef void (NetAnnounce)(NetClientState *);
|
||||
typedef bool (SetSteeringEBPF)(NetClientState *, int);
|
||||
typedef bool (NetCheckPeerType)(NetClientState *, ObjectClass *, Error **);
|
||||
typedef bool (IsWaitIncoming)(NetClientState *);
|
||||
typedef struct vhost_net *(GetVHostNet)(NetClientState *nc);
|
||||
|
||||
typedef struct NetClientInfo {
|
||||
@@ -110,6 +111,7 @@ typedef struct NetClientInfo {
|
||||
NetAnnounce *announce;
|
||||
SetSteeringEBPF *set_steering_ebpf;
|
||||
NetCheckPeerType *check_peer_type;
|
||||
IsWaitIncoming *is_wait_incoming;
|
||||
GetVHostNet *get_vhost_net;
|
||||
} NetClientInfo;
|
||||
|
||||
|
||||
Reference in new issue
Block a user