Support transferring of TAP state (including open fd) through
migration stream as part of viritio-net "local-migration".
Add new option, incoming-fds, which should be set to true to
trigger new logic.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
---
net/tap.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++-
qapi/net.json | 6 +++-
2 files changed, 96 insertions(+), 2 deletions(-)
diff --git a/net/tap.c b/net/tap.c
index 294ca61f8b..f6e4a36e97 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -393,6 +393,65 @@ static VHostNetState *tap_get_vhost_net(NetClientState *nc)
return s->vhost_net;
}
+static bool tap_is_wait_incoming(NetClientState *nc)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
+ return s->fd == -1;
+}
+
+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 bool tap_setup_vhost(TAPState *s, Error **errp);
+
+static int tap_post_load(void *opaque, int version_id)
+{
+ TAPState *s = opaque;
+ Error *local_err = NULL;
+
+ tap_read_poll(s, true);
+
+ if (s->fd < 0) {
+ return -1;
+ }
+
+ if (!tap_setup_vhost(s, &local_err)) {
+ error_prepend(&local_err,
+ "Failed to setup vhost during TAP post-load: ");
+ error_report_err(local_err);
+ return -1;
+ }
+
+ return 0;
+}
+
+static 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()
+ }
+};
+
/* fd support */
static NetClientInfo net_tap_info = {
@@ -412,7 +471,9 @@ 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,
+ .backend_vmsd = &vmstate_tap,
};
static TAPState *net_tap_fd_init(NetClientState *peer,
@@ -905,6 +966,12 @@ int net_init_tap(const Netdev *netdev, const char *name,
return -1;
}
+ if (tap->incoming_fds && (tap->fd || tap->fds || tap->helper)) {
+ error_setg(errp, "incoming-fds is incompatible with "
+ "fd=, fds=, helper=");
+ return -1;
+ }
+
queues = tap_parse_fds_and_queues(tap, &fds, errp);
if (queues < 0) {
return -1;
@@ -923,7 +990,24 @@ int net_init_tap(const Netdev *netdev, const char *name,
goto fail;
}
- if (fds) {
+ if (tap->incoming_fds) {
+ for (i = 0; i < queues; i++) {
+ NetClientState *nc;
+ TAPState *s;
+
+ nc = qemu_new_net_client(&net_tap_info, peer, "tap", name);
+ qemu_set_info_str(nc, "incoming");
+
+ s = DO_UPCAST(TAPState, nc, nc);
+ 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);
@@ -949,6 +1033,12 @@ int net_init_tap(const Netdev *netdev, const char *name,
g_autofree char *downscript =
tap_parse_script(tap->downscript, DEFAULT_NETWORK_DOWN_SCRIPT);
+ if (tap->incoming_fds && (script || downscript)) {
+ error_setg(errp,
+ "incoming-fds requires script=no and downscript=no");
+ return -1;
+ }
+
if (tap->ifname) {
pstrcpy(ifname, sizeof ifname, tap->ifname);
} else {
diff --git a/qapi/net.json b/qapi/net.json
index 118bd34965..79f5ce9f43 100644
--- a/qapi/net.json
+++ b/qapi/net.json
@@ -355,6 +355,9 @@
# @poll-us: maximum number of microseconds that could be spent on busy
# polling for tap (since 2.7)
#
+# @incoming-fds: do not open/connnect any resources, instead wait for
+# TAP state from incoming migration stream. (Since 11.0)
+#
# Since: 1.2
##
{ 'struct': 'NetdevTapOptions',
@@ -373,7 +376,8 @@
'*vhostfds': 'str',
'*vhostforce': 'bool',
'*queues': 'uint32',
- '*poll-us': 'uint32'} }
+ '*poll-us': 'uint32',
+ '*incoming-fds': 'bool' } }
##
# @NetdevSocketOptions:
--
2.52.0