Support transferring of TAP state (including open fd) through
migration stream.
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 | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++-
qapi/net.json | 6 +++-
2 files changed, 92 insertions(+), 2 deletions(-)
diff --git a/net/tap.c b/net/tap.c
index bd19c71c42..57d4d2d9f8 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,
@@ -907,6 +968,14 @@ int net_init_tap(const Netdev *netdev, const char *name,
return -1;
}
+ if (tap->incoming_fds &&
+ (tap->fd || tap->fds || tap->helper || tap->script ||
+ tap->downscript)) {
+ error_setg(errp, "incoming-fds is incompatible with "
+ "fd=, fds=, helper=, script=, downscript=");
+ return -1;
+ }
+
queues = tap_parse_fds_and_queues(tap, &fds, errp);
if (queues < 0) {
return -1;
@@ -925,7 +994,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);
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
On 2/1/26, 11:21 AM, "Vladimir Sementsov-Ogievskiy" <vsementsov@yandex-team.ru <mailto:vsementsov@yandex-team.ru>> wrote:
> + if (tap->incoming_fds &&
> + (tap->fd || tap->fds || tap->helper || tap->script ||
> + tap->downscript)) {
> + error_setg(errp, "incoming-fds is incompatible with "
> + "fd=, fds=, helper=, script=, downscript=");
> + return -1;
> + }
Is it possible to relax this constraint at all? If so, I
would prefer to allow script= and downscript= parameters
to remain in place.
Thanks,
Ben
On 04.02.26 19:46, Chaney, Ben wrote:
>
> On 2/1/26, 11:21 AM, "Vladimir Sementsov-Ogievskiy" <vsementsov@yandex-team.ru <mailto:vsementsov@yandex-team.ru>> wrote:
>
>> + if (tap->incoming_fds &&
>> + (tap->fd || tap->fds || tap->helper || tap->script ||
>> + tap->downscript)) {
>> + error_setg(errp, "incoming-fds is incompatible with "
>> + "fd=, fds=, helper=, script=, downscript=");
>> + return -1;
>> + }
>
> Is it possible to relax this constraint at all? If so, I
> would prefer to allow script= and downscript= parameters
> to remain in place.
>
That possible, but this requires some additional logic I think.
What if migration fails? Who should call downscript? Migration
may be successful on source, and fail on target.. In this case,
management tool usually resume stopped source. And in this case
source should get again a responsibility to call downscript.
So, I think it should be additional patch on top, which introduce
support for script/downscript together with backend-transfer.
--
Best regards,
Vladimir
On 2/5/26, 3:12 AM, "Vladimir Sementsov-Ogievskiy" <vsementsov@yandex-team.ru <mailto:vsementsov@yandex-team.ru>> wrote:
On 04.02.26 19:46, Chaney, Ben wrote:
>
>
> Is it possible to relax this constraint at all? If so, I
> would prefer to allow script= and downscript= parameters
> to remain in place.
>
>
> That possible, but this requires some additional logic I think.
>
>
> What if migration fails? Who should call downscript? Migration
> may be successful on source, and fail on target.. In this case,
> management tool usually resume stopped source. And in this case
> source should get again a responsibility to call downscript.
Hmm... If script and downscript are not set, they default to
/etc/qemu-ifup and /etc/qemu-ifdown. To disable them
altogether you must pass script=no,downscript=no, which
is not currently possible with this patch.
> So, I think it should be additional patch on top, which introduce
> support for script/downscript together with backend-transfer.
Would it be possible to at least support script=no,downscript=no as
part of this patch? We may want to require it because it sounds like
we don't have the logic to call qemu-ifup and qemu-ifdown correctly
in the event of a failure.
Thanks,
Ben
On 05.02.26 17:51, Chaney, Ben wrote: > > On 2/5/26, 3:12 AM, "Vladimir Sementsov-Ogievskiy" <vsementsov@yandex-team.ru <mailto:vsementsov@yandex-team.ru>> wrote: > > On 04.02.26 19:46, Chaney, Ben wrote: >> >> >> Is it possible to relax this constraint at all? If so, I >> would prefer to allow script= and downscript= parameters >> to remain in place. >> >> >> That possible, but this requires some additional logic I think. >> >> >> What if migration fails? Who should call downscript? Migration >> may be successful on source, and fail on target.. In this case, >> management tool usually resume stopped source. And in this case >> source should get again a responsibility to call downscript. > > Hmm... If script and downscript are not set, they default to > /etc/qemu-ifup and /etc/qemu-ifdown. To disable them > altogether you must pass script=no,downscript=no, which > is not currently possible with this patch. Yes, but when new option is set, we don't use any scripts. Like for other options which doesn't allow use of script/downscript (fd=, fds=, helper=). But I see now, it was wrong decision. > >> So, I think it should be additional patch on top, which introduce >> support for script/downscript together with backend-transfer. > > Would it be possible to at least support script=no,downscript=no as > part of this patch? We may want to require it because it sounds like > we don't have the logic to call qemu-ifup and qemu-ifdown correctly > in the event of a failure. > Agree, that's correct. So, for this patch I should require script and downscript to be present and set to "no", to be able make a full support for them later. -- Best regards, Vladimir
© 2016 - 2026 Red Hat, Inc.