[PATCH v12 6/8] net/tap: support local migration with virtio-net

Vladimir Sementsov-Ogievskiy posted 8 patches 1 month, 1 week ago
There is a newer version of this series
[PATCH v12 6/8] net/tap: support local migration with virtio-net
Posted by Vladimir Sementsov-Ogievskiy 1 month, 1 week ago
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
Re: [PATCH v12 6/8] net/tap: support local migration with virtio-net
Posted by Markus Armbruster 1 month ago
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:
Re: [PATCH v12 6/8] net/tap: support local migration with virtio-net
Posted by Vladimir Sementsov-Ogievskiy 1 month ago
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
Re: [PATCH v12 6/8] net/tap: support local migration with virtio-net
Posted by Markus Armbruster 1 month ago
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?

[...]
Re: [PATCH v12 6/8] net/tap: support local migration with virtio-net
Posted by Vladimir Sementsov-Ogievskiy 1 month ago
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