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.
For new option require explicitly unset script and downscript,
to keep possibility of implementing support for them in future.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
---
net/tap.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++---
qapi/net.json | 6 ++-
2 files changed, 113 insertions(+), 7 deletions(-)
diff --git a/net/tap.c b/net/tap.c
index 2b96348eb18..412b91d3780 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -94,19 +94,25 @@ static void launch_script(const char *setup_script, const char *ifname,
static void tap_send(void *opaque);
static void tap_writable(void *opaque);
+static bool tap_is_explicit_no_scirpt(const char *script_arg)
+{
+ return script_arg &&
+ (script_arg[0] == '\0' || strcmp(script_arg, "no") == 0);
+}
+
static char *tap_parse_script(const char *script_arg, const char *default_path)
{
g_autofree char *res = g_strdup(script_arg);
- if (!res) {
- res = get_relocated_path(default_path);
+ if (tap_is_explicit_no_scirpt(script_arg)) {
+ return NULL;
}
- if (res[0] == '\0' || strcmp(res, "no") == 0) {
- return NULL;
+ if (!script_arg) {
+ return get_relocated_path(default_path);
}
- return g_steal_pointer(&res);
+ return g_strdup(script_arg);
}
static void tap_update_fd_handler(TAPState *s)
@@ -393,6 +399,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 +477,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,
@@ -909,6 +976,24 @@ 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;
+ }
+
+ if (tap->incoming_fds &&
+ !(tap_is_explicit_no_scirpt(tap->script) &&
+ tap_is_explicit_no_scirpt(tap->downscript))) {
+ /*
+ * script="" and downscript="" are silently supported to be consistent
+ * with cases without incoming_fds, but do not care to put this into
+ * error message.
+ */
+ error_setg(errp, "incoming-fds requires script=no and downscript=no");
+ return -1;
+ }
+
queues = tap_parse_fds_and_queues(tap, &fds, errp);
if (queues < 0) {
return -1;
@@ -927,7 +1012,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 118bd349651..79f5ce9f431 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
Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> writes:
> 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.
>
> For new option require explicitly unset script and downscript,
> to keep possibility of implementing support for them in future.
>
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
[...]
> diff --git a/qapi/net.json b/qapi/net.json
> index 118bd349651..79f5ce9f431 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)
"resources"? The name @incoming-fds suggests this is about file
descriptors...
"wait for TAP state from" does not make sense. Do you mean "retrieve
TAP state from"?
Is any part of NetdevTapOptions ignored when @incoming-fds is true?
> +#
> # 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:
On 10.03.26 16:46, Markus Armbruster wrote:
> Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> writes:
>
>> 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.
>>
>> For new option require explicitly unset script and downscript,
>> to keep possibility of implementing support for them in future.
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
>
> [...]
>
>> diff --git a/qapi/net.json b/qapi/net.json
>> index 118bd349651..79f5ce9f431 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)
>
> "resources"? The name @incoming-fds suggests this is about file
> descriptors...
Hmm. But, I can't say "open any file descriptors"..
>
> "wait for TAP state from" does not make sense. Do you mean "retrieve
> TAP state from"?
Retrieve means actively do something, but I mean, that FDs will come in
future from incoming migration stream, and we just wait for it and "do nothing"
at the moment of net-tap initialization.
Let me try to reword:
Do not open or create any TAP devices. Prepare for getting opened
TAP file descriptors from incoming migration stream.
>
> Is any part of NetdevTapOptions ignored when @incoming-fds is true?
>
Hmm yes. sndbuf, vnet_hdr and ifname. I should add this information here.
>> +#
>> # 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:
>
--
Best regards,
Vladimir
Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> writes: > On 10.03.26 16:46, Markus Armbruster wrote: >> Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> writes: >> >>> 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. >>> >>> For new option require explicitly unset script and downscript, >>> to keep possibility of implementing support for them in future. >>> >>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> >> [...] >> >>> diff --git a/qapi/net.json b/qapi/net.json >>> index 118bd349651..79f5ce9f431 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) >> "resources"? The name @incoming-fds suggests this is about file >> descriptors... > > Hmm. But, I can't say "open any file descriptors".. > > >> "wait for TAP state from" does not make sense. Do you mean "retrieve >> TAP state from"? > > Retrieve means actively do something, but I mean, that FDs will come in > future from incoming migration stream, and we just wait for it and "do nothing" > at the moment of net-tap initialization. > > > Let me try to reword: > > Do not open or create any TAP devices. Prepare for getting opened > TAP file descriptors from incoming migration stream. Better, thanks! >> Is any part of NetdevTapOptions ignored when @incoming-fds is true? > > Hmm yes. sndbuf, vnet_hdr and ifname. I should add this information here. I dislike ignoring the user's instructions silently. Can we make it an error? [...]
On 11.03.26 12:38, Markus Armbruster wrote: > Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> writes: > >> On 10.03.26 16:46, Markus Armbruster wrote: >>> Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> writes: >>> >>>> 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. >>>> >>>> For new option require explicitly unset script and downscript, >>>> to keep possibility of implementing support for them in future. >>>> >>>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> >>> [...] >>> >>>> diff --git a/qapi/net.json b/qapi/net.json >>>> index 118bd349651..79f5ce9f431 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) >>> "resources"? The name @incoming-fds suggests this is about file >>> descriptors... >> >> Hmm. But, I can't say "open any file descriptors".. >> >> >>> "wait for TAP state from" does not make sense. Do you mean "retrieve >>> TAP state from"? >> >> Retrieve means actively do something, but I mean, that FDs will come in >> future from incoming migration stream, and we just wait for it and "do nothing" >> at the moment of net-tap initialization. >> >> >> Let me try to reword: >> >> Do not open or create any TAP devices. Prepare for getting opened >> TAP file descriptors from incoming migration stream. > > Better, thanks! > >>> Is any part of NetdevTapOptions ignored when @incoming-fds is true? >> >> Hmm yes. sndbuf, vnet_hdr and ifname. I should add this information here. > > I dislike ignoring the user's instructions silently. Can we make it an > error? > Agree, will do. -- Best regards, Vladimir
© 2016 - 2026 Red Hat, Inc.