Finally implement the new migration option
backend-transfer = ["virtio-net-tap"].
With this enabled (both on source and target) of-course, and with
unix-socket used as migration-channel, we do "migrate" the virtio-net
backend - TAP device, with all its fds.
This way management tool should not care about creating new TAP, and
should not handle switching to it. Migration downtime become shorter.
How it works:
1. For incoming migration, we postpone TAP initialization up to
pre-incoming point.
2. At pre-incoming point we see that "virtio-net-tap" is set for
backend-transfer, so we postpone TAP initialization up to
post-load
3. During virtio-load, we get TAP state (and fds) as part of
virtio-net state
4. In post-load we finalize TAP initialization
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Tested-by: Lei Yang <leiyang@redhat.com>
Reviewed-by: Maksim Davydov <davydov-max@yandex-team.ru>
---
hw/net/virtio-net.c | 74 ++++++++++++++++++++++++++++++++++++++++++++-
include/net/tap.h | 2 ++
migration/options.c | 6 ----
net/tap.c | 45 ++++++++++++++++++++++++++-
4 files changed, 119 insertions(+), 8 deletions(-)
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 661413c72f..41c45a4bc7 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -38,6 +38,7 @@
#include "qapi/qapi-events-migration.h"
#include "hw/virtio/virtio-access.h"
#include "migration/misc.h"
+#include "migration/options.h"
#include "standard-headers/linux/ethtool.h"
#include "system/system.h"
#include "system/replay.h"
@@ -3358,6 +3359,9 @@ struct VirtIONetMigTmp {
uint16_t curr_queue_pairs_1;
uint8_t has_ufo;
uint32_t has_vnet_hdr;
+
+ NetClientState *ncs;
+ uint32_t max_queue_pairs;
};
/* The 2nd and subsequent tx_waiting flags are loaded later than
@@ -3627,6 +3631,71 @@ static const VMStateDescription vhost_user_net_backend_state = {
}
};
+static bool virtio_net_is_tap_mig(void *opaque, int version_id)
+{
+ VirtIONet *n = opaque;
+ NetClientState *nc;
+
+ nc = qemu_get_queue(n->nic);
+
+ return migrate_virtio_net_tap() && nc->peer &&
+ nc->peer->info->type == NET_CLIENT_DRIVER_TAP;
+}
+
+static int virtio_net_nic_pre_save(void *opaque)
+{
+ struct VirtIONetMigTmp *tmp = opaque;
+
+ tmp->ncs = tmp->parent->nic->ncs;
+ tmp->max_queue_pairs = tmp->parent->max_queue_pairs;
+
+ return 0;
+}
+
+static int virtio_net_nic_pre_load(void *opaque)
+{
+ /* Reuse the pointer setup from save */
+ virtio_net_nic_pre_save(opaque);
+
+ return 0;
+}
+
+static int virtio_net_nic_post_load(void *opaque, int version_id)
+{
+ struct VirtIONetMigTmp *tmp = opaque;
+ Error *local_err = NULL;
+
+ if (!virtio_net_update_host_features(tmp->parent, &local_err)) {
+ error_report_err(local_err);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_virtio_net_nic_nc = {
+ .name = "virtio-net-nic-nc",
+ .fields = (const VMStateField[]) {
+ VMSTATE_STRUCT_POINTER(peer, NetClientState, vmstate_tap,
+ NetClientState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static const VMStateDescription vmstate_virtio_net_nic = {
+ .name = "virtio-net-nic",
+ .pre_load = virtio_net_nic_pre_load,
+ .pre_save = virtio_net_nic_pre_save,
+ .post_load = virtio_net_nic_post_load,
+ .fields = (const VMStateField[]) {
+ VMSTATE_STRUCT_VARRAY_POINTER_UINT32(ncs, struct VirtIONetMigTmp,
+ max_queue_pairs,
+ vmstate_virtio_net_nic_nc,
+ struct NetClientState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
static const VMStateDescription vmstate_virtio_net_device = {
.name = "virtio-net-device",
.version_id = VIRTIO_NET_VM_VERSION,
@@ -3658,6 +3727,9 @@ static const VMStateDescription vmstate_virtio_net_device = {
* but based on the uint.
*/
VMSTATE_BUFFER_POINTER_UNSAFE(vlans, VirtIONet, 0, MAX_VLAN >> 3),
+ VMSTATE_WITH_TMP_TEST(VirtIONet, virtio_net_is_tap_mig,
+ struct VirtIONetMigTmp,
+ vmstate_virtio_net_nic),
VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
vmstate_virtio_net_has_vnet),
VMSTATE_UINT8(mac_table.multi_overflow, VirtIONet),
@@ -4239,7 +4311,7 @@ static bool vhost_user_blk_pre_incoming(void *opaque, Error **errp)
VirtIONet *n = opaque;
int i;
- if (peer_wait_incoming(n)) {
+ if (!virtio_net_is_tap_mig(opaque, 0) && peer_wait_incoming(n)) {
for (i = 0; i < n->max_queue_pairs; i++) {
if (!peer_postponed_init(n, i, errp)) {
return false;
diff --git a/include/net/tap.h b/include/net/tap.h
index 5a926ba513..506f7ab719 100644
--- a/include/net/tap.h
+++ b/include/net/tap.h
@@ -36,4 +36,6 @@ int tap_get_fd(NetClientState *nc);
bool tap_wait_incoming(NetClientState *nc);
bool tap_postponed_init(NetClientState *nc, Error **errp);
+extern const VMStateDescription vmstate_tap;
+
#endif /* QEMU_NET_TAP_H */
diff --git a/migration/options.c b/migration/options.c
index 76709af3ab..7c7df6c484 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -1205,12 +1205,6 @@ bool migrate_params_check(MigrationParameters *params, Error **errp)
return false;
}
- /* TODO: implement backend-transfer and remove this check */
- if (params->has_backend_transfer) {
- error_setg(errp, "Not implemented");
- return false;
- }
-
return true;
}
diff --git a/net/tap.c b/net/tap.c
index 8afbf3b407..b9c12dd64c 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -819,7 +819,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
static bool net_tap_setup(TAPState *s, int fd, int vnet_hdr, Error **errp)
{
- if (!net_tap_set_fd(s, fd, vnet_hdr, errp)) {
+ if (fd != -1 && !net_tap_set_fd(s, fd, vnet_hdr, errp)) {
return false;
}
@@ -1225,6 +1225,49 @@ int tap_disable(NetClientState *nc)
}
}
+static int tap_pre_load(void *opaque)
+{
+ TAPState *s = opaque;
+
+ if (s->fd != -1) {
+ error_report(
+ "TAP is already initialized and cannot receive incoming fd");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tap_post_load(void *opaque, int version_id)
+{
+ TAPState *s = opaque;
+ Error *local_err = NULL;
+
+ if (!net_tap_setup(s, -1, -1, &local_err)) {
+ error_report_err(local_err);
+ qemu_del_net_client(&s->nc);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+const VMStateDescription vmstate_tap = {
+ .name = "net-tap",
+ .pre_load = tap_pre_load,
+ .post_load = tap_post_load,
+ .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()
+ }
+};
+
bool tap_wait_incoming(NetClientState *nc)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
--
2.48.1