* [PATCH v18 01/14] net/tap: rework tap_parse_script
2026-07-10 19:17 [PATCH v18 00/14] virtio-net: live-TAP local migration Vladimir Sementsov-Ogievskiy
@ 2026-07-10 19:17 ` Vladimir Sementsov-Ogievskiy
2026-07-10 19:17 ` [PATCH v18 02/14] net/tap: improve script/downscript options documentation Vladimir Sementsov-Ogievskiy
` (12 subsequent siblings)
13 siblings, 0 replies; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-10 19:17 UTC (permalink / raw)
To: jasowang, mst
Cc: armbru, peterx, farosas, raphael.s.norwitz, bchaney, vsementsov,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Jason Wang
Factor out tap_is_explicit_no_script() helper, to simplify
further changes.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
---
net/tap.c | 27 +++++++++++++++++++++------
1 file changed, 21 insertions(+), 6 deletions(-)
diff --git a/net/tap.c b/net/tap.c
index 57ffb09885c..fedd48c48d2 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -92,19 +92,34 @@ static void launch_script(const char *setup_script, const char *ifname,
static void tap_send(void *opaque);
static void tap_writable(void *opaque);
-static char *tap_parse_script(const char *script_arg, const char *default_path)
+static bool tap_is_explicit_no_script(const char *script_arg)
{
- g_autofree char *res = g_strdup(script_arg);
+ if (!script_arg) {
+ return false;
+ }
- if (!res) {
- res = get_relocated_path(default_path);
+ if (script_arg[0] == '\0') {
+ return true;
+ }
+
+ if (strcmp(script_arg, "no") == 0) {
+ return true;
}
- if (res[0] == '\0' || strcmp(res, "no") == 0) {
+ return false;
+}
+
+static char *tap_parse_script(const char *script_arg, const char *default_path)
+{
+ if (tap_is_explicit_no_script(script_arg)) {
return NULL;
}
- return g_steal_pointer(&res);
+ if (!script_arg) {
+ return get_relocated_path(default_path);
+ }
+
+ return g_strdup(script_arg);
}
static void tap_update_fd_handler(TAPState *s)
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* [PATCH v18 02/14] net/tap: improve script/downscript options documentation
2026-07-10 19:17 [PATCH v18 00/14] virtio-net: live-TAP local migration Vladimir Sementsov-Ogievskiy
2026-07-10 19:17 ` [PATCH v18 01/14] net/tap: rework tap_parse_script Vladimir Sementsov-Ogievskiy
@ 2026-07-10 19:17 ` Vladimir Sementsov-Ogievskiy
2026-07-13 7:33 ` Markus Armbruster
2026-07-10 19:17 ` [PATCH v18 03/14] net/tap: deprecate "no" as special value for script/downscript Vladimir Sementsov-Ogievskiy
` (11 subsequent siblings)
13 siblings, 1 reply; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-10 19:17 UTC (permalink / raw)
To: jasowang, mst
Cc: armbru, peterx, farosas, raphael.s.norwitz, bchaney, vsementsov,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Jason Wang, Eric Blake
Properly document defaults and special values of "" and "no".
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
---
qapi/net.json | 12 +++++++++---
qemu-options.hx | 9 +++++----
2 files changed, 14 insertions(+), 7 deletions(-)
diff --git a/qapi/net.json b/qapi/net.json
index 1a6382825c5..8f0915c4d86 100644
--- a/qapi/net.json
+++ b/qapi/net.json
@@ -399,9 +399,15 @@
# @fds: multiple file descriptors of already opened multiqueue capable
# tap
#
-# @script: script to initialize the interface
-#
-# @downscript: script to shut down the interface
+# @script: script to initialize the interface. An empty string or
+# "no" disables script execution. Defaults to
+# ``<sysconfdir>/qemu-ifup``, where ``<sysconfdir>`` is the
+# system configuration directory at build time (typically /etc).
+#
+# @downscript: script to shut down the interface. An empty string or
+# "no" disables script execution. Defaults to
+# ``<sysconfdir>/qemu-ifdown``, where ``<sysconfdir>`` is the
+# system configuration directory at build time (typically /etc).
#
# @br: bridge name (since 2.8)
#
diff --git a/qemu-options.hx b/qemu-options.hx
index 34970fffc94..200949655ea 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3014,7 +3014,7 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev,
" use network scripts 'file' (default=" DEFAULT_NETWORK_SCRIPT ")\n"
" to configure it and 'dfile' (default=" DEFAULT_NETWORK_DOWN_SCRIPT ")\n"
" to deconfigure it\n"
- " use '[down]script=no' to disable script execution\n"
+ " use '[down]script=no' or '[down]script=' to disable script execution\n"
" use network helper 'helper' (default=" DEFAULT_BRIDGE_HELPER ") to\n"
" configure it\n"
" use 'fd=h' to connect to an already opened TAP interface\n"
@@ -3550,9 +3550,10 @@ SRST
Use the network script file to configure it and the network script
dfile to deconfigure it. If name is not provided, the OS
automatically provides one. The default network configure script is
- ``/etc/qemu-ifup`` and the default network deconfigure script is
- ``/etc/qemu-ifdown``. Use ``script=no`` or ``downscript=no`` to
- disable script execution.
+ ``<sysconfdir>/qemu-ifup`` and the default network deconfigure script is
+ ``<sysconfdir>/qemu-ifdown``, where ``<sysconfdir>`` is the system
+ configuration directory at build time (typically ``/etc``).
+ Use ``[down]script=no`` or ``[down]script=`` to disable script execution.
If running QEMU as an unprivileged user, use the network helper
to configure the TAP interface and attach it to the bridge.
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* Re: [PATCH v18 02/14] net/tap: improve script/downscript options documentation
2026-07-10 19:17 ` [PATCH v18 02/14] net/tap: improve script/downscript options documentation Vladimir Sementsov-Ogievskiy
@ 2026-07-13 7:33 ` Markus Armbruster
0 siblings, 0 replies; 44+ messages in thread
From: Markus Armbruster @ 2026-07-13 7:33 UTC (permalink / raw)
To: Vladimir Sementsov-Ogievskiy
Cc: jasowang, mst, peterx, farosas, raphael.s.norwitz, bchaney,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Jason Wang, Eric Blake
Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> writes:
> Properly document defaults and special values of "" and "no".
>
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
^ permalink raw reply [flat|nested] 44+ messages in thread
* [PATCH v18 03/14] net/tap: deprecate "no" as special value for script/downscript
2026-07-10 19:17 [PATCH v18 00/14] virtio-net: live-TAP local migration Vladimir Sementsov-Ogievskiy
2026-07-10 19:17 ` [PATCH v18 01/14] net/tap: rework tap_parse_script Vladimir Sementsov-Ogievskiy
2026-07-10 19:17 ` [PATCH v18 02/14] net/tap: improve script/downscript options documentation Vladimir Sementsov-Ogievskiy
@ 2026-07-10 19:17 ` Vladimir Sementsov-Ogievskiy
2026-07-13 7:45 ` Markus Armbruster
2026-07-10 19:17 ` [PATCH v18 04/14] net/tap: move vhost-net open() calls to tap_parse_vhost_fds() Vladimir Sementsov-Ogievskiy
` (10 subsequent siblings)
13 siblings, 1 reply; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-10 19:17 UTC (permalink / raw)
To: jasowang, mst
Cc: armbru, peterx, farosas, raphael.s.norwitz, bchaney, vsementsov,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Pierrick Bouvier, Sergio Lopez, Zhao Liu, Stefano Stabellini,
Anthony PERARD, Edgar E. Iglesias, Bernhard Beschow,
Alistair Francis, Palmer Dabbelt, Jason Wang, Eric Blake,
open list:Incompatible changes, open list:X86 Xen CPUs,
open list:e500, open list:Microchip PolarFi...
The interface is ambiguous, as "no" is valid file name. So,
using "no" as a special value to disable script is deprecated.
Use an empty string ("script=" / "downscript=") instead.
In a future version, "no" will be treated as a plain file name, just
like any other non-empty value.
Document the deprecation in docs/about/deprecated.rst, qapi/net.json,
and qemu-options.hx. Update other docs to use empty string instead of
"no". Add a warning.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
---
docs/about/deprecated.rst | 18 ++++++++++++++++++
docs/system/i386/microvm.rst | 4 ++--
docs/system/i386/xenpvh.rst | 2 +-
docs/system/ppc/ppce500.rst | 4 ++--
docs/system/riscv/microchip-icicle-kit.rst | 2 +-
docs/system/riscv/sifive_u.rst | 2 +-
net/tap.c | 2 ++
qapi/net.json | 12 ++++++++----
qemu-options.hx | 7 +++++--
9 files changed, 40 insertions(+), 13 deletions(-)
diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst
index 0c656a968fc..c4929317e3a 100644
--- a/docs/about/deprecated.rst
+++ b/docs/about/deprecated.rst
@@ -71,6 +71,15 @@ flexible enough. The monitor objects have been converted to QOM, so
``-mon mode=control`` is replaced by ``-object monitor-qmp``. The
short convenience options are not deprecated, only ``-mon``.
+``script=no`` and ``downscript=no`` for ``-netdev tap`` (since 11.2)
+'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+The special value ``"no"`` for the ``script`` and ``downscript``
+parameters of ``-netdev tap`` disables script execution. This special
+treatment of ``"no"`` is deprecated. Use an empty string (``script=``
+or ``downscript=``) to disable script execution instead. In a future
+version, ``"no"`` will be treated as a plain file name.
+
QEMU Machine Protocol (QMP) commands
------------------------------------
@@ -164,6 +173,15 @@ Use ``job-finalize`` instead.
Use ``query-accelerators`` instead.
+``"no"`` as value of ``script``/``downscript`` for tap in ``netdev_add`` (since 11.2)
+'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+The special value ``"no"`` for the ``script`` and ``downscript``
+parameters of ``netdev_add`` with ``type=tap`` disables script
+execution. This special treatment of ``"no"`` is deprecated. Use an
+empty string instead. In a future version, ``"no"`` will be treated as
+a plain file name.
+
Human Machine Protocol (HMP) commands
-------------------------------------
diff --git a/docs/system/i386/microvm.rst b/docs/system/i386/microvm.rst
index 1675e37d3e7..077ea15751e 100644
--- a/docs/system/i386/microvm.rst
+++ b/docs/system/i386/microvm.rst
@@ -79,7 +79,7 @@ legacy ``ISA serial`` device as console::
-serial stdio \
-drive id=test,file=test.img,format=raw,if=none \
-device virtio-blk-device,drive=test \
- -netdev tap,id=tap0,script=no,downscript=no \
+ -netdev tap,id=tap0,script=,downscript= \
-device virtio-net-device,netdev=tap0
While the example above works, you might be interested in reducing the
@@ -103,7 +103,7 @@ disabled::
-device virtconsole,chardev=virtiocon0 \
-drive id=test,file=test.img,format=raw,if=none \
-device virtio-blk-device,drive=test \
- -netdev tap,id=tap0,script=no,downscript=no \
+ -netdev tap,id=tap0,script=,downscript= \
-device virtio-net-device,netdev=tap0
diff --git a/docs/system/i386/xenpvh.rst b/docs/system/i386/xenpvh.rst
index 904778e3f5c..862f38830b1 100644
--- a/docs/system/i386/xenpvh.rst
+++ b/docs/system/i386/xenpvh.rst
@@ -42,7 +42,7 @@ case you need to construct one manually:
-vnc none \
-display none \
-device virtio-net-pci,id=nic0,netdev=net0,mac=00:16:3e:5c:81:78 \
- -netdev type=tap,id=net0,ifname=vif3.0-emu,br=xenbr0,script=no,downscript=no \
+ -netdev type=tap,id=net0,ifname=vif3.0-emu,br=xenbr0,script=,downscript= \
-smp 4,maxcpus=4 \
-nographic \
-machine xenpvh,ram-low-base=0,ram-low-size=2147483648,ram-high-base=4294967296,ram-high-size=2147483648,pci-ecam-base=824633720832,pci-ecam-size=268435456,pci-mmio-base=4026531840,pci-mmio-size=33554432,pci-mmio-high-base=824902156288,pci-mmio-high-size=68719476736 \
diff --git a/docs/system/ppc/ppce500.rst b/docs/system/ppc/ppce500.rst
index c9fe0915dc5..ec5aaf14fd9 100644
--- a/docs/system/ppc/ppce500.rst
+++ b/docs/system/ppc/ppce500.rst
@@ -158,14 +158,14 @@ interface at PCI address 0.1.0, but we can switch that to an e1000 NIC by:
$ qemu-system-ppc64 -M ppce500 -smp 4 -m 2G \
-display none -serial stdio \
-bios u-boot \
- -nic tap,ifname=tap0,script=no,downscript=no,model=e1000
+ -nic tap,ifname=tap0,script=,downscript=,model=e1000
The QEMU ``ppce500`` machine can also dynamically instantiate an eTSEC device
if “-device eTSEC” is given to QEMU:
.. code-block:: bash
- -netdev tap,ifname=tap0,script=no,downscript=no,id=net0 -device eTSEC,netdev=net0
+ -netdev tap,ifname=tap0,script=,downscript=,id=net0 -device eTSEC,netdev=net0
Root file system on flash drive
-------------------------------
diff --git a/docs/system/riscv/microchip-icicle-kit.rst b/docs/system/riscv/microchip-icicle-kit.rst
index 9809e94b84b..7fdb96601ad 100644
--- a/docs/system/riscv/microchip-icicle-kit.rst
+++ b/docs/system/riscv/microchip-icicle-kit.rst
@@ -84,7 +84,7 @@ Then we can boot the machine by:
$ qemu-system-riscv64 -M microchip-icicle-kit -smp 5 -m 2G \
-sd path/to/sdcard.img \
-nic user,model=cadence_gem \
- -nic tap,ifname=tap,model=cadence_gem,script=no \
+ -nic tap,ifname=tap,model=cadence_gem,script= \
-display none -serial stdio \
-kernel path/to/u-boot/build/dir/u-boot.bin \
-dtb path/to/u-boot/build/dir/u-boot.dtb
diff --git a/docs/system/riscv/sifive_u.rst b/docs/system/riscv/sifive_u.rst
index 8f55ae8e313..0e4dcf3e70c 100644
--- a/docs/system/riscv/sifive_u.rst
+++ b/docs/system/riscv/sifive_u.rst
@@ -199,7 +199,7 @@ To boot the VxWorks kernel in QEMU with the ``sifive_u`` machine, use:
$ qemu-system-riscv64 -M sifive_u -smp 5 -m 2G \
-display none -serial stdio \
- -nic tap,ifname=tap0,script=no,downscript=no \
+ -nic tap,ifname=tap0,script=,downscript= \
-kernel /path/to/vxWorks \
-append "gem(0,0)host:vxWorks h=192.168.200.1 e=192.168.200.2:ffffff00 u=target pw=vxTarget f=0x01"
diff --git a/net/tap.c b/net/tap.c
index fedd48c48d2..1ec9e5ebe6f 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -103,6 +103,8 @@ static bool tap_is_explicit_no_script(const char *script_arg)
}
if (strcmp(script_arg, "no") == 0) {
+ warn_report("script=no/downscript=no is deprecated; "
+ "use script=/downscript= instead");
return true;
}
diff --git a/qapi/net.json b/qapi/net.json
index 8f0915c4d86..ada0329ef9d 100644
--- a/qapi/net.json
+++ b/qapi/net.json
@@ -399,15 +399,19 @@
# @fds: multiple file descriptors of already opened multiqueue capable
# tap
#
-# @script: script to initialize the interface. An empty string or
-# "no" disables script execution. Defaults to
+# @script: script to initialize the interface. An empty string
+# disables script execution. Defaults to
# ``<sysconfdir>/qemu-ifup``, where ``<sysconfdir>`` is the
# system configuration directory at build time (typically /etc).
+# Using "no" to disable script execution is deprecated (since
+# 11.2); use an empty string instead.
#
-# @downscript: script to shut down the interface. An empty string or
-# "no" disables script execution. Defaults to
+# @downscript: script to shut down the interface. An empty string
+# disables script execution. Defaults to
# ``<sysconfdir>/qemu-ifdown``, where ``<sysconfdir>`` is the
# system configuration directory at build time (typically /etc).
+# Using "no" to disable script execution is deprecated (since
+# 11.2); use an empty string instead.
#
# @br: bridge name (since 2.8)
#
diff --git a/qemu-options.hx b/qemu-options.hx
index 200949655ea..7b9a14f5b38 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3014,7 +3014,8 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev,
" use network scripts 'file' (default=" DEFAULT_NETWORK_SCRIPT ")\n"
" to configure it and 'dfile' (default=" DEFAULT_NETWORK_DOWN_SCRIPT ")\n"
" to deconfigure it\n"
- " use '[down]script=no' or '[down]script=' to disable script execution\n"
+ " use '[down]script=' to disable script execution\n"
+ " ('[down]script=no' is deprecated and will be treated as a file name in future)\n"
" use network helper 'helper' (default=" DEFAULT_BRIDGE_HELPER ") to\n"
" configure it\n"
" use 'fd=h' to connect to an already opened TAP interface\n"
@@ -3553,7 +3554,9 @@ SRST
``<sysconfdir>/qemu-ifup`` and the default network deconfigure script is
``<sysconfdir>/qemu-ifdown``, where ``<sysconfdir>`` is the system
configuration directory at build time (typically ``/etc``).
- Use ``[down]script=no`` or ``[down]script=`` to disable script execution.
+ Use ``[down]script=`` to disable script execution.
+ Using ``[down]script=no`` is deprecated; in a future version it will
+ be treated as a plain file name.
If running QEMU as an unprivileged user, use the network helper
to configure the TAP interface and attach it to the bridge.
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* Re: [PATCH v18 03/14] net/tap: deprecate "no" as special value for script/downscript
2026-07-10 19:17 ` [PATCH v18 03/14] net/tap: deprecate "no" as special value for script/downscript Vladimir Sementsov-Ogievskiy
@ 2026-07-13 7:45 ` Markus Armbruster
2026-07-13 9:37 ` Vladimir Sementsov-Ogievskiy
0 siblings, 1 reply; 44+ messages in thread
From: Markus Armbruster @ 2026-07-13 7:45 UTC (permalink / raw)
To: Vladimir Sementsov-Ogievskiy
Cc: jasowang, mst, peterx, farosas, raphael.s.norwitz, bchaney,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Pierrick Bouvier, Sergio Lopez, Zhao Liu, Stefano Stabellini,
Anthony PERARD, Edgar E. Iglesias, Bernhard Beschow,
Alistair Francis, Palmer Dabbelt, Jason Wang, Eric Blake,
open list:Incompatible changes, open list:X86 Xen CPUs,
open list:e500, open list:Microchip PolarFi...
Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> writes:
> The interface is ambiguous, as "no" is valid file name. So,
> using "no" as a special value to disable script is deprecated.
> Use an empty string ("script=" / "downscript=") instead.
>
> In a future version, "no" will be treated as a plain file name, just
> like any other non-empty value.
>
> Document the deprecation in docs/about/deprecated.rst, qapi/net.json,
> and qemu-options.hx. Update other docs to use empty string instead of
> "no". Add a warning.
>
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
[...]
> diff --git a/net/tap.c b/net/tap.c
> index fedd48c48d2..1ec9e5ebe6f 100644
> --- a/net/tap.c
> +++ b/net/tap.c
> @@ -103,6 +103,8 @@ static bool tap_is_explicit_no_script(const char *script_arg)
> }
>
> if (strcmp(script_arg, "no") == 0) {
> + warn_report("script=no/downscript=no is deprecated; "
> + "use script=/downscript= instead");
net_init_tap() could pass the parameter name for a simpler error
message. Probably not worth the bother.
> return true;
> }
>
In net_init_tap_one():
qemu_set_info_str(&s->nc, "ifname=%s,script=%s,downscript=%s", ifname,
script ?: "no", downscript ?: "no");
This sets nc->info_str, which is shown by "info network". Should we
replace "no" by ""?
[...]
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 03/14] net/tap: deprecate "no" as special value for script/downscript
2026-07-13 7:45 ` Markus Armbruster
@ 2026-07-13 9:37 ` Vladimir Sementsov-Ogievskiy
2026-07-13 9:47 ` Daniel P. Berrangé
0 siblings, 1 reply; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-13 9:37 UTC (permalink / raw)
To: Markus Armbruster
Cc: jasowang, mst, peterx, farosas, raphael.s.norwitz, bchaney,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Pierrick Bouvier, Sergio Lopez, Zhao Liu, Stefano Stabellini,
Anthony PERARD, Edgar E. Iglesias, Bernhard Beschow,
Alistair Francis, Palmer Dabbelt, Jason Wang, Eric Blake,
open list:Incompatible changes, open list:X86 Xen CPUs,
open list:e500, open list:Microchip PolarFi...
On 13.07.26 10:45, Markus Armbruster wrote:
> Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> writes:
>
>> The interface is ambiguous, as "no" is valid file name. So,
>> using "no" as a special value to disable script is deprecated.
>> Use an empty string ("script=" / "downscript=") instead.
>>
>> In a future version, "no" will be treated as a plain file name, just
>> like any other non-empty value.
>>
>> Document the deprecation in docs/about/deprecated.rst, qapi/net.json,
>> and qemu-options.hx. Update other docs to use empty string instead of
>> "no". Add a warning.
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
>
> [...]
>
>> diff --git a/net/tap.c b/net/tap.c
>> index fedd48c48d2..1ec9e5ebe6f 100644
>> --- a/net/tap.c
>> +++ b/net/tap.c
>> @@ -103,6 +103,8 @@ static bool tap_is_explicit_no_script(const char *script_arg)
>> }
>>
>> if (strcmp(script_arg, "no") == 0) {
>> + warn_report("script=no/downscript=no is deprecated; "
>> + "use script=/downscript= instead");
>
> net_init_tap() could pass the parameter name for a simpler error
> message. Probably not worth the bother.
>
>> return true;
>> }
>>
>
> In net_init_tap_one():
>
> qemu_set_info_str(&s->nc, "ifname=%s,script=%s,downscript=%s", ifname,
> script ?: "no", downscript ?: "no");
>
> This sets nc->info_str, which is shown by "info network". Should we
> replace "no" by ""?
>
Agree, will do.
--
Best regards,
Vladimir
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 03/14] net/tap: deprecate "no" as special value for script/downscript
2026-07-13 9:37 ` Vladimir Sementsov-Ogievskiy
@ 2026-07-13 9:47 ` Daniel P. Berrangé
2026-07-13 10:25 ` Vladimir Sementsov-Ogievskiy
0 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2026-07-13 9:47 UTC (permalink / raw)
To: Vladimir Sementsov-Ogievskiy
Cc: Markus Armbruster, jasowang, mst, peterx, farosas,
raphael.s.norwitz, bchaney, qemu-devel, pbonzini, yc-core,
mark.caveayland, Pierrick Bouvier, Sergio Lopez, Zhao Liu,
Stefano Stabellini, Anthony PERARD, Edgar E. Iglesias,
Bernhard Beschow, Alistair Francis, Palmer Dabbelt, Jason Wang,
Eric Blake, open list:Incompatible changes,
open list:X86 Xen CPUs, open list:e500,
open list:Microchip PolarFi...
On Mon, Jul 13, 2026 at 12:37:54PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> On 13.07.26 10:45, Markus Armbruster wrote:
> > Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> writes:
> >
> > > The interface is ambiguous, as "no" is valid file name. So,
> > > using "no" as a special value to disable script is deprecated.
> > > Use an empty string ("script=" / "downscript=") instead.
> > >
> > > In a future version, "no" will be treated as a plain file name, just
> > > like any other non-empty value.
> > >
> > > Document the deprecation in docs/about/deprecated.rst, qapi/net.json,
> > > and qemu-options.hx. Update other docs to use empty string instead of
> > > "no". Add a warning.
> > >
> > > Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
> >
> > [...]
> >
> > > diff --git a/net/tap.c b/net/tap.c
> > > index fedd48c48d2..1ec9e5ebe6f 100644
> > > --- a/net/tap.c
> > > +++ b/net/tap.c
> > > @@ -103,6 +103,8 @@ static bool tap_is_explicit_no_script(const char *script_arg)
> > > }
> > > if (strcmp(script_arg, "no") == 0) {
> > > + warn_report("script=no/downscript=no is deprecated; "
> > > + "use script=/downscript= instead");
> >
> > net_init_tap() could pass the parameter name for a simpler error
> > message. Probably not worth the bother.
> >
> > > return true;
> > > }
> >
> > In net_init_tap_one():
> >
> > qemu_set_info_str(&s->nc, "ifname=%s,script=%s,downscript=%s", ifname,
> > script ?: "no", downscript ?: "no");
> >
> > This sets nc->info_str, which is shown by "info network". Should we
> > replace "no" by ""?
> >
>
> Agree, will do.
Would it be better to not include the script/downscript parameters at
all if they're disabled ?
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 03/14] net/tap: deprecate "no" as special value for script/downscript
2026-07-13 9:47 ` Daniel P. Berrangé
@ 2026-07-13 10:25 ` Vladimir Sementsov-Ogievskiy
0 siblings, 0 replies; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-13 10:25 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: Markus Armbruster, jasowang, mst, peterx, farosas,
raphael.s.norwitz, bchaney, qemu-devel, pbonzini, yc-core,
mark.caveayland, Pierrick Bouvier, Sergio Lopez, Zhao Liu,
Stefano Stabellini, Anthony PERARD, Edgar E. Iglesias,
Bernhard Beschow, Alistair Francis, Palmer Dabbelt, Jason Wang,
Eric Blake, open list:Incompatible changes,
open list:X86 Xen CPUs, open list:e500,
open list:Microchip PolarFi...
On 13.07.26 12:47, Daniel P. Berrangé wrote:
> On Mon, Jul 13, 2026 at 12:37:54PM +0300, Vladimir Sementsov-Ogievskiy wrote:
>> On 13.07.26 10:45, Markus Armbruster wrote:
>>> Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> writes:
>>>
>>>> The interface is ambiguous, as "no" is valid file name. So,
>>>> using "no" as a special value to disable script is deprecated.
>>>> Use an empty string ("script=" / "downscript=") instead.
>>>>
>>>> In a future version, "no" will be treated as a plain file name, just
>>>> like any other non-empty value.
>>>>
>>>> Document the deprecation in docs/about/deprecated.rst, qapi/net.json,
>>>> and qemu-options.hx. Update other docs to use empty string instead of
>>>> "no". Add a warning.
>>>>
>>>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
>>>
>>> [...]
>>>
>>>> diff --git a/net/tap.c b/net/tap.c
>>>> index fedd48c48d2..1ec9e5ebe6f 100644
>>>> --- a/net/tap.c
>>>> +++ b/net/tap.c
>>>> @@ -103,6 +103,8 @@ static bool tap_is_explicit_no_script(const char *script_arg)
>>>> }
>>>> if (strcmp(script_arg, "no") == 0) {
>>>> + warn_report("script=no/downscript=no is deprecated; "
>>>> + "use script=/downscript= instead");
>>>
>>> net_init_tap() could pass the parameter name for a simpler error
>>> message. Probably not worth the bother.
>>>
>>>> return true;
>>>> }
>>>
>>> In net_init_tap_one():
>>>
>>> qemu_set_info_str(&s->nc, "ifname=%s,script=%s,downscript=%s", ifname,
>>> script ?: "no", downscript ?: "no");
>>>
>>> This sets nc->info_str, which is shown by "info network". Should we
>>> replace "no" by ""?
>>>
>>
>> Agree, will do.
>
> Would it be better to not include the script/downscript parameters at
> all if they're disabled ?
>
Unfortunately current default is: use <sysconfdir>/qemu-ifup and
<sysconfdir>/qemu-ifdown if these options are unset. So, omitting
them in info-string would be ambiguous.
--
Best regards,
Vladimir
^ permalink raw reply [flat|nested] 44+ messages in thread
* [PATCH v18 04/14] net/tap: move vhost-net open() calls to tap_parse_vhost_fds()
2026-07-10 19:17 [PATCH v18 00/14] virtio-net: live-TAP local migration Vladimir Sementsov-Ogievskiy
` (2 preceding siblings ...)
2026-07-10 19:17 ` [PATCH v18 03/14] net/tap: deprecate "no" as special value for script/downscript Vladimir Sementsov-Ogievskiy
@ 2026-07-10 19:17 ` Vladimir Sementsov-Ogievskiy
2026-07-10 19:17 ` [PATCH v18 05/14] net/tap: move vhost initialization to tap_setup_vhost() Vladimir Sementsov-Ogievskiy
` (9 subsequent siblings)
13 siblings, 0 replies; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-10 19:17 UTC (permalink / raw)
To: jasowang, mst
Cc: armbru, peterx, farosas, raphael.s.norwitz, bchaney, vsementsov,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Jason Wang
1. Simplify code path: get vhostfds for all cases in one function.
2. Prepare for further tap-fd-migraton feature, when we'll need to
postpone vhost initialization up to post-load stage.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Reviewed-by: Ben Chaney <bchaney@akamai.com>
---
net/tap.c | 39 ++++++++++++++++++++++-----------------
1 file changed, 22 insertions(+), 17 deletions(-)
diff --git a/net/tap.c b/net/tap.c
index 1ec9e5ebe6f..2f0ccc138da 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -753,8 +753,7 @@ static bool net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
}
}
- if (tap->has_vhost ? tap->vhost :
- (vhostfd != -1) || (tap->has_vhostforce && tap->vhostforce)) {
+ if (vhostfd != -1) {
VhostNetOptions options;
options.backend_type = VHOST_BACKEND_TYPE_KERNEL;
@@ -764,17 +763,6 @@ static bool net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
} else {
options.busyloop_timeout = 0;
}
-
- if (vhostfd == -1) {
- vhostfd = open("/dev/vhost-net", O_RDWR);
- if (vhostfd < 0) {
- error_setg_file_open(errp, errno, "/dev/vhost-net");
- goto failed;
- }
- if (!qemu_set_blocking(vhostfd, false, errp)) {
- goto failed;
- }
- }
options.opaque = (void *)(uintptr_t)vhostfd;
options.nvqs = 2;
options.feature_bits = kernel_feature_bits;
@@ -860,14 +848,31 @@ static int tap_parse_fds_and_queues(const NetdevTapOptions *tap, int **fds,
static bool tap_parse_vhost_fds(const NetdevTapOptions *tap, int **vhost_fds,
int queues, Error **errp)
{
- if (!(tap->vhostfd || tap->vhostfds)) {
+ bool need_vhost = tap->has_vhost ? tap->vhost :
+ ((tap->vhostfd || tap->vhostfds) ||
+ (tap->has_vhostforce && tap->vhostforce));
+
+ if (!need_vhost) {
*vhost_fds = NULL;
return true;
}
- if (net_parse_fds(tap->vhostfd ?: tap->vhostfds,
- vhost_fds, queues, errp) < 0) {
- return false;
+ if (tap->vhostfd || tap->vhostfds) {
+ if (net_parse_fds(tap->vhostfd ?: tap->vhostfds,
+ vhost_fds, queues, errp) < 0) {
+ return false;
+ }
+ } else {
+ *vhost_fds = g_new(int, queues);
+ for (int i = 0; i < queues; i++) {
+ int vhostfd = open("/dev/vhost-net", O_RDWR);
+ if (vhostfd < 0) {
+ error_setg_file_open(errp, errno, "/dev/vhost-net");
+ net_free_fds(*vhost_fds, i);
+ return false;
+ }
+ (*vhost_fds)[i] = vhostfd;
+ }
}
if (!unblock_fds(*vhost_fds, queues, errp)) {
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* [PATCH v18 05/14] net/tap: move vhost initialization to tap_setup_vhost()
2026-07-10 19:17 [PATCH v18 00/14] virtio-net: live-TAP local migration Vladimir Sementsov-Ogievskiy
` (3 preceding siblings ...)
2026-07-10 19:17 ` [PATCH v18 04/14] net/tap: move vhost-net open() calls to tap_parse_vhost_fds() Vladimir Sementsov-Ogievskiy
@ 2026-07-10 19:17 ` Vladimir Sementsov-Ogievskiy
2026-07-10 19:17 ` [PATCH v18 06/14] net/tap: use container_of instead of DO_UPCAST Vladimir Sementsov-Ogievskiy
` (8 subsequent siblings)
13 siblings, 0 replies; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-10 19:17 UTC (permalink / raw)
To: jasowang, mst
Cc: armbru, peterx, farosas, raphael.s.norwitz, bchaney, vsementsov,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Jason Wang
Make a new helper function in a way it can be reused later for
TAP fd-migration feature: we'll need to initialize vhost in a later
point when we doesn't have access to QAPI parameters.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Reviewed-by: Ben Chaney <bchaney@akamai.com>
---
net/tap.c | 62 ++++++++++++++++++++++++++++++++++---------------------
1 file changed, 38 insertions(+), 24 deletions(-)
diff --git a/net/tap.c b/net/tap.c
index 2f0ccc138da..bc269a2fc3b 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -71,6 +71,8 @@ static const int kernel_feature_bits[] = {
typedef struct TAPState {
NetClientState nc;
int fd;
+ int vhostfd;
+ uint32_t vhost_busyloop_timeout;
char down_script[1024];
char down_script_arg[128];
uint8_t buf[NET_BUFSIZE];
@@ -719,6 +721,38 @@ static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr,
return fd;
}
+static bool tap_setup_vhost(TAPState *s, Error **errp)
+{
+ VhostNetOptions options;
+
+ if (s->vhostfd == -1) {
+ return true;
+ }
+
+ options.backend_type = VHOST_BACKEND_TYPE_KERNEL;
+ options.net_backend = &s->nc;
+ options.busyloop_timeout = s->vhost_busyloop_timeout;
+ options.opaque = (void *)(uintptr_t)s->vhostfd;
+ options.nvqs = 2;
+ options.feature_bits = kernel_feature_bits;
+ options.get_acked_features = NULL;
+ options.save_acked_features = NULL;
+ options.max_tx_queue_size = 0;
+ options.is_vhost_user = false;
+
+ s->vhost_net = vhost_net_init(&options);
+ if (!s->vhost_net) {
+ error_setg(errp,
+ "vhost-net requested but could not be initialized");
+ return false;
+ }
+
+ /* vhostfd ownership is passed to s->vhost_net */
+ s->vhostfd = -1;
+
+ return true;
+}
+
static bool net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
const char *name,
const char *ifname, const char *script,
@@ -753,30 +787,10 @@ static bool net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
}
}
- if (vhostfd != -1) {
- VhostNetOptions options;
-
- options.backend_type = VHOST_BACKEND_TYPE_KERNEL;
- options.net_backend = &s->nc;
- if (tap->has_poll_us) {
- options.busyloop_timeout = tap->poll_us;
- } else {
- options.busyloop_timeout = 0;
- }
- options.opaque = (void *)(uintptr_t)vhostfd;
- options.nvqs = 2;
- options.feature_bits = kernel_feature_bits;
- options.get_acked_features = NULL;
- options.save_acked_features = NULL;
- options.max_tx_queue_size = 0;
- options.is_vhost_user = false;
-
- s->vhost_net = vhost_net_init(&options);
- if (!s->vhost_net) {
- error_setg(errp,
- "vhost-net requested but could not be initialized");
- goto failed;
- }
+ s->vhostfd = vhostfd;
+ s->vhost_busyloop_timeout = tap->has_poll_us ? tap->poll_us : 0;
+ if (!tap_setup_vhost(s, errp)) {
+ return false;
}
return true;
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* [PATCH v18 06/14] net/tap: use container_of instead of DO_UPCAST
2026-07-10 19:17 [PATCH v18 00/14] virtio-net: live-TAP local migration Vladimir Sementsov-Ogievskiy
` (4 preceding siblings ...)
2026-07-10 19:17 ` [PATCH v18 05/14] net/tap: move vhost initialization to tap_setup_vhost() Vladimir Sementsov-Ogievskiy
@ 2026-07-10 19:17 ` Vladimir Sementsov-Ogievskiy
2026-07-10 19:17 ` [PATCH v18 07/14] net/tap: QOMify tap backend Vladimir Sementsov-Ogievskiy
` (7 subsequent siblings)
13 siblings, 0 replies; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-10 19:17 UTC (permalink / raw)
To: jasowang, mst
Cc: armbru, peterx, farosas, raphael.s.norwitz, bchaney, vsementsov,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Jason Wang
We are going to QOMify tap backend, which includes deriving TAPState
from Object. So "NetClientState nc" will not be a first member.
Let's parepare for this change, and use container_of(), which will
work regardless position of "nc" field.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
---
net/tap.c | 36 ++++++++++++++++++------------------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/net/tap.c b/net/tap.c
index bc269a2fc3b..7380a62da80 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -172,7 +172,7 @@ static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt
static ssize_t tap_receive_iov(NetClientState *nc, const struct iovec *iov,
int iovcnt)
{
- TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ TAPState *s = container_of(nc, TAPState, nc);
const struct iovec *iovp = iov;
g_autofree struct iovec *iov_copy = NULL;
struct virtio_net_hdr hdr = { };
@@ -208,7 +208,7 @@ ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen)
static void tap_send_completed(NetClientState *nc, ssize_t len)
{
- TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ TAPState *s = container_of(nc, TAPState, nc);
tap_read_poll(s, true);
}
@@ -268,7 +268,7 @@ static void tap_send(void *opaque)
static bool tap_has_ufo(NetClientState *nc)
{
- TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ TAPState *s = container_of(nc, TAPState, nc);
assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
@@ -277,7 +277,7 @@ static bool tap_has_ufo(NetClientState *nc)
static bool tap_has_uso(NetClientState *nc)
{
- TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ TAPState *s = container_of(nc, TAPState, nc);
assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
@@ -286,7 +286,7 @@ static bool tap_has_uso(NetClientState *nc)
static bool tap_has_tunnel(NetClientState *nc)
{
- TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ TAPState *s = container_of(nc, TAPState, nc);
assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
return s->has_tunnel;
@@ -294,7 +294,7 @@ static bool tap_has_tunnel(NetClientState *nc)
static bool tap_has_vnet_hdr(NetClientState *nc)
{
- TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ TAPState *s = container_of(nc, TAPState, nc);
assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
@@ -308,7 +308,7 @@ static bool tap_has_vnet_hdr_len(NetClientState *nc, int len)
static void tap_set_vnet_hdr_len(NetClientState *nc, int len)
{
- TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ TAPState *s = container_of(nc, TAPState, nc);
assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
@@ -319,21 +319,21 @@ static void tap_set_vnet_hdr_len(NetClientState *nc, int len)
static int tap_set_vnet_le(NetClientState *nc, bool is_le)
{
- TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ TAPState *s = container_of(nc, TAPState, nc);
return tap_fd_set_vnet_le(s->fd, is_le);
}
static int tap_set_vnet_be(NetClientState *nc, bool is_be)
{
- TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ TAPState *s = container_of(nc, TAPState, nc);
return tap_fd_set_vnet_be(s->fd, is_be);
}
static void tap_set_offload(NetClientState *nc, const NetOffloads *ol)
{
- TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ TAPState *s = container_of(nc, TAPState, nc);
if (s->fd < 0) {
return;
}
@@ -354,7 +354,7 @@ static void tap_exit_notify(Notifier *notifier, void *data)
static void tap_cleanup(NetClientState *nc)
{
- TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ TAPState *s = container_of(nc, TAPState, nc);
if (s->vhost_net) {
vhost_net_cleanup(s->vhost_net);
@@ -378,14 +378,14 @@ static void tap_cleanup(NetClientState *nc)
static void tap_poll(NetClientState *nc, bool enable)
{
- TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ TAPState *s = container_of(nc, TAPState, nc);
tap_read_poll(s, enable);
tap_write_poll(s, enable);
}
static bool tap_set_steering_ebpf(NetClientState *nc, int prog_fd)
{
- TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ TAPState *s = container_of(nc, TAPState, nc);
assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
return tap_fd_set_steering_ebpf(s->fd, prog_fd) == 0;
@@ -393,7 +393,7 @@ static bool tap_set_steering_ebpf(NetClientState *nc, int prog_fd)
int tap_get_fd(NetClientState *nc)
{
- TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ TAPState *s = container_of(nc, TAPState, nc);
assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
return s->fd;
}
@@ -405,7 +405,7 @@ int tap_get_fd(NetClientState *nc)
*/
static VHostNetState *tap_get_vhost_net(NetClientState *nc)
{
- TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ TAPState *s = container_of(nc, TAPState, nc);
assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
return s->vhost_net;
}
@@ -444,7 +444,7 @@ static TAPState *net_tap_fd_init(NetClientState *peer,
nc = qemu_new_net_client(&net_tap_info, peer, model, name);
- s = DO_UPCAST(TAPState, nc, nc);
+ s = container_of(nc, TAPState, nc);
s->fd = fd;
s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0;
@@ -1012,7 +1012,7 @@ fail:
int tap_enable(NetClientState *nc)
{
- TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ TAPState *s = container_of(nc, TAPState, nc);
int ret;
if (s->enabled) {
@@ -1029,7 +1029,7 @@ int tap_enable(NetClientState *nc)
int tap_disable(NetClientState *nc)
{
- TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ TAPState *s = container_of(nc, TAPState, nc);
int ret;
if (s->enabled == 0) {
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* [PATCH v18 07/14] net/tap: QOMify tap backend
2026-07-10 19:17 [PATCH v18 00/14] virtio-net: live-TAP local migration Vladimir Sementsov-Ogievskiy
` (5 preceding siblings ...)
2026-07-10 19:17 ` [PATCH v18 06/14] net/tap: use container_of instead of DO_UPCAST Vladimir Sementsov-Ogievskiy
@ 2026-07-10 19:17 ` Vladimir Sementsov-Ogievskiy
2026-07-10 19:17 ` [PATCH v18 08/14] net/tap: add TYPE_VMSTATE_IF interface Vladimir Sementsov-Ogievskiy
` (6 subsequent siblings)
13 siblings, 0 replies; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-10 19:17 UTC (permalink / raw)
To: jasowang, mst
Cc: armbru, peterx, farosas, raphael.s.norwitz, bchaney, vsementsov,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Jason Wang
We prepare for being able to migrate TAP backend. We'll need a
user change-able property for it, which can be set from machine
type. So, let's QOMify it first.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
---
include/net/net.h | 7 +++++++
include/net/tap.h | 2 ++
net/net.c | 14 +++++++-------
net/tap.c | 48 +++++++++++++++++++++++++++++++++++++++--------
4 files changed, 56 insertions(+), 15 deletions(-)
diff --git a/include/net/net.h b/include/net/net.h
index 45bc86fc86b..bdf16bb3e64 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -160,6 +160,13 @@ char *qemu_mac_strdup_printf(const uint8_t *macaddr);
NetClientState *qemu_find_netdev(const char *id);
int qemu_find_net_clients_except(const char *id, NetClientState **ncs,
NetClientDriver type, int max);
+void qemu_net_client_setup(NetClientState *nc,
+ NetClientInfo *info,
+ NetClientState *peer,
+ const char *model,
+ const char *name,
+ NetClientDestructor *destructor,
+ bool is_datapath);
NetClientState *qemu_new_net_client(NetClientInfo *info,
NetClientState *peer,
const char *model,
diff --git a/include/net/tap.h b/include/net/tap.h
index 6f34f13eae4..268570571f4 100644
--- a/include/net/tap.h
+++ b/include/net/tap.h
@@ -28,6 +28,8 @@
#include "standard-headers/linux/virtio_net.h"
+#define TYPE_TAP_NETDEV "tap-netdev"
+
int tap_enable(NetClientState *nc);
int tap_disable(NetClientState *nc);
diff --git a/net/net.c b/net/net.c
index 2892f1730d1..a06558b75bf 100644
--- a/net/net.c
+++ b/net/net.c
@@ -261,13 +261,13 @@ static ssize_t qemu_deliver_packet_iov(NetClientState *sender,
int iovcnt,
void *opaque);
-static void qemu_net_client_setup(NetClientState *nc,
- NetClientInfo *info,
- NetClientState *peer,
- const char *model,
- const char *name,
- NetClientDestructor *destructor,
- bool is_datapath)
+void qemu_net_client_setup(NetClientState *nc,
+ NetClientInfo *info,
+ NetClientState *peer,
+ const char *model,
+ const char *name,
+ NetClientDestructor *destructor,
+ bool is_datapath)
{
nc->info = info;
nc->model = g_strdup(model);
diff --git a/net/tap.c b/net/tap.c
index 7380a62da80..c2f63308ff9 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -43,6 +43,7 @@
#include "qemu/main-loop.h"
#include "qemu/sockets.h"
#include "hw/virtio/vhost.h"
+#include "qom/object.h"
#include "net/tap.h"
#include "net/util.h"
@@ -68,7 +69,11 @@ static const int kernel_feature_bits[] = {
VHOST_INVALID_FEATURE_BIT
};
-typedef struct TAPState {
+OBJECT_DECLARE_SIMPLE_TYPE(TAPState, TAP_NETDEV)
+
+struct TAPState {
+ Object parent_obj;
+
NetClientState nc;
int fd;
int vhostfd;
@@ -86,7 +91,7 @@ typedef struct TAPState {
VHostNetState *vhost_net;
unsigned host_vnet_hdr_len;
Notifier exit;
-} TAPState;
+};
static void launch_script(const char *setup_script, const char *ifname,
int fd, Error **errp);
@@ -410,6 +415,19 @@ static VHostNetState *tap_get_vhost_net(NetClientState *nc)
return s->vhost_net;
}
+
+static const TypeInfo tap_netdev_info = {
+ .name = TYPE_TAP_NETDEV,
+ .parent = TYPE_OBJECT,
+ .instance_size = sizeof(TAPState),
+};
+
+static void tap_net_client_destructor(NetClientState *nc)
+{
+ TAPState *s = container_of(nc, TAPState, nc);
+ object_unref(OBJECT(s));
+}
+
/* fd support */
static NetClientInfo net_tap_info = {
@@ -432,6 +450,18 @@ static NetClientInfo net_tap_info = {
.get_vhost_net = tap_get_vhost_net,
};
+static TAPState *new_tap(NetClientState *peer,
+ const char *model,
+ const char *name)
+{
+ TAPState *s = TAP_NETDEV(object_new(TYPE_TAP_NETDEV));
+
+ qemu_net_client_setup(&s->nc, &net_tap_info, peer, model, name,
+ tap_net_client_destructor, true);
+
+ return s;
+}
+
static TAPState *net_tap_fd_init(NetClientState *peer,
const char *model,
const char *name,
@@ -439,12 +469,7 @@ static TAPState *net_tap_fd_init(NetClientState *peer,
int vnet_hdr)
{
NetOffloads ol = {};
- NetClientState *nc;
- TAPState *s;
-
- nc = qemu_new_net_client(&net_tap_info, peer, model, name);
-
- s = container_of(nc, TAPState, nc);
+ TAPState *s = new_tap(peer, model, name);
s->fd = fd;
s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0;
@@ -1044,3 +1069,10 @@ int tap_disable(NetClientState *nc)
return ret;
}
}
+
+static void tap_register_types(void)
+{
+ type_register_static(&tap_netdev_info);
+}
+
+type_init(tap_register_types)
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* [PATCH v18 08/14] net/tap: add TYPE_VMSTATE_IF interface
2026-07-10 19:17 [PATCH v18 00/14] virtio-net: live-TAP local migration Vladimir Sementsov-Ogievskiy
` (6 preceding siblings ...)
2026-07-10 19:17 ` [PATCH v18 07/14] net/tap: QOMify tap backend Vladimir Sementsov-Ogievskiy
@ 2026-07-10 19:17 ` Vladimir Sementsov-Ogievskiy
2026-07-10 19:17 ` [PATCH v18 09/14] qapi: add local migration parameter Vladimir Sementsov-Ogievskiy
` (5 subsequent siblings)
13 siblings, 0 replies; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-10 19:17 UTC (permalink / raw)
To: jasowang, mst
Cc: armbru, peterx, farosas, raphael.s.norwitz, bchaney, vsementsov,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Jason Wang
We'll need it to implement TAP backend live migration.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
---
net/tap.c | 43 ++++++++++++++++++++++++++++++++++---------
1 file changed, 34 insertions(+), 9 deletions(-)
diff --git a/net/tap.c b/net/tap.c
index c2f63308ff9..b5be456936d 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -43,7 +43,6 @@
#include "qemu/main-loop.h"
#include "qemu/sockets.h"
#include "hw/virtio/vhost.h"
-#include "qom/object.h"
#include "net/tap.h"
#include "net/util.h"
@@ -91,6 +90,8 @@ struct TAPState {
VHostNetState *vhost_net;
unsigned host_vnet_hdr_len;
Notifier exit;
+
+ int queue_index;
};
static void launch_script(const char *setup_script, const char *ifname,
@@ -416,10 +417,29 @@ static VHostNetState *tap_get_vhost_net(NetClientState *nc)
}
+static char *tap_vmstate_if_get_id(VMStateIf *obj)
+{
+ TAPState *s = TAP_NETDEV(obj);
+ char *res = g_strdup_printf("%s/%d", s->nc.name, s->queue_index);
+ return res;
+}
+
+static void tap_class_init(ObjectClass *klass, const void *data)
+{
+ VMStateIfClass *vc = VMSTATE_IF_CLASS(klass);
+
+ vc->get_id = tap_vmstate_if_get_id;
+}
+
static const TypeInfo tap_netdev_info = {
.name = TYPE_TAP_NETDEV,
.parent = TYPE_OBJECT,
.instance_size = sizeof(TAPState),
+ .class_init = tap_class_init,
+ .interfaces = (const InterfaceInfo[]) {
+ { TYPE_VMSTATE_IF },
+ { }
+ },
};
static void tap_net_client_destructor(NetClientState *nc)
@@ -452,13 +472,16 @@ static NetClientInfo net_tap_info = {
static TAPState *new_tap(NetClientState *peer,
const char *model,
- const char *name)
+ const char *name,
+ int queue_index)
{
TAPState *s = TAP_NETDEV(object_new(TYPE_TAP_NETDEV));
qemu_net_client_setup(&s->nc, &net_tap_info, peer, model, name,
tap_net_client_destructor, true);
+ s->queue_index = queue_index;
+
return s;
}
@@ -466,10 +489,11 @@ static TAPState *net_tap_fd_init(NetClientState *peer,
const char *model,
const char *name,
int fd,
- int vnet_hdr)
+ int vnet_hdr,
+ int queue_index)
{
NetOffloads ol = {};
- TAPState *s = new_tap(peer, model, name);
+ TAPState *s = new_tap(peer, model, name, queue_index);
s->fd = fd;
s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0;
@@ -706,7 +730,7 @@ int net_init_bridge(const Netdev *netdev, const char *name,
close(fd);
return -1;
}
- s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr);
+ s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr, 0);
qemu_set_info_str(&s->nc, "helper=%s,br=%s", helper, br);
@@ -782,10 +806,11 @@ static bool net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
const char *name,
const char *ifname, const char *script,
const char *downscript, int vhostfd,
- int vnet_hdr, int fd, Error **errp)
+ int vnet_hdr, int fd, int queue_index,
+ Error **errp)
{
TAPState *s = net_tap_fd_init(peer, tap->helper ? "bridge" : "tap",
- name, fd, vnet_hdr);
+ name, fd, vnet_hdr, queue_index);
bool sndbuf_required = tap->has_sndbuf;
int sndbuf =
(tap->has_sndbuf && tap->sndbuf) ? MIN(tap->sndbuf, INT_MAX) : INT_MAX;
@@ -986,7 +1011,7 @@ int net_init_tap(const Netdev *netdev, const char *name,
if (!net_init_tap_one(tap, peer, name, ifname,
NULL, NULL,
vhost_fds ? vhost_fds[i] : -1,
- vnet_hdr, fds[i], errp)) {
+ vnet_hdr, fds[i], i, errp)) {
goto fail;
}
}
@@ -1020,7 +1045,7 @@ int net_init_tap(const Netdev *netdev, const char *name,
i >= 1 ? NULL : script,
i >= 1 ? NULL : downscript,
vhost_fds ? vhost_fds[i] : -1,
- vnet_hdr, fd, errp)) {
+ vnet_hdr, fd, i, errp)) {
goto fail;
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* [PATCH v18 09/14] qapi: add local migration parameter
2026-07-10 19:17 [PATCH v18 00/14] virtio-net: live-TAP local migration Vladimir Sementsov-Ogievskiy
` (7 preceding siblings ...)
2026-07-10 19:17 ` [PATCH v18 08/14] net/tap: add TYPE_VMSTATE_IF interface Vladimir Sementsov-Ogievskiy
@ 2026-07-10 19:17 ` Vladimir Sementsov-Ogievskiy
2026-07-13 9:51 ` Daniel P. Berrangé
2026-07-10 19:17 ` [PATCH v18 10/14] virtio-net: support local migration of backend Vladimir Sementsov-Ogievskiy
` (4 subsequent siblings)
13 siblings, 1 reply; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-10 19:17 UTC (permalink / raw)
To: jasowang, mst
Cc: armbru, peterx, farosas, raphael.s.norwitz, bchaney, vsementsov,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Eric Blake
We are going to implement local-migration feature: some devices will be
able to transfer open file descriptors through migration stream (which
must UNIX domain socket for that purpose). This allows to transfer the
whole backend state without reconnecting and restarting the backend
service. For example, virtio-net will migrate its attached TAP netdev,
together with its connected file descriptors.
In this commit we introduce a migration parameter, which enables
the feature for devices that support it (none at the moment).
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Acked-by: Markus Armbruster <armbru@redhat.com>
Acked-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Ben Chaney <bchaney@akamai.com>
---
include/migration/misc.h | 2 ++
migration/options.c | 18 +++++++++++++++++-
qapi/migration.json | 12 ++++++++++--
3 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/include/migration/misc.h b/include/migration/misc.h
index 2b2fbb59a40..019717f4cda 100644
--- a/include/migration/misc.h
+++ b/include/migration/misc.h
@@ -161,4 +161,6 @@ bool multifd_join_device_state_save_threads(void);
void migration_request_switchover_ack_legacy(const char *requester);
+bool migrate_local(void);
+
#endif
diff --git a/migration/options.c b/migration/options.c
index dfce19405d4..5c439f6d724 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -14,6 +14,7 @@
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qemu/units.h"
+#include "qapi/util.h"
#include "exec/target_page.h"
#include "qapi/clone-visitor.h"
#include "qapi/error.h"
@@ -25,6 +26,7 @@
#include "migration/colo.h"
#include "migration/cpr.h"
#include "migration/misc.h"
+#include "migration/options.h"
#include "migration.h"
#include "migration-stats.h"
#include "qemu-file.h"
@@ -350,6 +352,12 @@ bool migrate_mapped_ram(void)
return s->capabilities[MIGRATION_CAPABILITY_MAPPED_RAM];
}
+bool migrate_local(void)
+{
+ MigrationState *s = migrate_get_current();
+ return s->parameters.local;
+}
+
bool migrate_ignore_shared(void)
{
MigrationState *s = migrate_get_current();
@@ -1085,7 +1093,7 @@ static void migrate_mark_all_params_present(MigrationParameters *p)
&p->has_announce_step, &p->has_block_bitmap_mapping,
&p->has_x_vcpu_dirty_limit_period, &p->has_vcpu_dirty_limit,
&p->has_mode, &p->has_zero_page_detection, &p->has_direct_io,
- &p->has_x_rdma_chunk_size, &p->has_cpr_exec_command,
+ &p->has_x_rdma_chunk_size, &p->has_cpr_exec_command, &p->has_local,
};
len = ARRAY_SIZE(has_fields);
@@ -1433,6 +1441,10 @@ static void migrate_params_test_apply(MigrationParameters *params,
qapi_free_strList(dest->cpr_exec_command);
dest->cpr_exec_command = QAPI_CLONE(strList, params->cpr_exec_command);
}
+
+ if (params->has_local) {
+ dest->local = params->local;
+ }
}
static void migrate_params_apply(MigrationParameters *params)
@@ -1565,6 +1577,10 @@ static void migrate_params_apply(MigrationParameters *params)
s->parameters.cpr_exec_command =
QAPI_CLONE(strList, params->cpr_exec_command);
}
+
+ if (params->has_local) {
+ s->parameters.local = params->local;
+ }
}
void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp)
diff --git a/qapi/migration.json b/qapi/migration.json
index 66bd900465a..03bdf19f3a6 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -830,7 +830,8 @@
'zero-page-detection',
'direct-io',
{ 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] },
- 'cpr-exec-command'] }
+ 'cpr-exec-command',
+ 'local'] }
##
# @migrate-set-parameters:
@@ -1012,6 +1013,12 @@
# Must be set to the same value on both source and destination
# before migration starts. (Since 11.1)
#
+# @local: Enable local migration for devices that support it. Backend
+# state and its file descriptors can then be passed to the
+# destination in the migration channel. The migration channel
+# must be a Unix domain socket. Usually needs to be enabled per
+# device. (Since 11.2)
+#
# Features:
#
# @unstable: Members @x-checkpoint-delay, @x-rdma-chunk-size, and
@@ -1053,7 +1060,8 @@
'*direct-io': 'bool',
'*x-rdma-chunk-size': { 'type': 'uint64',
'features': [ 'unstable' ] },
- '*cpr-exec-command': [ 'str' ]} }
+ '*cpr-exec-command': [ 'str' ],
+ '*local': 'bool' } }
##
# @query-migrate-parameters:
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* Re: [PATCH v18 09/14] qapi: add local migration parameter
2026-07-10 19:17 ` [PATCH v18 09/14] qapi: add local migration parameter Vladimir Sementsov-Ogievskiy
@ 2026-07-13 9:51 ` Daniel P. Berrangé
2026-07-13 10:55 ` Vladimir Sementsov-Ogievskiy
0 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2026-07-13 9:51 UTC (permalink / raw)
To: Vladimir Sementsov-Ogievskiy
Cc: jasowang, mst, armbru, peterx, farosas, raphael.s.norwitz,
bchaney, qemu-devel, pbonzini, yc-core, mark.caveayland,
Eric Blake
On Fri, Jul 10, 2026 at 10:17:58PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> We are going to implement local-migration feature: some devices will be
> able to transfer open file descriptors through migration stream (which
> must UNIX domain socket for that purpose). This allows to transfer the
> whole backend state without reconnecting and restarting the backend
> service. For example, virtio-net will migrate its attached TAP netdev,
> together with its connected file descriptors.
>
> In this commit we introduce a migration parameter, which enables
> the feature for devices that support it (none at the moment).
>
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
> Acked-by: Markus Armbruster <armbru@redhat.com>
> Acked-by: Peter Xu <peterx@redhat.com>
> Reviewed-by: Ben Chaney <bchaney@akamai.com>
> ---
> include/migration/misc.h | 2 ++
> migration/options.c | 18 +++++++++++++++++-
> qapi/migration.json | 12 ++++++++++--
> 3 files changed, 29 insertions(+), 3 deletions(-)
> diff --git a/qapi/migration.json b/qapi/migration.json
> index 66bd900465a..03bdf19f3a6 100644
> --- a/qapi/migration.json
> +++ b/qapi/migration.json
> @@ -830,7 +830,8 @@
> 'zero-page-detection',
> 'direct-io',
> { 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] },
> - 'cpr-exec-command'] }
> + 'cpr-exec-command',
> + 'local'] }
>
> ##
> # @migrate-set-parameters:
> @@ -1012,6 +1013,12 @@
> # Must be set to the same value on both source and destination
> # before migration starts. (Since 11.1)
> #
> +# @local: Enable local migration for devices that support it. Backend
> +# state and its file descriptors can then be passed to the
> +# destination in the migration channel. The migration channel
> +# must be a Unix domain socket. Usually needs to be enabled per
> +# device. (Since 11.2)
What happens when this is enabled and there are devices which
don't support it ?
What happens when this is enabled and a device on the src
does not support it but a device on the (newer) QEMU dest
does support it ? How does the mgmt app know which devices
support it ?
Can we document the semantics in these cases.
> +#
> # Features:
> #
> # @unstable: Members @x-checkpoint-delay, @x-rdma-chunk-size, and
> @@ -1053,7 +1060,8 @@
> '*direct-io': 'bool',
> '*x-rdma-chunk-size': { 'type': 'uint64',
> 'features': [ 'unstable' ] },
> - '*cpr-exec-command': [ 'str' ]} }
> + '*cpr-exec-command': [ 'str' ],
> + '*local': 'bool' } }
>
> ##
> # @query-migrate-parameters:
> --
> 2.43.0
>
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 09/14] qapi: add local migration parameter
2026-07-13 9:51 ` Daniel P. Berrangé
@ 2026-07-13 10:55 ` Vladimir Sementsov-Ogievskiy
2026-07-13 11:21 ` Daniel P. Berrangé
0 siblings, 1 reply; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-13 10:55 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: jasowang, mst, armbru, peterx, farosas, raphael.s.norwitz,
bchaney, qemu-devel, pbonzini, yc-core, mark.caveayland,
Eric Blake
On 13.07.26 12:51, Daniel P. Berrangé wrote:
> On Fri, Jul 10, 2026 at 10:17:58PM +0300, Vladimir Sementsov-Ogievskiy wrote:
>> We are going to implement local-migration feature: some devices will be
>> able to transfer open file descriptors through migration stream (which
>> must UNIX domain socket for that purpose). This allows to transfer the
>> whole backend state without reconnecting and restarting the backend
>> service. For example, virtio-net will migrate its attached TAP netdev,
>> together with its connected file descriptors.
>>
>> In this commit we introduce a migration parameter, which enables
>> the feature for devices that support it (none at the moment).
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
>> Acked-by: Markus Armbruster <armbru@redhat.com>
>> Acked-by: Peter Xu <peterx@redhat.com>
>> Reviewed-by: Ben Chaney <bchaney@akamai.com>
>> ---
>> include/migration/misc.h | 2 ++
>> migration/options.c | 18 +++++++++++++++++-
>> qapi/migration.json | 12 ++++++++++--
>> 3 files changed, 29 insertions(+), 3 deletions(-)
>
>
>> diff --git a/qapi/migration.json b/qapi/migration.json
>> index 66bd900465a..03bdf19f3a6 100644
>> --- a/qapi/migration.json
>> +++ b/qapi/migration.json
>> @@ -830,7 +830,8 @@
>> 'zero-page-detection',
>> 'direct-io',
>> { 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] },
>> - 'cpr-exec-command'] }
>> + 'cpr-exec-command',
>> + 'local'] }
>>
>> ##
>> # @migrate-set-parameters:
>> @@ -1012,6 +1013,12 @@
>> # Must be set to the same value on both source and destination
>> # before migration starts. (Since 11.1)
>> #
>> +# @local: Enable local migration for devices that support it. Backend
>> +# state and its file descriptors can then be passed to the
>> +# destination in the migration channel. The migration channel
>> +# must be a Unix domain socket. Usually needs to be enabled per
>> +# device. (Since 11.2)
>
> What happens when this is enabled and there are devices which
> don't support it ?
These devices are migrated as usual, ignoring "local" option.
>
> What happens when this is enabled and a device on the src
> does not support it but a device on the (newer) QEMU dest
> does support it ? How does the mgmt app know which devices
> support it ?
The only supporting device (TAP-backend) declares support by
"local-migration-supported", so mgmt app should care to set
same value for it on host and target (like for other options
actually). Or simply relay on default, which is true starting
from 11.2 Machine Type.
>
> Can we document the semantics in these cases.
That's: "Usually needs to be enabled per device."
Requiring any device to have a specific options like
"local-migration-supported" (for TAP device in this series) would
be not correct if new devices added, supporting "local" migration
from the beginning..
So, we (may) have three types of devices:
1. Not supporting "local" migration: they just ignore "local" migration
parameter.
2. Supporting "local" from the beginning (not such devices for now):
they simply do "local" migration when "local" is set, and "usual"
migration when "local" is not set.
3. Support for "local" appears at some moment (like TAP in this series).
Such devices should have a possibility to enable/disable the "support
for local" by hand, to be able to migrate betweend supporting and
not supporting QEMU versions.
Unsure, that we need this all be described here, may be, just ref
the only supporting device (TAP), so user may go to
"local-migration-supported" description for details?
>
>> +#
>> # Features:
>> #
>> # @unstable: Members @x-checkpoint-delay, @x-rdma-chunk-size, and
>> @@ -1053,7 +1060,8 @@
>> '*direct-io': 'bool',
>> '*x-rdma-chunk-size': { 'type': 'uint64',
>> 'features': [ 'unstable' ] },
>> - '*cpr-exec-command': [ 'str' ]} }
>> + '*cpr-exec-command': [ 'str' ],
>> + '*local': 'bool' } }
>>
>> ##
>> # @query-migrate-parameters:
>> --
>> 2.43.0
>>
>
> With regards,
> Daniel
--
Best regards,
Vladimir
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 09/14] qapi: add local migration parameter
2026-07-13 10:55 ` Vladimir Sementsov-Ogievskiy
@ 2026-07-13 11:21 ` Daniel P. Berrangé
2026-07-13 17:55 ` Vladimir Sementsov-Ogievskiy
0 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2026-07-13 11:21 UTC (permalink / raw)
To: Vladimir Sementsov-Ogievskiy
Cc: jasowang, mst, armbru, peterx, farosas, raphael.s.norwitz,
bchaney, qemu-devel, pbonzini, yc-core, mark.caveayland,
Eric Blake
On Mon, Jul 13, 2026 at 01:55:48PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> On 13.07.26 12:51, Daniel P. Berrangé wrote:
> > On Fri, Jul 10, 2026 at 10:17:58PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > > We are going to implement local-migration feature: some devices will be
> > > able to transfer open file descriptors through migration stream (which
> > > must UNIX domain socket for that purpose). This allows to transfer the
> > > whole backend state without reconnecting and restarting the backend
> > > service. For example, virtio-net will migrate its attached TAP netdev,
> > > together with its connected file descriptors.
> > >
> > > In this commit we introduce a migration parameter, which enables
> > > the feature for devices that support it (none at the moment).
> > >
> > > Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
> > > Acked-by: Markus Armbruster <armbru@redhat.com>
> > > Acked-by: Peter Xu <peterx@redhat.com>
> > > Reviewed-by: Ben Chaney <bchaney@akamai.com>
> > > ---
> > > include/migration/misc.h | 2 ++
> > > migration/options.c | 18 +++++++++++++++++-
> > > qapi/migration.json | 12 ++++++++++--
> > > 3 files changed, 29 insertions(+), 3 deletions(-)
> >
> >
> > > diff --git a/qapi/migration.json b/qapi/migration.json
> > > index 66bd900465a..03bdf19f3a6 100644
> > > --- a/qapi/migration.json
> > > +++ b/qapi/migration.json
> > > @@ -830,7 +830,8 @@
> > > 'zero-page-detection',
> > > 'direct-io',
> > > { 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] },
> > > - 'cpr-exec-command'] }
> > > + 'cpr-exec-command',
> > > + 'local'] }
> > > ##
> > > # @migrate-set-parameters:
> > > @@ -1012,6 +1013,12 @@
> > > # Must be set to the same value on both source and destination
> > > # before migration starts. (Since 11.1)
> > > #
> > > +# @local: Enable local migration for devices that support it. Backend
> > > +# state and its file descriptors can then be passed to the
> > > +# destination in the migration channel. The migration channel
> > > +# must be a Unix domain socket. Usually needs to be enabled per
> > > +# device. (Since 11.2)
> >
> > What happens when this is enabled and there are devices which
> > don't support it ?
>
> These devices are migrated as usual, ignoring "local" option.
>
> >
> > What happens when this is enabled and a device on the src
> > does not support it but a device on the (newer) QEMU dest
> > does support it ? How does the mgmt app know which devices
> > support it ?
>
> The only supporting device (TAP-backend) declares support by
> "local-migration-supported", so mgmt app should care to set
> same value for it on host and target (like for other options
> actually). Or simply relay on default, which is true starting
> from 11.2 Machine Type.
>
> >
> > Can we document the semantics in these cases.
>
> That's: "Usually needs to be enabled per device."
....except that we have it enabled by default for TAP backends
in new machine types.... except that it will then break if the
app/user has enabled any of the "forbidden" options.
The way this doc is written makes it sound like "local" is an
optimization you can turn on in any scenario, and it will do
the right thing if the device supports it. That is very much
not the case - enabling this flag will definitely break many
configs out of the box, with new machine types due to the TAP
device defaults & incompatible options.
> So, we (may) have three types of devices:
>
> 1. Not supporting "local" migration: they just ignore "local" migration
> parameter.
Yep, the easy case.
>
> 2. Supporting "local" from the beginning (not such devices for now):
> they simply do "local" migration when "local" is set, and "usual"
> migration when "local" is not set.
...except that abort the migration if you used any forbidden options.
I really dislike this as default out of the box behaviour for the
TAP backend.
> 3. Support for "local" appears at some moment (like TAP in this series).
> Such devices should have a possibility to enable/disable the "support
> for local" by hand, to be able to migrate betweend supporting and
> not supporting QEMU versions.
If the mgmt app is manually enabling the feature, then mitigates
the problem of forbidden options, as they should be aware of
whether their config is safe to enable or not.
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 09/14] qapi: add local migration parameter
2026-07-13 11:21 ` Daniel P. Berrangé
@ 2026-07-13 17:55 ` Vladimir Sementsov-Ogievskiy
2026-07-13 18:37 ` Daniel P. Berrangé
0 siblings, 1 reply; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-13 17:55 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: jasowang, mst, armbru, peterx, farosas, raphael.s.norwitz,
bchaney, qemu-devel, pbonzini, yc-core, mark.caveayland,
Eric Blake
On 13.07.26 14:21, Daniel P. Berrangé wrote:
> On Mon, Jul 13, 2026 at 01:55:48PM +0300, Vladimir Sementsov-Ogievskiy wrote:
>> On 13.07.26 12:51, Daniel P. Berrangé wrote:
>>> On Fri, Jul 10, 2026 at 10:17:58PM +0300, Vladimir Sementsov-Ogievskiy wrote:
>>>> We are going to implement local-migration feature: some devices will be
>>>> able to transfer open file descriptors through migration stream (which
>>>> must UNIX domain socket for that purpose). This allows to transfer the
>>>> whole backend state without reconnecting and restarting the backend
>>>> service. For example, virtio-net will migrate its attached TAP netdev,
>>>> together with its connected file descriptors.
>>>>
>>>> In this commit we introduce a migration parameter, which enables
>>>> the feature for devices that support it (none at the moment).
>>>>
>>>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
>>>> Acked-by: Markus Armbruster <armbru@redhat.com>
>>>> Acked-by: Peter Xu <peterx@redhat.com>
>>>> Reviewed-by: Ben Chaney <bchaney@akamai.com>
>>>> ---
>>>> include/migration/misc.h | 2 ++
>>>> migration/options.c | 18 +++++++++++++++++-
>>>> qapi/migration.json | 12 ++++++++++--
>>>> 3 files changed, 29 insertions(+), 3 deletions(-)
>>>
>>>
>>>> diff --git a/qapi/migration.json b/qapi/migration.json
>>>> index 66bd900465a..03bdf19f3a6 100644
>>>> --- a/qapi/migration.json
>>>> +++ b/qapi/migration.json
>>>> @@ -830,7 +830,8 @@
>>>> 'zero-page-detection',
>>>> 'direct-io',
>>>> { 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] },
>>>> - 'cpr-exec-command'] }
>>>> + 'cpr-exec-command',
>>>> + 'local'] }
>>>> ##
>>>> # @migrate-set-parameters:
>>>> @@ -1012,6 +1013,12 @@
>>>> # Must be set to the same value on both source and destination
>>>> # before migration starts. (Since 11.1)
>>>> #
>>>> +# @local: Enable local migration for devices that support it. Backend
>>>> +# state and its file descriptors can then be passed to the
>>>> +# destination in the migration channel. The migration channel
>>>> +# must be a Unix domain socket. Usually needs to be enabled per
>>>> +# device. (Since 11.2)
>>>
>>> What happens when this is enabled and there are devices which
>>> don't support it ?
>>
>> These devices are migrated as usual, ignoring "local" option.
>>
>>>
>>> What happens when this is enabled and a device on the src
>>> does not support it but a device on the (newer) QEMU dest
>>> does support it ? How does the mgmt app know which devices
>>> support it ?
>>
>> The only supporting device (TAP-backend) declares support by
>> "local-migration-supported", so mgmt app should care to set
>> same value for it on host and target (like for other options
>> actually). Or simply relay on default, which is true starting
>> from 11.2 Machine Type.
>>
>>>
>>> Can we document the semantics in these cases.
>>
>> That's: "Usually needs to be enabled per device."
>
> ....except that we have it enabled by default for TAP backends
> in new machine types.... except that it will then break if the
> app/user has enabled any of the "forbidden" options.
>
> The way this doc is written makes it sound like "local" is an
> optimization you can turn on in any scenario, and it will do
> the right thing if the device supports it. That is very much
> not the case - enabling this flag will definitely break many
> configs out of the box, with new machine types due to the TAP
> device defaults & incompatible options.
Ok, now we decided to NOT make default support for TAP in newer
machine types.
So, enabling "local" is safe in existing scenarios, only explicit
enabling TAP.local-migration-supported will change the behavior.
Keep "local" documentation as is?
I see the only way to make it more informative: directly describe
connection with TAP.local-migration-supported, without general
phrases about "devices that support" and "usual". We'll have to
update it when more devices added (my next step is vhost-user-blk),
but at least, it would be absolutely correct at the moment.
>
>
>> So, we (may) have three types of devices:
>>
>> 1. Not supporting "local" migration: they just ignore "local" migration
>> parameter.
>
> Yep, the easy case.
>
>>
>> 2. Supporting "local" from the beginning (not such devices for now):
>> they simply do "local" migration when "local" is set, and "usual"
>> migration when "local" is not set.
>
> ...except that abort the migration if you used any forbidden options.
> I really dislike this as default out of the box behaviour for the
> TAP backend.
>
>> 3. Support for "local" appears at some moment (like TAP in this series).
>> Such devices should have a possibility to enable/disable the "support
>> for local" by hand, to be able to migrate betweend supporting and
>> not supporting QEMU versions.
>
> If the mgmt app is manually enabling the feature, then mitigates
> the problem of forbidden options, as they should be aware of
> whether their config is safe to enable or not.
>
>
> With regards,
> Daniel
--
Best regards,
Vladimir
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 09/14] qapi: add local migration parameter
2026-07-13 17:55 ` Vladimir Sementsov-Ogievskiy
@ 2026-07-13 18:37 ` Daniel P. Berrangé
2026-07-13 20:22 ` Michael S. Tsirkin
2026-07-14 6:45 ` Vladimir Sementsov-Ogievskiy
0 siblings, 2 replies; 44+ messages in thread
From: Daniel P. Berrangé @ 2026-07-13 18:37 UTC (permalink / raw)
To: Vladimir Sementsov-Ogievskiy
Cc: jasowang, mst, armbru, peterx, farosas, raphael.s.norwitz,
bchaney, qemu-devel, pbonzini, yc-core, mark.caveayland,
Eric Blake
On Mon, Jul 13, 2026 at 08:55:37PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> On 13.07.26 14:21, Daniel P. Berrangé wrote:
> > On Mon, Jul 13, 2026 at 01:55:48PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > > On 13.07.26 12:51, Daniel P. Berrangé wrote:
> > > > On Fri, Jul 10, 2026 at 10:17:58PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > > > > We are going to implement local-migration feature: some devices will be
> > > > > able to transfer open file descriptors through migration stream (which
> > > > > must UNIX domain socket for that purpose). This allows to transfer the
> > > > > whole backend state without reconnecting and restarting the backend
> > > > > service. For example, virtio-net will migrate its attached TAP netdev,
> > > > > together with its connected file descriptors.
> > > > >
> > > > > In this commit we introduce a migration parameter, which enables
> > > > > the feature for devices that support it (none at the moment).
> > > > >
> > > > > Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
> > > > > Acked-by: Markus Armbruster <armbru@redhat.com>
> > > > > Acked-by: Peter Xu <peterx@redhat.com>
> > > > > Reviewed-by: Ben Chaney <bchaney@akamai.com>
> > > > > ---
> > > > > include/migration/misc.h | 2 ++
> > > > > migration/options.c | 18 +++++++++++++++++-
> > > > > qapi/migration.json | 12 ++++++++++--
> > > > > 3 files changed, 29 insertions(+), 3 deletions(-)
> > > >
> > > >
> > > > > diff --git a/qapi/migration.json b/qapi/migration.json
> > > > > index 66bd900465a..03bdf19f3a6 100644
> > > > > --- a/qapi/migration.json
> > > > > +++ b/qapi/migration.json
> > > > > @@ -830,7 +830,8 @@
> > > > > 'zero-page-detection',
> > > > > 'direct-io',
> > > > > { 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] },
> > > > > - 'cpr-exec-command'] }
> > > > > + 'cpr-exec-command',
> > > > > + 'local'] }
> > > > > ##
> > > > > # @migrate-set-parameters:
> > > > > @@ -1012,6 +1013,12 @@
> > > > > # Must be set to the same value on both source and destination
> > > > > # before migration starts. (Since 11.1)
> > > > > #
> > > > > +# @local: Enable local migration for devices that support it. Backend
> > > > > +# state and its file descriptors can then be passed to the
> > > > > +# destination in the migration channel. The migration channel
> > > > > +# must be a Unix domain socket. Usually needs to be enabled per
> > > > > +# device. (Since 11.2)
> > > >
> > > > What happens when this is enabled and there are devices which
> > > > don't support it ?
> > >
> > > These devices are migrated as usual, ignoring "local" option.
> > >
> > > >
> > > > What happens when this is enabled and a device on the src
> > > > does not support it but a device on the (newer) QEMU dest
> > > > does support it ? How does the mgmt app know which devices
> > > > support it ?
> > >
> > > The only supporting device (TAP-backend) declares support by
> > > "local-migration-supported", so mgmt app should care to set
> > > same value for it on host and target (like for other options
> > > actually). Or simply relay on default, which is true starting
> > > from 11.2 Machine Type.
> > >
> > > >
> > > > Can we document the semantics in these cases.
> > >
> > > That's: "Usually needs to be enabled per device."
> >
> > ....except that we have it enabled by default for TAP backends
> > in new machine types.... except that it will then break if the
> > app/user has enabled any of the "forbidden" options.
> >
> > The way this doc is written makes it sound like "local" is an
> > optimization you can turn on in any scenario, and it will do
> > the right thing if the device supports it. That is very much
> > not the case - enabling this flag will definitely break many
> > configs out of the box, with new machine types due to the TAP
> > device defaults & incompatible options.
>
> Ok, now we decided to NOT make default support for TAP in newer
> machine types.
>
> So, enabling "local" is safe in existing scenarios, only explicit
> enabling TAP.local-migration-supported will change the behavior.
>
> Keep "local" documentation as is?
>
> I see the only way to make it more informative: directly describe
> connection with TAP.local-migration-supported, without general
> phrases about "devices that support" and "usual". We'll have to
> update it when more devices added (my next step is vhost-user-blk),
> but at least, it would be absolutely correct at the moment.
How about....
# @local: Permit the use of optimizations for local migration.
# This must only be set when both the source and destination
# QEMU processes are on the same OS and directly connected
# with a UNIX domain socket as the migration channel to enable
# use of file descriptor passing. Individual device backends
# may need additional configuration flags set to enable local
# migration optimizations. This will be documented against the
# device backends where it applies. (Since 11.2)
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 09/14] qapi: add local migration parameter
2026-07-13 18:37 ` Daniel P. Berrangé
@ 2026-07-13 20:22 ` Michael S. Tsirkin
2026-07-14 6:50 ` Vladimir Sementsov-Ogievskiy
2026-07-14 7:55 ` Daniel P. Berrangé
2026-07-14 6:45 ` Vladimir Sementsov-Ogievskiy
1 sibling, 2 replies; 44+ messages in thread
From: Michael S. Tsirkin @ 2026-07-13 20:22 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: Vladimir Sementsov-Ogievskiy, jasowang, armbru, peterx, farosas,
raphael.s.norwitz, bchaney, qemu-devel, pbonzini, yc-core,
mark.caveayland, Eric Blake
On Mon, Jul 13, 2026 at 07:37:50PM +0100, Daniel P. Berrangé wrote:
> On Mon, Jul 13, 2026 at 08:55:37PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > On 13.07.26 14:21, Daniel P. Berrangé wrote:
> > > On Mon, Jul 13, 2026 at 01:55:48PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > > > On 13.07.26 12:51, Daniel P. Berrangé wrote:
> > > > > On Fri, Jul 10, 2026 at 10:17:58PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > > > > > We are going to implement local-migration feature: some devices will be
> > > > > > able to transfer open file descriptors through migration stream (which
> > > > > > must UNIX domain socket for that purpose). This allows to transfer the
> > > > > > whole backend state without reconnecting and restarting the backend
> > > > > > service. For example, virtio-net will migrate its attached TAP netdev,
> > > > > > together with its connected file descriptors.
> > > > > >
> > > > > > In this commit we introduce a migration parameter, which enables
> > > > > > the feature for devices that support it (none at the moment).
> > > > > >
> > > > > > Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
> > > > > > Acked-by: Markus Armbruster <armbru@redhat.com>
> > > > > > Acked-by: Peter Xu <peterx@redhat.com>
> > > > > > Reviewed-by: Ben Chaney <bchaney@akamai.com>
> > > > > > ---
> > > > > > include/migration/misc.h | 2 ++
> > > > > > migration/options.c | 18 +++++++++++++++++-
> > > > > > qapi/migration.json | 12 ++++++++++--
> > > > > > 3 files changed, 29 insertions(+), 3 deletions(-)
> > > > >
> > > > >
> > > > > > diff --git a/qapi/migration.json b/qapi/migration.json
> > > > > > index 66bd900465a..03bdf19f3a6 100644
> > > > > > --- a/qapi/migration.json
> > > > > > +++ b/qapi/migration.json
> > > > > > @@ -830,7 +830,8 @@
> > > > > > 'zero-page-detection',
> > > > > > 'direct-io',
> > > > > > { 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] },
> > > > > > - 'cpr-exec-command'] }
> > > > > > + 'cpr-exec-command',
> > > > > > + 'local'] }
> > > > > > ##
> > > > > > # @migrate-set-parameters:
> > > > > > @@ -1012,6 +1013,12 @@
> > > > > > # Must be set to the same value on both source and destination
> > > > > > # before migration starts. (Since 11.1)
> > > > > > #
> > > > > > +# @local: Enable local migration for devices that support it. Backend
> > > > > > +# state and its file descriptors can then be passed to the
> > > > > > +# destination in the migration channel. The migration channel
> > > > > > +# must be a Unix domain socket. Usually needs to be enabled per
> > > > > > +# device. (Since 11.2)
> > > > >
> > > > > What happens when this is enabled and there are devices which
> > > > > don't support it ?
> > > >
> > > > These devices are migrated as usual, ignoring "local" option.
> > > >
> > > > >
> > > > > What happens when this is enabled and a device on the src
> > > > > does not support it but a device on the (newer) QEMU dest
> > > > > does support it ? How does the mgmt app know which devices
> > > > > support it ?
> > > >
> > > > The only supporting device (TAP-backend) declares support by
> > > > "local-migration-supported", so mgmt app should care to set
> > > > same value for it on host and target (like for other options
> > > > actually). Or simply relay on default, which is true starting
> > > > from 11.2 Machine Type.
> > > >
> > > > >
> > > > > Can we document the semantics in these cases.
> > > >
> > > > That's: "Usually needs to be enabled per device."
> > >
> > > ....except that we have it enabled by default for TAP backends
> > > in new machine types.... except that it will then break if the
> > > app/user has enabled any of the "forbidden" options.
> > >
> > > The way this doc is written makes it sound like "local" is an
> > > optimization you can turn on in any scenario, and it will do
> > > the right thing if the device supports it. That is very much
> > > not the case - enabling this flag will definitely break many
> > > configs out of the box, with new machine types due to the TAP
> > > device defaults & incompatible options.
> >
> > Ok, now we decided to NOT make default support for TAP in newer
> > machine types.
> >
> > So, enabling "local" is safe in existing scenarios, only explicit
> > enabling TAP.local-migration-supported will change the behavior.
> >
> > Keep "local" documentation as is?
> >
> > I see the only way to make it more informative: directly describe
> > connection with TAP.local-migration-supported, without general
> > phrases about "devices that support" and "usual". We'll have to
> > update it when more devices added (my next step is vhost-user-blk),
> > but at least, it would be absolutely correct at the moment.
>
> How about....
>
> # @local: Permit the use of optimizations for local migration.
> # This must only be set when both the source and destination
> # QEMU processes are on the same OS and directly connected
> # with a UNIX domain socket as the migration channel to enable
> # use of file descriptor passing. Individual device backends
> # may need additional configuration flags set to enable local
> # migration optimizations. This will be documented against the
> # device backends where it applies. (Since 11.2)
>
BTW surely qemu can just detect this? getsockname maybe?
> With regards,
> Daniel
> --
> |: https://berrange.com ~~ https://hachyderm.io/@berrange :|
> |: https://libvirt.org ~~ https://entangle-photo.org :|
> |: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 09/14] qapi: add local migration parameter
2026-07-13 20:22 ` Michael S. Tsirkin
@ 2026-07-14 6:50 ` Vladimir Sementsov-Ogievskiy
2026-07-14 8:03 ` Michael S. Tsirkin
2026-07-14 7:55 ` Daniel P. Berrangé
1 sibling, 1 reply; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-14 6:50 UTC (permalink / raw)
To: Michael S. Tsirkin, Daniel P. Berrangé
Cc: jasowang, armbru, peterx, farosas, raphael.s.norwitz, bchaney,
qemu-devel, pbonzini, yc-core, mark.caveayland, Eric Blake
On 13.07.26 23:22, Michael S. Tsirkin wrote:
> On Mon, Jul 13, 2026 at 07:37:50PM +0100, Daniel P. Berrangé wrote:
>> On Mon, Jul 13, 2026 at 08:55:37PM +0300, Vladimir Sementsov-Ogievskiy wrote:
>>> On 13.07.26 14:21, Daniel P. Berrangé wrote:
>>>> On Mon, Jul 13, 2026 at 01:55:48PM +0300, Vladimir Sementsov-Ogievskiy wrote:
>>>>> On 13.07.26 12:51, Daniel P. Berrangé wrote:
>>>>>> On Fri, Jul 10, 2026 at 10:17:58PM +0300, Vladimir Sementsov-Ogievskiy wrote:
>>>>>>> We are going to implement local-migration feature: some devices will be
>>>>>>> able to transfer open file descriptors through migration stream (which
>>>>>>> must UNIX domain socket for that purpose). This allows to transfer the
>>>>>>> whole backend state without reconnecting and restarting the backend
>>>>>>> service. For example, virtio-net will migrate its attached TAP netdev,
>>>>>>> together with its connected file descriptors.
>>>>>>>
>>>>>>> In this commit we introduce a migration parameter, which enables
>>>>>>> the feature for devices that support it (none at the moment).
>>>>>>>
>>>>>>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
>>>>>>> Acked-by: Markus Armbruster <armbru@redhat.com>
>>>>>>> Acked-by: Peter Xu <peterx@redhat.com>
>>>>>>> Reviewed-by: Ben Chaney <bchaney@akamai.com>
>>>>>>> ---
>>>>>>> include/migration/misc.h | 2 ++
>>>>>>> migration/options.c | 18 +++++++++++++++++-
>>>>>>> qapi/migration.json | 12 ++++++++++--
>>>>>>> 3 files changed, 29 insertions(+), 3 deletions(-)
>>>>>>
>>>>>>
>>>>>>> diff --git a/qapi/migration.json b/qapi/migration.json
>>>>>>> index 66bd900465a..03bdf19f3a6 100644
>>>>>>> --- a/qapi/migration.json
>>>>>>> +++ b/qapi/migration.json
>>>>>>> @@ -830,7 +830,8 @@
>>>>>>> 'zero-page-detection',
>>>>>>> 'direct-io',
>>>>>>> { 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] },
>>>>>>> - 'cpr-exec-command'] }
>>>>>>> + 'cpr-exec-command',
>>>>>>> + 'local'] }
>>>>>>> ##
>>>>>>> # @migrate-set-parameters:
>>>>>>> @@ -1012,6 +1013,12 @@
>>>>>>> # Must be set to the same value on both source and destination
>>>>>>> # before migration starts. (Since 11.1)
>>>>>>> #
>>>>>>> +# @local: Enable local migration for devices that support it. Backend
>>>>>>> +# state and its file descriptors can then be passed to the
>>>>>>> +# destination in the migration channel. The migration channel
>>>>>>> +# must be a Unix domain socket. Usually needs to be enabled per
>>>>>>> +# device. (Since 11.2)
>>>>>>
>>>>>> What happens when this is enabled and there are devices which
>>>>>> don't support it ?
>>>>>
>>>>> These devices are migrated as usual, ignoring "local" option.
>>>>>
>>>>>>
>>>>>> What happens when this is enabled and a device on the src
>>>>>> does not support it but a device on the (newer) QEMU dest
>>>>>> does support it ? How does the mgmt app know which devices
>>>>>> support it ?
>>>>>
>>>>> The only supporting device (TAP-backend) declares support by
>>>>> "local-migration-supported", so mgmt app should care to set
>>>>> same value for it on host and target (like for other options
>>>>> actually). Or simply relay on default, which is true starting
>>>>> from 11.2 Machine Type.
>>>>>
>>>>>>
>>>>>> Can we document the semantics in these cases.
>>>>>
>>>>> That's: "Usually needs to be enabled per device."
>>>>
>>>> ....except that we have it enabled by default for TAP backends
>>>> in new machine types.... except that it will then break if the
>>>> app/user has enabled any of the "forbidden" options.
>>>>
>>>> The way this doc is written makes it sound like "local" is an
>>>> optimization you can turn on in any scenario, and it will do
>>>> the right thing if the device supports it. That is very much
>>>> not the case - enabling this flag will definitely break many
>>>> configs out of the box, with new machine types due to the TAP
>>>> device defaults & incompatible options.
>>>
>>> Ok, now we decided to NOT make default support for TAP in newer
>>> machine types.
>>>
>>> So, enabling "local" is safe in existing scenarios, only explicit
>>> enabling TAP.local-migration-supported will change the behavior.
>>>
>>> Keep "local" documentation as is?
>>>
>>> I see the only way to make it more informative: directly describe
>>> connection with TAP.local-migration-supported, without general
>>> phrases about "devices that support" and "usual". We'll have to
>>> update it when more devices added (my next step is vhost-user-blk),
>>> but at least, it would be absolutely correct at the moment.
>>
>> How about....
>>
>> # @local: Permit the use of optimizations for local migration.
>> # This must only be set when both the source and destination
>> # QEMU processes are on the same OS and directly connected
>> # with a UNIX domain socket as the migration channel to enable
>> # use of file descriptor passing. Individual device backends
>> # may need additional configuration flags set to enable local
>> # migration optimizations. This will be documented against the
>> # device backends where it applies. (Since 11.2)
>>
>
> BTW surely qemu can just detect this? getsockname maybe?
>
Yes, but this doesn't guarantee that it's a direct connection by UNIX
socket to target, and it really can pass FDs. UNIX socket may be only
a first part of transfer channel to the remote target.
--
Best regards,
Vladimir
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 09/14] qapi: add local migration parameter
2026-07-14 6:50 ` Vladimir Sementsov-Ogievskiy
@ 2026-07-14 8:03 ` Michael S. Tsirkin
2026-07-14 8:04 ` Vladimir Sementsov-Ogievskiy
0 siblings, 1 reply; 44+ messages in thread
From: Michael S. Tsirkin @ 2026-07-14 8:03 UTC (permalink / raw)
To: Vladimir Sementsov-Ogievskiy
Cc: Daniel P. Berrangé, jasowang, armbru, peterx, farosas,
raphael.s.norwitz, bchaney, qemu-devel, pbonzini, yc-core,
mark.caveayland, Eric Blake
On Tue, Jul 14, 2026 at 09:50:34AM +0300, Vladimir Sementsov-Ogievskiy wrote:
> On 13.07.26 23:22, Michael S. Tsirkin wrote:
> > On Mon, Jul 13, 2026 at 07:37:50PM +0100, Daniel P. Berrangé wrote:
> > > On Mon, Jul 13, 2026 at 08:55:37PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > > > On 13.07.26 14:21, Daniel P. Berrangé wrote:
> > > > > On Mon, Jul 13, 2026 at 01:55:48PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > > > > > On 13.07.26 12:51, Daniel P. Berrangé wrote:
> > > > > > > On Fri, Jul 10, 2026 at 10:17:58PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > > > > > > > We are going to implement local-migration feature: some devices will be
> > > > > > > > able to transfer open file descriptors through migration stream (which
> > > > > > > > must UNIX domain socket for that purpose). This allows to transfer the
> > > > > > > > whole backend state without reconnecting and restarting the backend
> > > > > > > > service. For example, virtio-net will migrate its attached TAP netdev,
> > > > > > > > together with its connected file descriptors.
> > > > > > > >
> > > > > > > > In this commit we introduce a migration parameter, which enables
> > > > > > > > the feature for devices that support it (none at the moment).
> > > > > > > >
> > > > > > > > Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
> > > > > > > > Acked-by: Markus Armbruster <armbru@redhat.com>
> > > > > > > > Acked-by: Peter Xu <peterx@redhat.com>
> > > > > > > > Reviewed-by: Ben Chaney <bchaney@akamai.com>
> > > > > > > > ---
> > > > > > > > include/migration/misc.h | 2 ++
> > > > > > > > migration/options.c | 18 +++++++++++++++++-
> > > > > > > > qapi/migration.json | 12 ++++++++++--
> > > > > > > > 3 files changed, 29 insertions(+), 3 deletions(-)
> > > > > > >
> > > > > > >
> > > > > > > > diff --git a/qapi/migration.json b/qapi/migration.json
> > > > > > > > index 66bd900465a..03bdf19f3a6 100644
> > > > > > > > --- a/qapi/migration.json
> > > > > > > > +++ b/qapi/migration.json
> > > > > > > > @@ -830,7 +830,8 @@
> > > > > > > > 'zero-page-detection',
> > > > > > > > 'direct-io',
> > > > > > > > { 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] },
> > > > > > > > - 'cpr-exec-command'] }
> > > > > > > > + 'cpr-exec-command',
> > > > > > > > + 'local'] }
> > > > > > > > ##
> > > > > > > > # @migrate-set-parameters:
> > > > > > > > @@ -1012,6 +1013,12 @@
> > > > > > > > # Must be set to the same value on both source and destination
> > > > > > > > # before migration starts. (Since 11.1)
> > > > > > > > #
> > > > > > > > +# @local: Enable local migration for devices that support it. Backend
> > > > > > > > +# state and its file descriptors can then be passed to the
> > > > > > > > +# destination in the migration channel. The migration channel
> > > > > > > > +# must be a Unix domain socket. Usually needs to be enabled per
> > > > > > > > +# device. (Since 11.2)
> > > > > > >
> > > > > > > What happens when this is enabled and there are devices which
> > > > > > > don't support it ?
> > > > > >
> > > > > > These devices are migrated as usual, ignoring "local" option.
> > > > > >
> > > > > > >
> > > > > > > What happens when this is enabled and a device on the src
> > > > > > > does not support it but a device on the (newer) QEMU dest
> > > > > > > does support it ? How does the mgmt app know which devices
> > > > > > > support it ?
> > > > > >
> > > > > > The only supporting device (TAP-backend) declares support by
> > > > > > "local-migration-supported", so mgmt app should care to set
> > > > > > same value for it on host and target (like for other options
> > > > > > actually). Or simply relay on default, which is true starting
> > > > > > from 11.2 Machine Type.
> > > > > >
> > > > > > >
> > > > > > > Can we document the semantics in these cases.
> > > > > >
> > > > > > That's: "Usually needs to be enabled per device."
> > > > >
> > > > > ....except that we have it enabled by default for TAP backends
> > > > > in new machine types.... except that it will then break if the
> > > > > app/user has enabled any of the "forbidden" options.
> > > > >
> > > > > The way this doc is written makes it sound like "local" is an
> > > > > optimization you can turn on in any scenario, and it will do
> > > > > the right thing if the device supports it. That is very much
> > > > > not the case - enabling this flag will definitely break many
> > > > > configs out of the box, with new machine types due to the TAP
> > > > > device defaults & incompatible options.
> > > >
> > > > Ok, now we decided to NOT make default support for TAP in newer
> > > > machine types.
> > > >
> > > > So, enabling "local" is safe in existing scenarios, only explicit
> > > > enabling TAP.local-migration-supported will change the behavior.
> > > >
> > > > Keep "local" documentation as is?
> > > >
> > > > I see the only way to make it more informative: directly describe
> > > > connection with TAP.local-migration-supported, without general
> > > > phrases about "devices that support" and "usual". We'll have to
> > > > update it when more devices added (my next step is vhost-user-blk),
> > > > but at least, it would be absolutely correct at the moment.
> > >
> > > How about....
> > >
> > > # @local: Permit the use of optimizations for local migration.
> > > # This must only be set when both the source and destination
> > > # QEMU processes are on the same OS and directly connected
> > > # with a UNIX domain socket as the migration channel to enable
> > > # use of file descriptor passing. Individual device backends
> > > # may need additional configuration flags set to enable local
> > > # migration optimizations. This will be documented against the
> > > # device backends where it applies. (Since 11.2)
> > >
> >
> > BTW surely qemu can just detect this? getsockname maybe?
> >
>
> Yes, but this doesn't guarantee that it's a direct connection by UNIX
> socket to target, and it really can pass FDs. UNIX socket may be only
> a first part of transfer channel to the remote target.
Sure, but that's not a reason not to check so we can fail early.
> --
> Best regards,
> Vladimir
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 09/14] qapi: add local migration parameter
2026-07-14 8:03 ` Michael S. Tsirkin
@ 2026-07-14 8:04 ` Vladimir Sementsov-Ogievskiy
0 siblings, 0 replies; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-14 8:04 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Daniel P. Berrangé, jasowang, armbru, peterx, farosas,
raphael.s.norwitz, bchaney, qemu-devel, pbonzini, yc-core,
mark.caveayland, Eric Blake
On 14.07.26 11:03, Michael S. Tsirkin wrote:
> On Tue, Jul 14, 2026 at 09:50:34AM +0300, Vladimir Sementsov-Ogievskiy wrote:
>> On 13.07.26 23:22, Michael S. Tsirkin wrote:
>>> On Mon, Jul 13, 2026 at 07:37:50PM +0100, Daniel P. Berrangé wrote:
>>>> On Mon, Jul 13, 2026 at 08:55:37PM +0300, Vladimir Sementsov-Ogievskiy wrote:
>>>>> On 13.07.26 14:21, Daniel P. Berrangé wrote:
>>>>>> On Mon, Jul 13, 2026 at 01:55:48PM +0300, Vladimir Sementsov-Ogievskiy wrote:
>>>>>>> On 13.07.26 12:51, Daniel P. Berrangé wrote:
>>>>>>>> On Fri, Jul 10, 2026 at 10:17:58PM +0300, Vladimir Sementsov-Ogievskiy wrote:
>>>>>>>>> We are going to implement local-migration feature: some devices will be
>>>>>>>>> able to transfer open file descriptors through migration stream (which
>>>>>>>>> must UNIX domain socket for that purpose). This allows to transfer the
>>>>>>>>> whole backend state without reconnecting and restarting the backend
>>>>>>>>> service. For example, virtio-net will migrate its attached TAP netdev,
>>>>>>>>> together with its connected file descriptors.
>>>>>>>>>
>>>>>>>>> In this commit we introduce a migration parameter, which enables
>>>>>>>>> the feature for devices that support it (none at the moment).
>>>>>>>>>
>>>>>>>>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
>>>>>>>>> Acked-by: Markus Armbruster <armbru@redhat.com>
>>>>>>>>> Acked-by: Peter Xu <peterx@redhat.com>
>>>>>>>>> Reviewed-by: Ben Chaney <bchaney@akamai.com>
>>>>>>>>> ---
>>>>>>>>> include/migration/misc.h | 2 ++
>>>>>>>>> migration/options.c | 18 +++++++++++++++++-
>>>>>>>>> qapi/migration.json | 12 ++++++++++--
>>>>>>>>> 3 files changed, 29 insertions(+), 3 deletions(-)
>>>>>>>>
>>>>>>>>
>>>>>>>>> diff --git a/qapi/migration.json b/qapi/migration.json
>>>>>>>>> index 66bd900465a..03bdf19f3a6 100644
>>>>>>>>> --- a/qapi/migration.json
>>>>>>>>> +++ b/qapi/migration.json
>>>>>>>>> @@ -830,7 +830,8 @@
>>>>>>>>> 'zero-page-detection',
>>>>>>>>> 'direct-io',
>>>>>>>>> { 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] },
>>>>>>>>> - 'cpr-exec-command'] }
>>>>>>>>> + 'cpr-exec-command',
>>>>>>>>> + 'local'] }
>>>>>>>>> ##
>>>>>>>>> # @migrate-set-parameters:
>>>>>>>>> @@ -1012,6 +1013,12 @@
>>>>>>>>> # Must be set to the same value on both source and destination
>>>>>>>>> # before migration starts. (Since 11.1)
>>>>>>>>> #
>>>>>>>>> +# @local: Enable local migration for devices that support it. Backend
>>>>>>>>> +# state and its file descriptors can then be passed to the
>>>>>>>>> +# destination in the migration channel. The migration channel
>>>>>>>>> +# must be a Unix domain socket. Usually needs to be enabled per
>>>>>>>>> +# device. (Since 11.2)
>>>>>>>>
>>>>>>>> What happens when this is enabled and there are devices which
>>>>>>>> don't support it ?
>>>>>>>
>>>>>>> These devices are migrated as usual, ignoring "local" option.
>>>>>>>
>>>>>>>>
>>>>>>>> What happens when this is enabled and a device on the src
>>>>>>>> does not support it but a device on the (newer) QEMU dest
>>>>>>>> does support it ? How does the mgmt app know which devices
>>>>>>>> support it ?
>>>>>>>
>>>>>>> The only supporting device (TAP-backend) declares support by
>>>>>>> "local-migration-supported", so mgmt app should care to set
>>>>>>> same value for it on host and target (like for other options
>>>>>>> actually). Or simply relay on default, which is true starting
>>>>>>> from 11.2 Machine Type.
>>>>>>>
>>>>>>>>
>>>>>>>> Can we document the semantics in these cases.
>>>>>>>
>>>>>>> That's: "Usually needs to be enabled per device."
>>>>>>
>>>>>> ....except that we have it enabled by default for TAP backends
>>>>>> in new machine types.... except that it will then break if the
>>>>>> app/user has enabled any of the "forbidden" options.
>>>>>>
>>>>>> The way this doc is written makes it sound like "local" is an
>>>>>> optimization you can turn on in any scenario, and it will do
>>>>>> the right thing if the device supports it. That is very much
>>>>>> not the case - enabling this flag will definitely break many
>>>>>> configs out of the box, with new machine types due to the TAP
>>>>>> device defaults & incompatible options.
>>>>>
>>>>> Ok, now we decided to NOT make default support for TAP in newer
>>>>> machine types.
>>>>>
>>>>> So, enabling "local" is safe in existing scenarios, only explicit
>>>>> enabling TAP.local-migration-supported will change the behavior.
>>>>>
>>>>> Keep "local" documentation as is?
>>>>>
>>>>> I see the only way to make it more informative: directly describe
>>>>> connection with TAP.local-migration-supported, without general
>>>>> phrases about "devices that support" and "usual". We'll have to
>>>>> update it when more devices added (my next step is vhost-user-blk),
>>>>> but at least, it would be absolutely correct at the moment.
>>>>
>>>> How about....
>>>>
>>>> # @local: Permit the use of optimizations for local migration.
>>>> # This must only be set when both the source and destination
>>>> # QEMU processes are on the same OS and directly connected
>>>> # with a UNIX domain socket as the migration channel to enable
>>>> # use of file descriptor passing. Individual device backends
>>>> # may need additional configuration flags set to enable local
>>>> # migration optimizations. This will be documented against the
>>>> # device backends where it applies. (Since 11.2)
>>>>
>>>
>>> BTW surely qemu can just detect this? getsockname maybe?
>>>
>>
>> Yes, but this doesn't guarantee that it's a direct connection by UNIX
>> socket to target, and it really can pass FDs. UNIX socket may be only
>> a first part of transfer channel to the remote target.
>
> Sure, but that's not a reason not to check so we can fail early.
>
Agree, will add a check.
--
Best regards,
Vladimir
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v18 09/14] qapi: add local migration parameter
2026-07-13 20:22 ` Michael S. Tsirkin
2026-07-14 6:50 ` Vladimir Sementsov-Ogievskiy
@ 2026-07-14 7:55 ` Daniel P. Berrangé
2026-07-14 8:02 ` Michael S. Tsirkin
1 sibling, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2026-07-14 7:55 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Vladimir Sementsov-Ogievskiy, jasowang, armbru, peterx, farosas,
raphael.s.norwitz, bchaney, qemu-devel, pbonzini, yc-core,
mark.caveayland, Eric Blake
On Mon, Jul 13, 2026 at 04:22:03PM -0400, Michael S. Tsirkin wrote:
> On Mon, Jul 13, 2026 at 07:37:50PM +0100, Daniel P. Berrangé wrote:
> > On Mon, Jul 13, 2026 at 08:55:37PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > > On 13.07.26 14:21, Daniel P. Berrangé wrote:
> > > > On Mon, Jul 13, 2026 at 01:55:48PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > > > > On 13.07.26 12:51, Daniel P. Berrangé wrote:
> > > > > > On Fri, Jul 10, 2026 at 10:17:58PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > > > > > > We are going to implement local-migration feature: some devices will be
> > > > > > > able to transfer open file descriptors through migration stream (which
> > > > > > > must UNIX domain socket for that purpose). This allows to transfer the
> > > > > > > whole backend state without reconnecting and restarting the backend
> > > > > > > service. For example, virtio-net will migrate its attached TAP netdev,
> > > > > > > together with its connected file descriptors.
> > > > > > >
> > > > > > > In this commit we introduce a migration parameter, which enables
> > > > > > > the feature for devices that support it (none at the moment).
> > > > > > >
> > > > > > > Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
> > > > > > > Acked-by: Markus Armbruster <armbru@redhat.com>
> > > > > > > Acked-by: Peter Xu <peterx@redhat.com>
> > > > > > > Reviewed-by: Ben Chaney <bchaney@akamai.com>
> > > > > > > ---
> > > > > > > include/migration/misc.h | 2 ++
> > > > > > > migration/options.c | 18 +++++++++++++++++-
> > > > > > > qapi/migration.json | 12 ++++++++++--
> > > > > > > 3 files changed, 29 insertions(+), 3 deletions(-)
> > > > > >
> > > > > >
> > > > > > > diff --git a/qapi/migration.json b/qapi/migration.json
> > > > > > > index 66bd900465a..03bdf19f3a6 100644
> > > > > > > --- a/qapi/migration.json
> > > > > > > +++ b/qapi/migration.json
> > > > > > > @@ -830,7 +830,8 @@
> > > > > > > 'zero-page-detection',
> > > > > > > 'direct-io',
> > > > > > > { 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] },
> > > > > > > - 'cpr-exec-command'] }
> > > > > > > + 'cpr-exec-command',
> > > > > > > + 'local'] }
> > > > > > > ##
> > > > > > > # @migrate-set-parameters:
> > > > > > > @@ -1012,6 +1013,12 @@
> > > > > > > # Must be set to the same value on both source and destination
> > > > > > > # before migration starts. (Since 11.1)
> > > > > > > #
> > > > > > > +# @local: Enable local migration for devices that support it. Backend
> > > > > > > +# state and its file descriptors can then be passed to the
> > > > > > > +# destination in the migration channel. The migration channel
> > > > > > > +# must be a Unix domain socket. Usually needs to be enabled per
> > > > > > > +# device. (Since 11.2)
> > > > > >
> > > > > > What happens when this is enabled and there are devices which
> > > > > > don't support it ?
> > > > >
> > > > > These devices are migrated as usual, ignoring "local" option.
> > > > >
> > > > > >
> > > > > > What happens when this is enabled and a device on the src
> > > > > > does not support it but a device on the (newer) QEMU dest
> > > > > > does support it ? How does the mgmt app know which devices
> > > > > > support it ?
> > > > >
> > > > > The only supporting device (TAP-backend) declares support by
> > > > > "local-migration-supported", so mgmt app should care to set
> > > > > same value for it on host and target (like for other options
> > > > > actually). Or simply relay on default, which is true starting
> > > > > from 11.2 Machine Type.
> > > > >
> > > > > >
> > > > > > Can we document the semantics in these cases.
> > > > >
> > > > > That's: "Usually needs to be enabled per device."
> > > >
> > > > ....except that we have it enabled by default for TAP backends
> > > > in new machine types.... except that it will then break if the
> > > > app/user has enabled any of the "forbidden" options.
> > > >
> > > > The way this doc is written makes it sound like "local" is an
> > > > optimization you can turn on in any scenario, and it will do
> > > > the right thing if the device supports it. That is very much
> > > > not the case - enabling this flag will definitely break many
> > > > configs out of the box, with new machine types due to the TAP
> > > > device defaults & incompatible options.
> > >
> > > Ok, now we decided to NOT make default support for TAP in newer
> > > machine types.
> > >
> > > So, enabling "local" is safe in existing scenarios, only explicit
> > > enabling TAP.local-migration-supported will change the behavior.
> > >
> > > Keep "local" documentation as is?
> > >
> > > I see the only way to make it more informative: directly describe
> > > connection with TAP.local-migration-supported, without general
> > > phrases about "devices that support" and "usual". We'll have to
> > > update it when more devices added (my next step is vhost-user-blk),
> > > but at least, it would be absolutely correct at the moment.
> >
> > How about....
> >
> > # @local: Permit the use of optimizations for local migration.
> > # This must only be set when both the source and destination
> > # QEMU processes are on the same OS and directly connected
> > # with a UNIX domain socket as the migration channel to enable
> > # use of file descriptor passing. Individual device backends
> > # may need additional configuration flags set to enable local
> > # migration optimizations. This will be documented against the
> > # device backends where it applies. (Since 11.2)
> >
>
> BTW surely qemu can just detect this? getsockname maybe?
QEMU might be given a UNIX socket that connects to a MITM proxy
that then uses TCP to reach the dest QEMU.
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 09/14] qapi: add local migration parameter
2026-07-14 7:55 ` Daniel P. Berrangé
@ 2026-07-14 8:02 ` Michael S. Tsirkin
2026-07-14 8:18 ` Daniel P. Berrangé
0 siblings, 1 reply; 44+ messages in thread
From: Michael S. Tsirkin @ 2026-07-14 8:02 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: Vladimir Sementsov-Ogievskiy, jasowang, armbru, peterx, farosas,
raphael.s.norwitz, bchaney, qemu-devel, pbonzini, yc-core,
mark.caveayland, Eric Blake
On Tue, Jul 14, 2026 at 08:55:28AM +0100, Daniel P. Berrangé wrote:
> On Mon, Jul 13, 2026 at 04:22:03PM -0400, Michael S. Tsirkin wrote:
> > On Mon, Jul 13, 2026 at 07:37:50PM +0100, Daniel P. Berrangé wrote:
> > > On Mon, Jul 13, 2026 at 08:55:37PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > > > On 13.07.26 14:21, Daniel P. Berrangé wrote:
> > > > > On Mon, Jul 13, 2026 at 01:55:48PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > > > > > On 13.07.26 12:51, Daniel P. Berrangé wrote:
> > > > > > > On Fri, Jul 10, 2026 at 10:17:58PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > > > > > > > We are going to implement local-migration feature: some devices will be
> > > > > > > > able to transfer open file descriptors through migration stream (which
> > > > > > > > must UNIX domain socket for that purpose). This allows to transfer the
> > > > > > > > whole backend state without reconnecting and restarting the backend
> > > > > > > > service. For example, virtio-net will migrate its attached TAP netdev,
> > > > > > > > together with its connected file descriptors.
> > > > > > > >
> > > > > > > > In this commit we introduce a migration parameter, which enables
> > > > > > > > the feature for devices that support it (none at the moment).
> > > > > > > >
> > > > > > > > Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
> > > > > > > > Acked-by: Markus Armbruster <armbru@redhat.com>
> > > > > > > > Acked-by: Peter Xu <peterx@redhat.com>
> > > > > > > > Reviewed-by: Ben Chaney <bchaney@akamai.com>
> > > > > > > > ---
> > > > > > > > include/migration/misc.h | 2 ++
> > > > > > > > migration/options.c | 18 +++++++++++++++++-
> > > > > > > > qapi/migration.json | 12 ++++++++++--
> > > > > > > > 3 files changed, 29 insertions(+), 3 deletions(-)
> > > > > > >
> > > > > > >
> > > > > > > > diff --git a/qapi/migration.json b/qapi/migration.json
> > > > > > > > index 66bd900465a..03bdf19f3a6 100644
> > > > > > > > --- a/qapi/migration.json
> > > > > > > > +++ b/qapi/migration.json
> > > > > > > > @@ -830,7 +830,8 @@
> > > > > > > > 'zero-page-detection',
> > > > > > > > 'direct-io',
> > > > > > > > { 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] },
> > > > > > > > - 'cpr-exec-command'] }
> > > > > > > > + 'cpr-exec-command',
> > > > > > > > + 'local'] }
> > > > > > > > ##
> > > > > > > > # @migrate-set-parameters:
> > > > > > > > @@ -1012,6 +1013,12 @@
> > > > > > > > # Must be set to the same value on both source and destination
> > > > > > > > # before migration starts. (Since 11.1)
> > > > > > > > #
> > > > > > > > +# @local: Enable local migration for devices that support it. Backend
> > > > > > > > +# state and its file descriptors can then be passed to the
> > > > > > > > +# destination in the migration channel. The migration channel
> > > > > > > > +# must be a Unix domain socket. Usually needs to be enabled per
> > > > > > > > +# device. (Since 11.2)
> > > > > > >
> > > > > > > What happens when this is enabled and there are devices which
> > > > > > > don't support it ?
> > > > > >
> > > > > > These devices are migrated as usual, ignoring "local" option.
> > > > > >
> > > > > > >
> > > > > > > What happens when this is enabled and a device on the src
> > > > > > > does not support it but a device on the (newer) QEMU dest
> > > > > > > does support it ? How does the mgmt app know which devices
> > > > > > > support it ?
> > > > > >
> > > > > > The only supporting device (TAP-backend) declares support by
> > > > > > "local-migration-supported", so mgmt app should care to set
> > > > > > same value for it on host and target (like for other options
> > > > > > actually). Or simply relay on default, which is true starting
> > > > > > from 11.2 Machine Type.
> > > > > >
> > > > > > >
> > > > > > > Can we document the semantics in these cases.
> > > > > >
> > > > > > That's: "Usually needs to be enabled per device."
> > > > >
> > > > > ....except that we have it enabled by default for TAP backends
> > > > > in new machine types.... except that it will then break if the
> > > > > app/user has enabled any of the "forbidden" options.
> > > > >
> > > > > The way this doc is written makes it sound like "local" is an
> > > > > optimization you can turn on in any scenario, and it will do
> > > > > the right thing if the device supports it. That is very much
> > > > > not the case - enabling this flag will definitely break many
> > > > > configs out of the box, with new machine types due to the TAP
> > > > > device defaults & incompatible options.
> > > >
> > > > Ok, now we decided to NOT make default support for TAP in newer
> > > > machine types.
> > > >
> > > > So, enabling "local" is safe in existing scenarios, only explicit
> > > > enabling TAP.local-migration-supported will change the behavior.
> > > >
> > > > Keep "local" documentation as is?
> > > >
> > > > I see the only way to make it more informative: directly describe
> > > > connection with TAP.local-migration-supported, without general
> > > > phrases about "devices that support" and "usual". We'll have to
> > > > update it when more devices added (my next step is vhost-user-blk),
> > > > but at least, it would be absolutely correct at the moment.
> > >
> > > How about....
> > >
> > > # @local: Permit the use of optimizations for local migration.
> > > # This must only be set when both the source and destination
> > > # QEMU processes are on the same OS and directly connected
> > > # with a UNIX domain socket as the migration channel to enable
> > > # use of file descriptor passing. Individual device backends
> > > # may need additional configuration flags set to enable local
> > > # migration optimizations. This will be documented against the
> > > # device backends where it applies. (Since 11.2)
> > >
> >
> > BTW surely qemu can just detect this? getsockname maybe?
>
> QEMU might be given a UNIX socket that connects to a MITM proxy
> that then uses TCP to reach the dest QEMU.
We could try to exchange FDs and see it it works I guess.
But anyway, my point is QEMU should validate it,
even if that's not enough of a signal to fully trust.
> With regards,
> Daniel
> --
> |: https://berrange.com ~~ https://hachyderm.io/@berrange :|
> |: https://libvirt.org ~~ https://entangle-photo.org :|
> |: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 09/14] qapi: add local migration parameter
2026-07-14 8:02 ` Michael S. Tsirkin
@ 2026-07-14 8:18 ` Daniel P. Berrangé
0 siblings, 0 replies; 44+ messages in thread
From: Daniel P. Berrangé @ 2026-07-14 8:18 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Vladimir Sementsov-Ogievskiy, jasowang, armbru, peterx, farosas,
raphael.s.norwitz, bchaney, qemu-devel, pbonzini, yc-core,
mark.caveayland, Eric Blake
On Tue, Jul 14, 2026 at 04:02:29AM -0400, Michael S. Tsirkin wrote:
> On Tue, Jul 14, 2026 at 08:55:28AM +0100, Daniel P. Berrangé wrote:
> > On Mon, Jul 13, 2026 at 04:22:03PM -0400, Michael S. Tsirkin wrote:
> > > On Mon, Jul 13, 2026 at 07:37:50PM +0100, Daniel P. Berrangé wrote:
> > > > On Mon, Jul 13, 2026 at 08:55:37PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > > > > On 13.07.26 14:21, Daniel P. Berrangé wrote:
> > > > > > On Mon, Jul 13, 2026 at 01:55:48PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > > > > > > On 13.07.26 12:51, Daniel P. Berrangé wrote:
> > > > > > > > On Fri, Jul 10, 2026 at 10:17:58PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > > > > > > > > We are going to implement local-migration feature: some devices will be
> > > > > > > > > able to transfer open file descriptors through migration stream (which
> > > > > > > > > must UNIX domain socket for that purpose). This allows to transfer the
> > > > > > > > > whole backend state without reconnecting and restarting the backend
> > > > > > > > > service. For example, virtio-net will migrate its attached TAP netdev,
> > > > > > > > > together with its connected file descriptors.
> > > > > > > > >
> > > > > > > > > In this commit we introduce a migration parameter, which enables
> > > > > > > > > the feature for devices that support it (none at the moment).
> > > > > > > > >
> > > > > > > > > Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
> > > > > > > > > Acked-by: Markus Armbruster <armbru@redhat.com>
> > > > > > > > > Acked-by: Peter Xu <peterx@redhat.com>
> > > > > > > > > Reviewed-by: Ben Chaney <bchaney@akamai.com>
> > > > > > > > > ---
> > > > > > > > > include/migration/misc.h | 2 ++
> > > > > > > > > migration/options.c | 18 +++++++++++++++++-
> > > > > > > > > qapi/migration.json | 12 ++++++++++--
> > > > > > > > > 3 files changed, 29 insertions(+), 3 deletions(-)
> > > > > > > >
> > > > > > > >
> > > > > > > > > diff --git a/qapi/migration.json b/qapi/migration.json
> > > > > > > > > index 66bd900465a..03bdf19f3a6 100644
> > > > > > > > > --- a/qapi/migration.json
> > > > > > > > > +++ b/qapi/migration.json
> > > > > > > > > @@ -830,7 +830,8 @@
> > > > > > > > > 'zero-page-detection',
> > > > > > > > > 'direct-io',
> > > > > > > > > { 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] },
> > > > > > > > > - 'cpr-exec-command'] }
> > > > > > > > > + 'cpr-exec-command',
> > > > > > > > > + 'local'] }
> > > > > > > > > ##
> > > > > > > > > # @migrate-set-parameters:
> > > > > > > > > @@ -1012,6 +1013,12 @@
> > > > > > > > > # Must be set to the same value on both source and destination
> > > > > > > > > # before migration starts. (Since 11.1)
> > > > > > > > > #
> > > > > > > > > +# @local: Enable local migration for devices that support it. Backend
> > > > > > > > > +# state and its file descriptors can then be passed to the
> > > > > > > > > +# destination in the migration channel. The migration channel
> > > > > > > > > +# must be a Unix domain socket. Usually needs to be enabled per
> > > > > > > > > +# device. (Since 11.2)
> > > > > > > >
> > > > > > > > What happens when this is enabled and there are devices which
> > > > > > > > don't support it ?
> > > > > > >
> > > > > > > These devices are migrated as usual, ignoring "local" option.
> > > > > > >
> > > > > > > >
> > > > > > > > What happens when this is enabled and a device on the src
> > > > > > > > does not support it but a device on the (newer) QEMU dest
> > > > > > > > does support it ? How does the mgmt app know which devices
> > > > > > > > support it ?
> > > > > > >
> > > > > > > The only supporting device (TAP-backend) declares support by
> > > > > > > "local-migration-supported", so mgmt app should care to set
> > > > > > > same value for it on host and target (like for other options
> > > > > > > actually). Or simply relay on default, which is true starting
> > > > > > > from 11.2 Machine Type.
> > > > > > >
> > > > > > > >
> > > > > > > > Can we document the semantics in these cases.
> > > > > > >
> > > > > > > That's: "Usually needs to be enabled per device."
> > > > > >
> > > > > > ....except that we have it enabled by default for TAP backends
> > > > > > in new machine types.... except that it will then break if the
> > > > > > app/user has enabled any of the "forbidden" options.
> > > > > >
> > > > > > The way this doc is written makes it sound like "local" is an
> > > > > > optimization you can turn on in any scenario, and it will do
> > > > > > the right thing if the device supports it. That is very much
> > > > > > not the case - enabling this flag will definitely break many
> > > > > > configs out of the box, with new machine types due to the TAP
> > > > > > device defaults & incompatible options.
> > > > >
> > > > > Ok, now we decided to NOT make default support for TAP in newer
> > > > > machine types.
> > > > >
> > > > > So, enabling "local" is safe in existing scenarios, only explicit
> > > > > enabling TAP.local-migration-supported will change the behavior.
> > > > >
> > > > > Keep "local" documentation as is?
> > > > >
> > > > > I see the only way to make it more informative: directly describe
> > > > > connection with TAP.local-migration-supported, without general
> > > > > phrases about "devices that support" and "usual". We'll have to
> > > > > update it when more devices added (my next step is vhost-user-blk),
> > > > > but at least, it would be absolutely correct at the moment.
> > > >
> > > > How about....
> > > >
> > > > # @local: Permit the use of optimizations for local migration.
> > > > # This must only be set when both the source and destination
> > > > # QEMU processes are on the same OS and directly connected
> > > > # with a UNIX domain socket as the migration channel to enable
> > > > # use of file descriptor passing. Individual device backends
> > > > # may need additional configuration flags set to enable local
> > > > # migration optimizations. This will be documented against the
> > > > # device backends where it applies. (Since 11.2)
> > > >
> > >
> > > BTW surely qemu can just detect this? getsockname maybe?
> >
> > QEMU might be given a UNIX socket that connects to a MITM proxy
> > that then uses TCP to reach the dest QEMU.
>
> We could try to exchange FDs and see it it works I guess.
See my earlier email response about QEMU live migration protocol being
awful. This is an example where we need to have a properly designed
protocol with a bidirectional feature negotation upfront, instead of
the current piles of hacks.
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v18 09/14] qapi: add local migration parameter
2026-07-13 18:37 ` Daniel P. Berrangé
2026-07-13 20:22 ` Michael S. Tsirkin
@ 2026-07-14 6:45 ` Vladimir Sementsov-Ogievskiy
1 sibling, 0 replies; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-14 6:45 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: jasowang, mst, armbru, peterx, farosas, raphael.s.norwitz,
bchaney, qemu-devel, pbonzini, yc-core, mark.caveayland,
Eric Blake
On 13.07.26 21:37, Daniel P. Berrangé wrote:
> On Mon, Jul 13, 2026 at 08:55:37PM +0300, Vladimir Sementsov-Ogievskiy wrote:
>> On 13.07.26 14:21, Daniel P. Berrangé wrote:
>>> On Mon, Jul 13, 2026 at 01:55:48PM +0300, Vladimir Sementsov-Ogievskiy wrote:
>>>> On 13.07.26 12:51, Daniel P. Berrangé wrote:
>>>>> On Fri, Jul 10, 2026 at 10:17:58PM +0300, Vladimir Sementsov-Ogievskiy wrote:
>>>>>> We are going to implement local-migration feature: some devices will be
>>>>>> able to transfer open file descriptors through migration stream (which
>>>>>> must UNIX domain socket for that purpose). This allows to transfer the
>>>>>> whole backend state without reconnecting and restarting the backend
>>>>>> service. For example, virtio-net will migrate its attached TAP netdev,
>>>>>> together with its connected file descriptors.
>>>>>>
>>>>>> In this commit we introduce a migration parameter, which enables
>>>>>> the feature for devices that support it (none at the moment).
>>>>>>
>>>>>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
>>>>>> Acked-by: Markus Armbruster <armbru@redhat.com>
>>>>>> Acked-by: Peter Xu <peterx@redhat.com>
>>>>>> Reviewed-by: Ben Chaney <bchaney@akamai.com>
>>>>>> ---
>>>>>> include/migration/misc.h | 2 ++
>>>>>> migration/options.c | 18 +++++++++++++++++-
>>>>>> qapi/migration.json | 12 ++++++++++--
>>>>>> 3 files changed, 29 insertions(+), 3 deletions(-)
>>>>>
>>>>>
>>>>>> diff --git a/qapi/migration.json b/qapi/migration.json
>>>>>> index 66bd900465a..03bdf19f3a6 100644
>>>>>> --- a/qapi/migration.json
>>>>>> +++ b/qapi/migration.json
>>>>>> @@ -830,7 +830,8 @@
>>>>>> 'zero-page-detection',
>>>>>> 'direct-io',
>>>>>> { 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] },
>>>>>> - 'cpr-exec-command'] }
>>>>>> + 'cpr-exec-command',
>>>>>> + 'local'] }
>>>>>> ##
>>>>>> # @migrate-set-parameters:
>>>>>> @@ -1012,6 +1013,12 @@
>>>>>> # Must be set to the same value on both source and destination
>>>>>> # before migration starts. (Since 11.1)
>>>>>> #
>>>>>> +# @local: Enable local migration for devices that support it. Backend
>>>>>> +# state and its file descriptors can then be passed to the
>>>>>> +# destination in the migration channel. The migration channel
>>>>>> +# must be a Unix domain socket. Usually needs to be enabled per
>>>>>> +# device. (Since 11.2)
>>>>>
>>>>> What happens when this is enabled and there are devices which
>>>>> don't support it ?
>>>>
>>>> These devices are migrated as usual, ignoring "local" option.
>>>>
>>>>>
>>>>> What happens when this is enabled and a device on the src
>>>>> does not support it but a device on the (newer) QEMU dest
>>>>> does support it ? How does the mgmt app know which devices
>>>>> support it ?
>>>>
>>>> The only supporting device (TAP-backend) declares support by
>>>> "local-migration-supported", so mgmt app should care to set
>>>> same value for it on host and target (like for other options
>>>> actually). Or simply relay on default, which is true starting
>>>> from 11.2 Machine Type.
>>>>
>>>>>
>>>>> Can we document the semantics in these cases.
>>>>
>>>> That's: "Usually needs to be enabled per device."
>>>
>>> ....except that we have it enabled by default for TAP backends
>>> in new machine types.... except that it will then break if the
>>> app/user has enabled any of the "forbidden" options.
>>>
>>> The way this doc is written makes it sound like "local" is an
>>> optimization you can turn on in any scenario, and it will do
>>> the right thing if the device supports it. That is very much
>>> not the case - enabling this flag will definitely break many
>>> configs out of the box, with new machine types due to the TAP
>>> device defaults & incompatible options.
>>
>> Ok, now we decided to NOT make default support for TAP in newer
>> machine types.
>>
>> So, enabling "local" is safe in existing scenarios, only explicit
>> enabling TAP.local-migration-supported will change the behavior.
>>
>> Keep "local" documentation as is?
>>
>> I see the only way to make it more informative: directly describe
>> connection with TAP.local-migration-supported, without general
>> phrases about "devices that support" and "usual". We'll have to
>> update it when more devices added (my next step is vhost-user-blk),
>> but at least, it would be absolutely correct at the moment.
>
> How about....
>
> # @local: Permit the use of optimizations for local migration.
> # This must only be set when both the source and destination
> # QEMU processes are on the same OS and directly connected
> # with a UNIX domain socket as the migration channel to enable
> # use of file descriptor passing. Individual device backends
> # may need additional configuration flags set to enable local
> # migration optimizations. This will be documented against the
> # device backends where it applies. (Since 11.2)
>
Sounds good, thanks!
--
Best regards,
Vladimir
^ permalink raw reply [flat|nested] 44+ messages in thread
* [PATCH v18 10/14] virtio-net: support local migration of backend
2026-07-10 19:17 [PATCH v18 00/14] virtio-net: live-TAP local migration Vladimir Sementsov-Ogievskiy
` (8 preceding siblings ...)
2026-07-10 19:17 ` [PATCH v18 09/14] qapi: add local migration parameter Vladimir Sementsov-Ogievskiy
@ 2026-07-10 19:17 ` Vladimir Sementsov-Ogievskiy
2026-07-10 19:18 ` [PATCH v18 11/14] net/tap: disable read polling for stopped VM Vladimir Sementsov-Ogievskiy
` (3 subsequent siblings)
13 siblings, 0 replies; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-10 19:17 UTC (permalink / raw)
To: jasowang, mst
Cc: armbru, peterx, farosas, raphael.s.norwitz, bchaney, vsementsov,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Jason Wang
Next commit will introduce live-migration (with fd-passing) for
TAP net backend. So, now we prepare virtio-net for it
Add virtio-net option local-migration, which is true by default,
but false for older machine types, which doesn't support the feature.
We introduce interface for live-migrating backends:
1. ->is_wait_incoming() handler, so that virtio-net knows, that
backend is not fully intialized, as it waits for incoming migration
stream.
2. MIG_PRI_BACKEND priority: backends should migrate with higher
priority than virtio-net, so that we can do final preparations
here in post-load handlers and be sure, that backends are already
prepared.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
---
hw/net/virtio-net.c | 89 +++++++++++++++++++++++++++++++++-
include/hw/virtio/virtio-net.h | 1 +
include/migration/vmstate.h | 2 +
include/net/net.h | 2 +
4 files changed, 93 insertions(+), 1 deletion(-)
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index f0e3beb2903..c6e7f7a370f 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -40,6 +40,7 @@
#include "migration/misc.h"
#include "standard-headers/linux/ethtool.h"
#include "system/system.h"
+#include "system/runstate.h"
#include "system/replay.h"
#include "trace.h"
#include "monitor/qdev.h"
@@ -3060,7 +3061,17 @@ static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue)
n->multiqueue = multiqueue;
virtio_net_change_num_queues(n, max * 2 + 1);
- virtio_net_set_queue_pairs(n);
+ /*
+ * virtio_net_set_multiqueue() called from set_features(0) on early
+ * reset, when peer may wait for incoming (and is not initialized
+ * yet).
+ * Don't worry about it: virtio_net_set_queue_pairs() will be called
+ * later from virtio_net_post_load_device(), and anyway will be
+ * no-op for local incoming migration with live backend passing.
+ */
+ if (!n->peers_wait_incoming) {
+ virtio_net_set_queue_pairs(n);
+ }
}
static int virtio_net_pre_load_queues(VirtIODevice *vdev, uint32_t n)
@@ -3089,6 +3100,17 @@ static void virtio_net_get_features(VirtIODevice *vdev, uint64_t *features,
virtio_add_feature_ex(features, VIRTIO_NET_F_MAC);
+ if (n->peers_wait_incoming) {
+ /*
+ * Excessive feature set is OK for early initialization when
+ * we wait for local incoming migration: actual guest-negotiated
+ * features will come with migration stream anyway. And we are sure
+ * that we support same host-features as source, because the backend
+ * is the same (the same TAP device, for example).
+ */
+ return;
+ }
+
if (!peer_has_vnet_hdr(n)) {
virtio_clear_feature_ex(features, VIRTIO_NET_F_CSUM);
virtio_clear_feature_ex(features, VIRTIO_NET_F_HOST_TSO4);
@@ -3185,6 +3207,7 @@ static int virtio_net_post_load_device(void *opaque, int version_id)
VirtIODevice *vdev = VIRTIO_DEVICE(n);
int i, link_down;
bool has_tunnel_hdr = virtio_has_tunnel_hdr(vdev->guest_features_ex);
+ Error *local_err = NULL;
trace_virtio_net_post_load_device();
virtio_net_set_mrg_rx_bufs(n, n->mergeable_rx_bufs,
@@ -3242,6 +3265,20 @@ static int virtio_net_post_load_device(void *opaque, int version_id)
}
virtio_net_commit_rss_config(n);
+
+ /*
+ * If live-migration is enabled for some backend, than backend
+ * has already been migrated at higher priority (MIG_PRI_BACKEND)
+ * and virtio_net_vnet_post_load() has already called
+ * peer_test_vnet_hdr(). Recompute host_features so that virtio-net
+ * reflects the capabilities of the restored backend.
+ */
+ virtio_net_get_features(vdev, &vdev->host_features, &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ return -EINVAL;
+ }
+
return 0;
}
@@ -3392,6 +3429,14 @@ static int virtio_net_vnet_post_load(void *opaque, int version_id)
{
struct VirtIONetMigTmp *tmp = opaque;
+ /*
+ * If live-migration is enabled for some backend, than backend
+ * has already been migrated at higher priority (MIG_PRI_BACKEND),
+ * so n->has_vnet_hdr can be refreshed from the live backend right
+ * here.
+ */
+ peer_test_vnet_hdr(tmp->parent);
+
if (tmp->has_vnet_hdr && !peer_has_vnet_hdr(tmp->parent)) {
error_report("virtio-net: saved image requires vnet_hdr=on");
return -EINVAL;
@@ -3871,6 +3916,42 @@ static bool failover_hide_primary_device(DeviceListener *listener,
return qatomic_read(&n->failover_primary_hidden);
}
+static bool virtio_net_check_peers_wait_incoming(VirtIONet *n, bool *waiting,
+ Error **errp)
+{
+ bool has_waiting = false;
+ bool has_not_waiting = false;
+
+ for (int i = 0; i < n->max_queue_pairs; i++) {
+ NetClientState *peer = n->nic->ncs[i].peer;
+ if (!peer) {
+ continue;
+ }
+
+ if (peer->info->is_wait_incoming &&
+ peer->info->is_wait_incoming(peer)) {
+ has_waiting = true;
+ } else {
+ has_not_waiting = true;
+ }
+
+ if (has_waiting && has_not_waiting) {
+ error_setg(errp, "Mixed peer states: some peers wait for incoming "
+ "migration while others don't");
+ return false;
+ }
+ }
+
+ if (has_waiting && !runstate_check(RUN_STATE_INMIGRATE)) {
+ error_setg(errp, "Peers wait for incoming, but it's not an incoming "
+ "migration.");
+ return false;
+ }
+
+ *waiting = has_waiting;
+ return true;
+}
+
static void virtio_net_device_realize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
@@ -4008,6 +4089,12 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
n->nic->ncs[i].do_not_pad = true;
}
+ if (!virtio_net_check_peers_wait_incoming(n, &n->peers_wait_incoming,
+ errp)) {
+ virtio_cleanup(vdev);
+ return;
+ }
+
peer_test_vnet_hdr(n);
if (peer_has_vnet_hdr(n)) {
n->host_hdr_len = sizeof(struct virtio_net_hdr);
diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h
index 371e3764282..8c967760c2a 100644
--- a/include/hw/virtio/virtio-net.h
+++ b/include/hw/virtio/virtio-net.h
@@ -230,6 +230,7 @@ struct VirtIONet {
struct EBPFRSSContext ebpf_rss;
uint32_t nr_ebpf_rss_fds;
char **ebpf_rss_fds;
+ bool peers_wait_incoming;
};
size_t virtio_net_handle_ctrl_iov(VirtIODevice *vdev,
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 1b7f295417e..bbc569adc52 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -178,6 +178,8 @@ typedef enum {
MIG_PRI_LOW, /* Must happen after default */
MIG_PRI_DEFAULT,
+ MIG_PRI_BACKEND, /* Must happen before emulated devices, */
+ /* e.g. virtio-net */
MIG_PRI_IOMMU, /* Must happen before PCI devices */
MIG_PRI_PCI_BUS, /* Must happen before IOMMU */
MIG_PRI_VIRTIO_MEM, /* Must happen before IOMMU */
diff --git a/include/net/net.h b/include/net/net.h
index bdf16bb3e64..658b0d0aaf1 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -81,6 +81,7 @@ typedef void (SocketReadStateFinalize)(SocketReadState *rs);
typedef void (NetAnnounce)(NetClientState *);
typedef bool (SetSteeringEBPF)(NetClientState *, int);
typedef bool (NetCheckPeerType)(NetClientState *, ObjectClass *, Error **);
+typedef bool (IsWaitIncoming)(NetClientState *);
typedef struct vhost_net *(GetVHostNet)(NetClientState *nc);
typedef struct NetClientInfo {
@@ -109,6 +110,7 @@ typedef struct NetClientInfo {
NetAnnounce *announce;
SetSteeringEBPF *set_steering_ebpf;
NetCheckPeerType *check_peer_type;
+ IsWaitIncoming *is_wait_incoming;
GetVHostNet *get_vhost_net;
} NetClientInfo;
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* [PATCH v18 11/14] net/tap: disable read polling for stopped VM
2026-07-10 19:17 [PATCH v18 00/14] virtio-net: live-TAP local migration Vladimir Sementsov-Ogievskiy
` (9 preceding siblings ...)
2026-07-10 19:17 ` [PATCH v18 10/14] virtio-net: support local migration of backend Vladimir Sementsov-Ogievskiy
@ 2026-07-10 19:18 ` Vladimir Sementsov-Ogievskiy
2026-07-10 19:18 ` [PATCH v18 12/14] net/tap: support local migration with virtio-net Vladimir Sementsov-Ogievskiy
` (2 subsequent siblings)
13 siblings, 0 replies; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-10 19:18 UTC (permalink / raw)
To: jasowang, mst
Cc: armbru, peterx, farosas, raphael.s.norwitz, bchaney, vsementsov,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Jason Wang
Polling when VM is stopped doesn't make real sense, as stopped VM can't
handle incoming traffic anyway.
And it's critical for introduction of local TAP migration
feature in the next commit: the TAP device will be transferred
to the target (open fd will be passed through migration channel),
and if we continue polling on source, we may get a package, which
we'll never handle on source (already stopped), it will be lost.
Better is save this package for target VM to handle.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
---
net/tap.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/net/tap.c b/net/tap.c
index b5be456936d..605e5867b01 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -36,6 +36,7 @@
#include "net/net.h"
#include "clients.h"
#include "monitor/monitor.h"
+#include "system/runstate.h"
#include "system/system.h"
#include "qapi/error.h"
#include "qemu/cutils.h"
@@ -92,6 +93,8 @@ struct TAPState {
Notifier exit;
int queue_index;
+ bool read_poll_detached;
+ VMChangeStateEntry *vmstate;
};
static void launch_script(const char *setup_script, const char *ifname,
@@ -146,6 +149,23 @@ static void tap_read_poll(TAPState *s, bool enable)
tap_update_fd_handler(s);
}
+static void tap_vm_state_change(void *opaque, bool running, RunState state)
+{
+ TAPState *s = opaque;
+
+ if (running) {
+ if (s->read_poll_detached) {
+ tap_read_poll(s, true);
+ s->read_poll_detached = false;
+ }
+ } else if (state == RUN_STATE_FINISH_MIGRATE) {
+ if (s->read_poll) {
+ s->read_poll_detached = true;
+ tap_read_poll(s, false);
+ }
+ }
+}
+
static void tap_write_poll(TAPState *s, bool enable)
{
s->write_poll = enable;
@@ -376,6 +396,11 @@ static void tap_cleanup(NetClientState *nc)
s->exit.notify = NULL;
}
+ if (s->vmstate) {
+ qemu_del_vm_change_state_handler(s->vmstate);
+ s->vmstate = NULL;
+ }
+
tap_read_poll(s, false);
tap_write_poll(s, false);
close(s->fd);
@@ -815,6 +840,9 @@ static bool net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
int sndbuf =
(tap->has_sndbuf && tap->sndbuf) ? MIN(tap->sndbuf, INT_MAX) : INT_MAX;
+ s->read_poll_detached = false;
+ s->vmstate = qemu_add_vm_change_state_handler(tap_vm_state_change, s);
+
if (!tap_set_sndbuf(fd, sndbuf, sndbuf_required ? errp : NULL) &&
sndbuf_required) {
goto failed;
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* [PATCH v18 12/14] net/tap: support local migration with virtio-net
2026-07-10 19:17 [PATCH v18 00/14] virtio-net: live-TAP local migration Vladimir Sementsov-Ogievskiy
` (10 preceding siblings ...)
2026-07-10 19:18 ` [PATCH v18 11/14] net/tap: disable read polling for stopped VM Vladimir Sementsov-Ogievskiy
@ 2026-07-10 19:18 ` Vladimir Sementsov-Ogievskiy
2026-07-13 7:57 ` Markus Armbruster
` (2 more replies)
2026-07-10 19:18 ` [PATCH v18 13/14] tests/functional: add skipWithoutSudo() decorator Vladimir Sementsov-Ogievskiy
2026-07-10 19:18 ` [PATCH v18 14/14] tests/functional: add test_tap_migration Vladimir Sementsov-Ogievskiy
13 siblings, 3 replies; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-10 19:18 UTC (permalink / raw)
To: jasowang, mst
Cc: armbru, peterx, farosas, raphael.s.norwitz, bchaney, vsementsov,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Philippe Mathieu-Daudé, Zhao Liu, Jason Wang, Eric Blake
Support transferring of TAP state (including open fd).
Add new property "local-migration-supported", which defines
whether local-migration is actually supported for this TAP device.
Starting from 11.2 QEMU Machine Types it's enabled by default.
Note, that local-migration (including migrating opened FDs
through migration channel, which must be UNIX socket), is
enabled by global "local" migration parameters. But individual
devices may have additional options to enable/disable it
per device.
The tricky thing, is that we need to know, should we do
call open/connect in TAP initialization code. That means,
that on incoming migration we want to know status of
"local" migration parameter at time of creating TAP device.
So, currently, for incoming local migration only TAP device
created through QMP is supported, as you need to set migration
parameter first (through QMP as well).
So the full picture is:
On source, to start outgoing "local" migration you need:
- migration parameter "local" set to true
- "local-migration-supported" TAP option set to true (the
default, starting from 11.2 QEMU Machine Types)
If at least one of these options is not set, TAP backend
doesn't participate in migration.
On target, things are more difficult:
Same, you need both "local" and "local-migration-supported"
be set. And same, if one of them is not set, TAP backend
is initialized as usual, and doesn't accept any incoming
state.
Additionally, if you are going to set "local", it must be
set before creating the TAP device. If TAP device created
with "local" unset, it initializes as usual. If you enable
"local" after it and start incoming migration, it will fail
in .pre_load handler of TAP backend.
Moreover, there are interface restrictions: if you create TAP
device when QEMU is in INCOMING state, and both "local"
and "local-migration-supported" set, most of TAP options are
not allowed, and script/downscript are required to be explicitly
unset (set to "" or "no").
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
---
In v17 thread we considered making new qapi parameters "unstable".
Still, it was because of doubtful "incoming-fds" parameter. In this
v18 it's completely removed, and we may live only with "local"
migration parameter + "local-migration-supported" TAP option.
This way the series doesn't conflict with suggested by Peter
[PATCH RFC 0/2] migration/vl: new -incoming config:* for early migration parameters
So, I think, worth making one more iteration as "stable candidate".
If still some doubts, not problem for me to resend with "unstable" features.
hw/core/machine.c | 7 ++
net/tap.c | 162 ++++++++++++++++++++++++++++++++++++++++++++--
qapi/net.json | 23 ++++++-
3 files changed, 185 insertions(+), 7 deletions(-)
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 15886a56b19..2a1abb29041 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -36,10 +36,16 @@
#include "hw/virtio/virtio-pci.h"
#include "hw/virtio/virtio-net.h"
#include "hw/virtio/virtio-iommu.h"
+#include "net/tap.h"
#include "hw/acpi/generic_event_device.h"
#include "qemu/audio.h"
#include "hw/arm/smmuv3.h"
+/*
+ * TODO: When hw_compat_11_1 appears, local-migration-supported=false
+ * for TYPE_TAP_NETDEV should be move to it from hw_compat_11_0.
+ */
+
GlobalProperty hw_compat_11_0[] = {
{ "chardev-vc", "encoding", "cp437" },
{ "tpm-crb", "cap-chunk", "off" },
@@ -49,6 +55,7 @@ GlobalProperty hw_compat_11_0[] = {
{ TYPE_ARM_SMMUV3, "ssidsize", "0" },
{ TYPE_ARM_SMMUV3, "oas", "44" },
{ "migration", "switchover-ack-legacy", "on" },
+ { TYPE_TAP_NETDEV, "local-migration-supported", "false" },
};
const size_t hw_compat_11_0_len = G_N_ELEMENTS(hw_compat_11_0);
diff --git a/net/tap.c b/net/tap.c
index 605e5867b01..adcac70b7ea 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -38,12 +38,17 @@
#include "monitor/monitor.h"
#include "system/runstate.h"
#include "system/system.h"
+#include "migration/misc.h"
#include "qapi/error.h"
#include "qemu/cutils.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
#include "qemu/sockets.h"
#include "hw/virtio/vhost.h"
+#include "hw/core/vmstate-if.h"
+#include "migration/vmstate.h"
+#include "qom/object.h"
+#include "qom/compat-properties.h"
#include "net/tap.h"
#include "net/util.h"
@@ -71,6 +76,8 @@ static const int kernel_feature_bits[] = {
OBJECT_DECLARE_SIMPLE_TYPE(TAPState, TAP_NETDEV)
+static const VMStateDescription vmstate_tap;
+
struct TAPState {
Object parent_obj;
@@ -95,6 +102,7 @@ struct TAPState {
int queue_index;
bool read_poll_detached;
VMChangeStateEntry *vmstate;
+ bool local_migration_supported;
};
static void launch_script(const char *setup_script, const char *ifname,
@@ -405,6 +413,8 @@ static void tap_cleanup(NetClientState *nc)
tap_write_poll(s, false);
close(s->fd);
s->fd = -1;
+
+ vmstate_unregister(VMSTATE_IF(s), &vmstate_tap, s);
}
static void tap_poll(NetClientState *nc, bool enable)
@@ -441,6 +451,73 @@ static VHostNetState *tap_get_vhost_net(NetClientState *nc)
return s->vhost_net;
}
+static bool tap_is_wait_incoming(NetClientState *nc)
+{
+ TAPState *s = container_of(nc, TAPState, 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 bool tap_needed(void *opaque)
+{
+ TAPState *s = opaque;
+
+ return s->local_migration_supported && migrate_local();
+}
+
+static const VMStateDescription vmstate_tap = {
+ .name = "net-tap",
+ .priority = MIG_PRI_BACKEND,
+ .pre_load = tap_pre_load,
+ .post_load = tap_post_load,
+ .needed = tap_needed,
+ .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()
+ }
+};
static char *tap_vmstate_if_get_id(VMStateIf *obj)
{
@@ -449,17 +526,42 @@ static char *tap_vmstate_if_get_id(VMStateIf *obj)
return res;
}
+static bool tap_get_local_migration_supported_prop(Object *obj, Error **errp)
+{
+ TAPState *s = TAP_NETDEV(obj);
+ return s->local_migration_supported;
+}
+
+static void tap_set_local_migration_supported_prop(Object *obj, bool value,
+ Error **errp)
+{
+ TAPState *s = TAP_NETDEV(obj);
+ s->local_migration_supported = value;
+}
+
+static void tap_instance_init(Object *obj)
+{
+ TAPState *s = TAP_NETDEV(obj);
+ s->local_migration_supported = true;
+}
+
static void tap_class_init(ObjectClass *klass, const void *data)
{
VMStateIfClass *vc = VMSTATE_IF_CLASS(klass);
vc->get_id = tap_vmstate_if_get_id;
+
+ object_class_property_add_bool(klass, "local-migration-supported",
+ tap_get_local_migration_supported_prop,
+ tap_set_local_migration_supported_prop);
}
static const TypeInfo tap_netdev_info = {
.name = TYPE_TAP_NETDEV,
.parent = TYPE_OBJECT,
.instance_size = sizeof(TAPState),
+ .instance_init = tap_instance_init,
+ .instance_post_init = object_apply_compat_props,
.class_init = tap_class_init,
.interfaces = (const InterfaceInfo[]) {
{ TYPE_VMSTATE_IF },
@@ -492,13 +594,16 @@ 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,
};
static TAPState *new_tap(NetClientState *peer,
const char *model,
const char *name,
- int queue_index)
+ int queue_index,
+ bool has_local_migration_supported,
+ bool local_migration_supported)
{
TAPState *s = TAP_NETDEV(object_new(TYPE_TAP_NETDEV));
@@ -507,6 +612,12 @@ static TAPState *new_tap(NetClientState *peer,
s->queue_index = queue_index;
+ if (has_local_migration_supported) {
+ s->local_migration_supported = local_migration_supported;
+ }
+
+ vmstate_register(VMSTATE_IF(s), VMSTATE_INSTANCE_ID_ANY, &vmstate_tap, s);
+
return s;
}
@@ -515,10 +626,14 @@ static TAPState *net_tap_fd_init(NetClientState *peer,
const char *name,
int fd,
int vnet_hdr,
- int queue_index)
+ int queue_index,
+ bool has_local_migration_supported,
+ bool local_migration_supported)
{
NetOffloads ol = {};
- TAPState *s = new_tap(peer, model, name, queue_index);
+ TAPState *s = new_tap(peer, model, name, queue_index,
+ has_local_migration_supported,
+ local_migration_supported);
s->fd = fd;
s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0;
@@ -755,7 +870,7 @@ int net_init_bridge(const Netdev *netdev, const char *name,
close(fd);
return -1;
}
- s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr, 0);
+ s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr, 0, true, false);
qemu_set_info_str(&s->nc, "helper=%s,br=%s", helper, br);
@@ -835,7 +950,9 @@ static bool net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
Error **errp)
{
TAPState *s = net_tap_fd_init(peer, tap->helper ? "bridge" : "tap",
- name, fd, vnet_hdr, queue_index);
+ name, fd, vnet_hdr, queue_index,
+ tap->has_local_migration_supported,
+ tap->local_migration_supported);
bool sndbuf_required = tap->has_sndbuf;
int sndbuf =
(tap->has_sndbuf && tap->sndbuf) ? MIN(tap->sndbuf, INT_MAX) : INT_MAX;
@@ -983,6 +1100,7 @@ int net_init_tap(const Netdev *netdev, const char *name,
/* for the no-fd, no-helper case */
char ifname[128];
int *fds = NULL, *vhost_fds = NULL;
+ bool incoming_fds;
assert(netdev->type == NET_CLIENT_DRIVER_TAP);
tap = &netdev->u.tap;
@@ -1005,6 +1123,23 @@ int net_init_tap(const Netdev *netdev, const char *name,
return -1;
}
+ incoming_fds = tap->local_migration_supported && migrate_local() &&
+ runstate_check(RUN_STATE_INMIGRATE);
+
+ if (incoming_fds &&
+ (tap->fd || tap->fds || tap->helper || tap->br || tap->ifname ||
+ tap->has_sndbuf || tap->has_vnet_hdr ||
+ !tap_is_explicit_no_script(tap->script) ||
+ !tap_is_explicit_no_script(tap->downscript))) {
+ error_setg(errp, "Local incoming migration of TAP device (-incoming, "
+ "migration parameter @local is set, "
+ "TAP parameter @local-migration-supported is set) "
+ "is incompatible with "
+ "fd=, fds=, helper=, br=, ifname=, sndbuf= and vnet_hdr=, "
+ "and requires explicit empty script= and downscript=");
+ return -1;
+ }
+
queues = tap_parse_fds_and_queues(tap, &fds, errp);
if (queues < 0) {
return -1;
@@ -1023,7 +1158,22 @@ int net_init_tap(const Netdev *netdev, const char *name,
goto fail;
}
- if (fds) {
+ if (incoming_fds) {
+ for (i = 0; i < queues; i++) {
+ TAPState *s = new_tap(peer, "tap", name, i,
+ tap->has_local_migration_supported,
+ tap->local_migration_supported);
+ qemu_set_info_str(&s->nc, "incoming");
+
+ 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 ada0329ef9d..2070979f20e 100644
--- a/qapi/net.json
+++ b/qapi/net.json
@@ -435,6 +435,26 @@
# @poll-us: maximum number of microseconds that could be spent on busy
# polling for tap (since 2.7)
#
+# @local-migration-supported: enable local migration for this TAP
+# backend. When set, local migration is enabled/disabled by
+# migration parameter @local for this TAP backend. When unset,
+# migration parameter @local is ignored for this TAP backend.
+# To be able to do incoming local migration of TAP backend,
+# migration parameter @local must be set _before_ creating the
+# TAP backend. Otherwise, TAP backend is initialized as usual,
+# opening/creating TAP devices in kernel. In this case further
+# local incoming migration (with migration parameter @local set
+# after creating TAP backend with @local-migration-supporeted
+# parameter set) will simply fail.
+# Moreover, when QEMU is in incoming migration state, migration
+# parameter @local is set and @local-migration-supported is set,
+# following options are not supported and must not be set:
+# @ds, @fds, @helper, @br, @ifname, @sndbuf, @vnet_hdr.
+# Additionally in this case @script and @downscipt must be
+# explicitly disabled (empty strings or "no").
+# (Since 11.2. Defaults to true for QEMU Machine Types >= 11.2,
+# and to false for QEMU Machine Types < 11.2)
+#
# Since: 1.2
##
{ 'struct': 'NetdevTapOptions',
@@ -453,7 +473,8 @@
'*vhostfds': 'str',
'*vhostforce': 'bool',
'*queues': 'uint32',
- '*poll-us': 'uint32'} }
+ '*poll-us': 'uint32',
+ '*local-migration-supported': 'bool' } }
##
# @NetdevSocketOptions:
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* Re: [PATCH v18 12/14] net/tap: support local migration with virtio-net
2026-07-10 19:18 ` [PATCH v18 12/14] net/tap: support local migration with virtio-net Vladimir Sementsov-Ogievskiy
@ 2026-07-13 7:57 ` Markus Armbruster
2026-07-13 11:51 ` Vladimir Sementsov-Ogievskiy
2026-07-13 11:17 ` Daniel P. Berrangé
2026-07-13 19:36 ` Michael S. Tsirkin
2 siblings, 1 reply; 44+ messages in thread
From: Markus Armbruster @ 2026-07-13 7:57 UTC (permalink / raw)
To: Vladimir Sementsov-Ogievskiy
Cc: jasowang, mst, peterx, farosas, raphael.s.norwitz, bchaney,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Philippe Mathieu-Daudé, Zhao Liu, Jason Wang, Eric Blake
Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> writes:
> Support transferring of TAP state (including open fd).
>
> Add new property "local-migration-supported", which defines
> whether local-migration is actually supported for this TAP device.
> Starting from 11.2 QEMU Machine Types it's enabled by default.
>
> Note, that local-migration (including migrating opened FDs
Scratch the comma after "Note", ...
> through migration channel, which must be UNIX socket), is
... and the one before "is".
> enabled by global "local" migration parameters. But individual
> devices may have additional options to enable/disable it
> per device.
>
> The tricky thing, is that we need to know, should we do
Do you mean "The tricky thing is that we ween to know whether to call"?
> call open/connect in TAP initialization code. That means,
> that on incoming migration we want to know status of
> "local" migration parameter at time of creating TAP device.
> So, currently, for incoming local migration only TAP device
> created through QMP is supported, as you need to set migration
> parameter first (through QMP as well).
>
> So the full picture is:
>
> On source, to start outgoing "local" migration you need:
>
> - migration parameter "local" set to true
> - "local-migration-supported" TAP option set to true (the
> default, starting from 11.2 QEMU Machine Types)
>
> If at least one of these options is not set, TAP backend
> doesn't participate in migration.
>
> On target, things are more difficult:
>
> Same, you need both "local" and "local-migration-supported"
> be set. And same, if one of them is not set, TAP backend
> is initialized as usual, and doesn't accept any incoming
> state.
>
> Additionally, if you are going to set "local", it must be
> set before creating the TAP device. If TAP device created
> with "local" unset, it initializes as usual. If you enable
> "local" after it and start incoming migration, it will fail
> in .pre_load handler of TAP backend.
Out of curiosity, how would such a failure look like?
> Moreover, there are interface restrictions: if you create TAP
> device when QEMU is in INCOMING state, and both "local"
> and "local-migration-supported" set, most of TAP options are
> not allowed, and script/downscript are required to be explicitly
> unset (set to "" or "no").
>
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
> ---
>
> In v17 thread we considered making new qapi parameters "unstable".
> Still, it was because of doubtful "incoming-fds" parameter. In this
> v18 it's completely removed, and we may live only with "local"
> migration parameter + "local-migration-supported" TAP option.
> This way the series doesn't conflict with suggested by Peter
> [PATCH RFC 0/2] migration/vl: new -incoming config:* for early migration parameters
> So, I think, worth making one more iteration as "stable candidate".
> If still some doubts, not problem for me to resend with "unstable" features.
Up to the migration maintainers, I think.
[...]
> diff --git a/qapi/net.json b/qapi/net.json
> index ada0329ef9d..2070979f20e 100644
> --- a/qapi/net.json
> +++ b/qapi/net.json
> @@ -435,6 +435,26 @@
> # @poll-us: maximum number of microseconds that could be spent on busy
> # polling for tap (since 2.7)
> #
> +# @local-migration-supported: enable local migration for this TAP
> +# backend. When set, local migration is enabled/disabled by
> +# migration parameter @local for this TAP backend. When unset,
> +# migration parameter @local is ignored for this TAP backend.
> +# To be able to do incoming local migration of TAP backend,
Suggest "of a TAP backend", or maybe "of any TAP backend".
> +# migration parameter @local must be set _before_ creating the
> +# TAP backend. Otherwise, TAP backend is initialized as usual,
> +# opening/creating TAP devices in kernel. In this case further
> +# local incoming migration (with migration parameter @local set
> +# after creating TAP backend with @local-migration-supporeted
> +# parameter set) will simply fail.
> +# Moreover, when QEMU is in incoming migration state, migration
> +# parameter @local is set and @local-migration-supported is set,
> +# following options are not supported and must not be set:
the following options
> +# @ds, @fds, @helper, @br, @ifname, @sndbuf, @vnet_hdr.
@ds does not exist. Do you mean @fd?
> +# Additionally in this case @script and @downscipt must be
> +# explicitly disabled (empty strings or "no").
"no" is deprecated. Scratch the parenthesis?
> +# (Since 11.2. Defaults to true for QEMU Machine Types >= 11.2,
> +# and to false for QEMU Machine Types < 11.2)
We commonly write this like
# Defaults to true for QEMU Machine Types >= 11.2, and to false
# for QEMU Machine Types < 11.2. (Since 11.2)
> +#
> # Since: 1.2
> ##
> { 'struct': 'NetdevTapOptions',
> @@ -453,7 +473,8 @@
> '*vhostfds': 'str',
> '*vhostforce': 'bool',
> '*queues': 'uint32',
> - '*poll-us': 'uint32'} }
> + '*poll-us': 'uint32',
> + '*local-migration-supported': 'bool' } }
>
> ##
> # @NetdevSocketOptions:
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 12/14] net/tap: support local migration with virtio-net
2026-07-13 7:57 ` Markus Armbruster
@ 2026-07-13 11:51 ` Vladimir Sementsov-Ogievskiy
2026-07-13 13:29 ` Markus Armbruster
0 siblings, 1 reply; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-13 11:51 UTC (permalink / raw)
To: Markus Armbruster
Cc: jasowang, mst, peterx, farosas, raphael.s.norwitz, bchaney,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Philippe Mathieu-Daudé, Zhao Liu, Jason Wang, Eric Blake
On 13.07.26 10:57, Markus Armbruster wrote:
> Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> writes:
>
>> Support transferring of TAP state (including open fd).
>>
>> Add new property "local-migration-supported", which defines
>> whether local-migration is actually supported for this TAP device.
>> Starting from 11.2 QEMU Machine Types it's enabled by default.
>>
>> Note, that local-migration (including migrating opened FDs
>
> Scratch the comma after "Note", ...
>
>> through migration channel, which must be UNIX socket), is
>
> ... and the one before "is".
>
>> enabled by global "local" migration parameters. But individual
>> devices may have additional options to enable/disable it
>> per device.
>>
>> The tricky thing, is that we need to know, should we do
>
> Do you mean "The tricky thing is that we ween to know whether to call"?
yes
>
>> call open/connect in TAP initialization code. That means,
>> that on incoming migration we want to know status of
>> "local" migration parameter at time of creating TAP device.
>> So, currently, for incoming local migration only TAP device
>> created through QMP is supported, as you need to set migration
>> parameter first (through QMP as well).
>>
>> So the full picture is:
>>
>> On source, to start outgoing "local" migration you need:
>>
>> - migration parameter "local" set to true
>> - "local-migration-supported" TAP option set to true (the
>> default, starting from 11.2 QEMU Machine Types)
>>
>> If at least one of these options is not set, TAP backend
>> doesn't participate in migration.
>>
>> On target, things are more difficult:
>>
>> Same, you need both "local" and "local-migration-supported"
>> be set. And same, if one of them is not set, TAP backend
>> is initialized as usual, and doesn't accept any incoming
>> state.
>>
>> Additionally, if you are going to set "local", it must be
>> set before creating the TAP device. If TAP device created
>> with "local" unset, it initializes as usual. If you enable
>> "local" after it and start incoming migration, it will fail
>> in .pre_load handler of TAP backend.
>
> Out of curiosity, how would such a failure look like?
TAP is initialized as usual, so the answer depends on the options.
For incoming migration we should open TAP without "ifname" argument.
If so (and "local" is not set), new TAP device will be created. It
shouldn't trigger any additional problems I think.
Checking with test:
diff --git a/tests/functional/x86_64/test_tap_migration.py b/tests/functional/x86_64/test_tap_migration.py
index bcc0a60dfe9..94982bc7b55 100755
--- a/tests/functional/x86_64/test_tap_migration.py
+++ b/tests/functional/x86_64/test_tap_migration.py
@@ -316,8 +316,8 @@ def prepare_and_launch_vm(
else:
tap_name = TAP_ID
- self.set_migration_capabilities(vm, local)
self.add_virtio_net(vm, vhost, tap_name, local, incoming)
+ self.set_migration_capabilities(vm, local)
def add_virtio_net(
self, vm, vhost: bool, tap_name: str, local: bool, incoming: bool
Leads to
qemu-system-x86_64: TAP is already initialized and cannot receive incoming fd
qemu-system-x86_64: warning: qemu_fclose: received fd 27 was never claimed
qemu-system-x86_64: load of migration failed: Invalid argument: error while loading state for instance 0x0 of device 'netdev.1/0/net-tap': pre load hook failed for: 'net-tap', version_id: 0, minimum version_id: 0, ret: -22
on target, and failed migration, as expected.
>
>> Moreover, there are interface restrictions: if you create TAP
>> device when QEMU is in INCOMING state, and both "local"
>> and "local-migration-supported" set, most of TAP options are
>> not allowed, and script/downscript are required to be explicitly
>> unset (set to "" or "no").
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
>> ---
>>
>> In v17 thread we considered making new qapi parameters "unstable".
>> Still, it was because of doubtful "incoming-fds" parameter. In this
>> v18 it's completely removed, and we may live only with "local"
>> migration parameter + "local-migration-supported" TAP option.
>> This way the series doesn't conflict with suggested by Peter
>> [PATCH RFC 0/2] migration/vl: new -incoming config:* for early migration parameters
>> So, I think, worth making one more iteration as "stable candidate".
>> If still some doubts, not problem for me to resend with "unstable" features.
>
> Up to the migration maintainers, I think.
>
> [...]
>
>> diff --git a/qapi/net.json b/qapi/net.json
>> index ada0329ef9d..2070979f20e 100644
>> --- a/qapi/net.json
>> +++ b/qapi/net.json
>> @@ -435,6 +435,26 @@
>> # @poll-us: maximum number of microseconds that could be spent on busy
>> # polling for tap (since 2.7)
>> #
>> +# @local-migration-supported: enable local migration for this TAP
>> +# backend. When set, local migration is enabled/disabled by
>> +# migration parameter @local for this TAP backend. When unset,
>> +# migration parameter @local is ignored for this TAP backend.
>> +# To be able to do incoming local migration of TAP backend,
>
> Suggest "of a TAP backend", or maybe "of any TAP backend".
>
>> +# migration parameter @local must be set _before_ creating the
>> +# TAP backend. Otherwise, TAP backend is initialized as usual,
>> +# opening/creating TAP devices in kernel. In this case further
>> +# local incoming migration (with migration parameter @local set
>> +# after creating TAP backend with @local-migration-supporeted
>> +# parameter set) will simply fail.
>> +# Moreover, when QEMU is in incoming migration state, migration
>> +# parameter @local is set and @local-migration-supported is set,
>> +# following options are not supported and must not be set:
>
> the following options
>
>> +# @ds, @fds, @helper, @br, @ifname, @sndbuf, @vnet_hdr.
>
> @ds does not exist. Do you mean @fd?
yes
>
>> +# Additionally in this case @script and @downscipt must be
>> +# explicitly disabled (empty strings or "no").
>
> "no" is deprecated. Scratch the parenthesis?
Unsure. I'd keep until it finally removed after deprecation period.
>
>> +# (Since 11.2. Defaults to true for QEMU Machine Types >= 11.2,
>> +# and to false for QEMU Machine Types < 11.2)
>
> We commonly write this like
>
> # Defaults to true for QEMU Machine Types >= 11.2, and to false
> # for QEMU Machine Types < 11.2. (Since 11.2)
ok.
>
>> +#
>> # Since: 1.2
>> ##
>> { 'struct': 'NetdevTapOptions',
>> @@ -453,7 +473,8 @@
>> '*vhostfds': 'str',
>> '*vhostforce': 'bool',
>> '*queues': 'uint32',
>> - '*poll-us': 'uint32'} }
>> + '*poll-us': 'uint32',
>> + '*local-migration-supported': 'bool' } }
>>
>> ##
>> # @NetdevSocketOptions:
>
Thanks!
--
Best regards,
Vladimir
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 12/14] net/tap: support local migration with virtio-net
2026-07-13 11:51 ` Vladimir Sementsov-Ogievskiy
@ 2026-07-13 13:29 ` Markus Armbruster
2026-07-13 15:08 ` Vladimir Sementsov-Ogievskiy
0 siblings, 1 reply; 44+ messages in thread
From: Markus Armbruster @ 2026-07-13 13:29 UTC (permalink / raw)
To: Vladimir Sementsov-Ogievskiy
Cc: Markus Armbruster, jasowang, mst, peterx, farosas,
raphael.s.norwitz, bchaney, qemu-devel, berrange, pbonzini,
yc-core, mark.caveayland, Philippe Mathieu-Daudé, Zhao Liu,
Jason Wang, Eric Blake
Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> writes:
> On 13.07.26 10:57, Markus Armbruster wrote:
>> Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> writes:
>>
>>> Support transferring of TAP state (including open fd).
>>>
>>> Add new property "local-migration-supported", which defines
>>> whether local-migration is actually supported for this TAP device.
>>> Starting from 11.2 QEMU Machine Types it's enabled by default.
>>>
>>> Note, that local-migration (including migrating opened FDs
>>
>> Scratch the comma after "Note", ...
>>
>>> through migration channel, which must be UNIX socket), is
>>
>> ... and the one before "is".
>>
>>> enabled by global "local" migration parameters. But individual
>>> devices may have additional options to enable/disable it
>>> per device.
>>>
>>> The tricky thing, is that we need to know, should we do
>>
>> Do you mean "The tricky thing is that we ween to know whether to call"?
>
> yes
>
>>
>>> call open/connect in TAP initialization code. That means,
>>> that on incoming migration we want to know status of
>>> "local" migration parameter at time of creating TAP device.
>>> So, currently, for incoming local migration only TAP device
>>> created through QMP is supported, as you need to set migration
>>> parameter first (through QMP as well).
Possibly clearer:
The tricky thing is that we need to know whether to call open/connect
in TAP initialization code, i.e. we need to know the value of
migration parameter "local" when creating the TAP device. For
incoming migration, we can know only for TAP devices created with QMP
after setting the migration parameter with QMP.
>>>
>>> So the full picture is:
>>>
>>> On source, to start outgoing "local" migration you need:
>>>
>>> - migration parameter "local" set to true
>>> - "local-migration-supported" TAP option set to true (the
>>> default, starting from 11.2 QEMU Machine Types)
>>>
>>> If at least one of these options is not set, TAP backend
>>> doesn't participate in migration.
>>>
>>> On target, things are more difficult:
>>>
>>> Same, you need both "local" and "local-migration-supported"
>>> be set. And same, if one of them is not set, TAP backend
>>> is initialized as usual, and doesn't accept any incoming
>>> state.
>>>
>>> Additionally, if you are going to set "local", it must be
>>> set before creating the TAP device. If TAP device created
>>> with "local" unset, it initializes as usual. If you enable
>>> "local" after it and start incoming migration, it will fail
>>> in .pre_load handler of TAP backend.
>>
>> Out of curiosity, how would such a failure look like?
>
> TAP is initialized as usual, so the answer depends on the options.
>
> For incoming migration we should open TAP without "ifname" argument.
> If so (and "local" is not set), new TAP device will be created. It
> shouldn't trigger any additional problems I think.
>
>
> Checking with test:
>
> diff --git a/tests/functional/x86_64/test_tap_migration.py b/tests/functional/x86_64/test_tap_migration.py
> index bcc0a60dfe9..94982bc7b55 100755
> --- a/tests/functional/x86_64/test_tap_migration.py
> +++ b/tests/functional/x86_64/test_tap_migration.py
> @@ -316,8 +316,8 @@ def prepare_and_launch_vm(
> else:
> tap_name = TAP_ID
>
> - self.set_migration_capabilities(vm, local)
> self.add_virtio_net(vm, vhost, tap_name, local, incoming)
> + self.set_migration_capabilities(vm, local)
>
> def add_virtio_net(
> self, vm, vhost: bool, tap_name: str, local: bool, incoming: bool
>
>
> Leads to
>
> qemu-system-x86_64: TAP is already initialized and cannot receive incoming fd
> qemu-system-x86_64: warning: qemu_fclose: received fd 27 was never claimed
> qemu-system-x86_64: load of migration failed: Invalid argument: error while loading state for instance 0x0 of device 'netdev.1/0/net-tap': pre load hook failed for: 'net-tap', version_id: 0, minimum version_id: 0, ret: -22
>
> on target, and failed migration, as expected.
How is the user supposed to reason their way from here to "oh, I need to
create my TAP device with QMP and after setting @local"?
>
>>
>>> Moreover, there are interface restrictions: if you create TAP
>>> device when QEMU is in INCOMING state, and both "local"
>>> and "local-migration-supported" set, most of TAP options are
>>> not allowed, and script/downscript are required to be explicitly
>>> unset (set to "" or "no").
>>>
>>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
[...]
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 12/14] net/tap: support local migration with virtio-net
2026-07-13 13:29 ` Markus Armbruster
@ 2026-07-13 15:08 ` Vladimir Sementsov-Ogievskiy
2026-07-14 13:19 ` Michael S. Tsirkin
0 siblings, 1 reply; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-13 15:08 UTC (permalink / raw)
To: Markus Armbruster
Cc: jasowang, mst, peterx, farosas, raphael.s.norwitz, bchaney,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Philippe Mathieu-Daudé, Zhao Liu, Jason Wang, Eric Blake
On 13.07.26 16:29, Markus Armbruster wrote:
> Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> writes:
>
>> On 13.07.26 10:57, Markus Armbruster wrote:
>>> Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> writes:
>>>
>>>> Support transferring of TAP state (including open fd).
>>>>
>>>> Add new property "local-migration-supported", which defines
>>>> whether local-migration is actually supported for this TAP device.
>>>> Starting from 11.2 QEMU Machine Types it's enabled by default.
>>>>
>>>> Note, that local-migration (including migrating opened FDs
>>>
>>> Scratch the comma after "Note", ...
>>>
>>>> through migration channel, which must be UNIX socket), is
>>>
>>> ... and the one before "is".
>>>
>>>> enabled by global "local" migration parameters. But individual
>>>> devices may have additional options to enable/disable it
>>>> per device.
>>>>
>>>> The tricky thing, is that we need to know, should we do
>>>
>>> Do you mean "The tricky thing is that we ween to know whether to call"?
>>
>> yes
>>
>>>
>>>> call open/connect in TAP initialization code. That means,
>>>> that on incoming migration we want to know status of
>>>> "local" migration parameter at time of creating TAP device.
>>>> So, currently, for incoming local migration only TAP device
>>>> created through QMP is supported, as you need to set migration
>>>> parameter first (through QMP as well).
>
> Possibly clearer:
>
> The tricky thing is that we need to know whether to call open/connect
> in TAP initialization code, i.e. we need to know the value of
> migration parameter "local" when creating the TAP device. For
> incoming migration, we can know only for TAP devices created with QMP
> after setting the migration parameter with QMP.
>
>>>>
>>>> So the full picture is:
>>>>
>>>> On source, to start outgoing "local" migration you need:
>>>>
>>>> - migration parameter "local" set to true
>>>> - "local-migration-supported" TAP option set to true (the
>>>> default, starting from 11.2 QEMU Machine Types)
>>>>
>>>> If at least one of these options is not set, TAP backend
>>>> doesn't participate in migration.
>>>>
>>>> On target, things are more difficult:
>>>>
>>>> Same, you need both "local" and "local-migration-supported"
>>>> be set. And same, if one of them is not set, TAP backend
>>>> is initialized as usual, and doesn't accept any incoming
>>>> state.
>>>>
>>>> Additionally, if you are going to set "local", it must be
>>>> set before creating the TAP device. If TAP device created
>>>> with "local" unset, it initializes as usual. If you enable
>>>> "local" after it and start incoming migration, it will fail
>>>> in .pre_load handler of TAP backend.
>>>
>>> Out of curiosity, how would such a failure look like?
>>
>> TAP is initialized as usual, so the answer depends on the options.
>>
>> For incoming migration we should open TAP without "ifname" argument.
>> If so (and "local" is not set), new TAP device will be created. It
>> shouldn't trigger any additional problems I think.
>>
>>
>> Checking with test:
>>
>> diff --git a/tests/functional/x86_64/test_tap_migration.py b/tests/functional/x86_64/test_tap_migration.py
>> index bcc0a60dfe9..94982bc7b55 100755
>> --- a/tests/functional/x86_64/test_tap_migration.py
>> +++ b/tests/functional/x86_64/test_tap_migration.py
>> @@ -316,8 +316,8 @@ def prepare_and_launch_vm(
>> else:
>> tap_name = TAP_ID
>>
>> - self.set_migration_capabilities(vm, local)
>> self.add_virtio_net(vm, vhost, tap_name, local, incoming)
>> + self.set_migration_capabilities(vm, local)
>>
>> def add_virtio_net(
>> self, vm, vhost: bool, tap_name: str, local: bool, incoming: bool
>>
>>
>> Leads to
>>
>> qemu-system-x86_64: TAP is already initialized and cannot receive incoming fd
>> qemu-system-x86_64: warning: qemu_fclose: received fd 27 was never claimed
>> qemu-system-x86_64: load of migration failed: Invalid argument: error while loading state for instance 0x0 of device 'netdev.1/0/net-tap': pre load hook failed for: 'net-tap', version_id: 0, minimum version_id: 0, ret: -22
>>
>> on target, and failed migration, as expected.
>
> How is the user supposed to reason their way from here to "oh, I need to
> create my TAP device with QMP and after setting @local"?
Not trivial, yes.
I can update error message to
TAP is already initialized and cannot receive incoming fd. For local migration, "local" migration parameter must be set _before_ creating TAP device.
>
>>
>>>
>>>> Moreover, there are interface restrictions: if you create TAP
>>>> device when QEMU is in INCOMING state, and both "local"
>>>> and "local-migration-supported" set, most of TAP options are
>>>> not allowed, and script/downscript are required to be explicitly
>>>> unset (set to "" or "no").
>>>>
>>>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
>
> [...]
>
--
Best regards,
Vladimir
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 12/14] net/tap: support local migration with virtio-net
2026-07-13 15:08 ` Vladimir Sementsov-Ogievskiy
@ 2026-07-14 13:19 ` Michael S. Tsirkin
0 siblings, 0 replies; 44+ messages in thread
From: Michael S. Tsirkin @ 2026-07-14 13:19 UTC (permalink / raw)
To: Vladimir Sementsov-Ogievskiy
Cc: Markus Armbruster, jasowang, peterx, farosas, raphael.s.norwitz,
bchaney, qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Philippe Mathieu-Daudé, Zhao Liu, Jason Wang, Eric Blake
On Mon, Jul 13, 2026 at 06:08:26PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> On 13.07.26 16:29, Markus Armbruster wrote:
> > Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> writes:
> >
> > > On 13.07.26 10:57, Markus Armbruster wrote:
> > > > Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> writes:
> > > >
> > > > > Support transferring of TAP state (including open fd).
> > > > >
> > > > > Add new property "local-migration-supported", which defines
> > > > > whether local-migration is actually supported for this TAP device.
> > > > > Starting from 11.2 QEMU Machine Types it's enabled by default.
> > > > >
> > > > > Note, that local-migration (including migrating opened FDs
> > > >
> > > > Scratch the comma after "Note", ...
> > > >
> > > > > through migration channel, which must be UNIX socket), is
> > > >
> > > > ... and the one before "is".
> > > >
> > > > > enabled by global "local" migration parameters. But individual
> > > > > devices may have additional options to enable/disable it
> > > > > per device.
> > > > >
> > > > > The tricky thing, is that we need to know, should we do
> > > >
> > > > Do you mean "The tricky thing is that we ween to know whether to call"?
> > >
> > > yes
> > >
> > > >
> > > > > call open/connect in TAP initialization code. That means,
> > > > > that on incoming migration we want to know status of
> > > > > "local" migration parameter at time of creating TAP device.
> > > > > So, currently, for incoming local migration only TAP device
> > > > > created through QMP is supported, as you need to set migration
> > > > > parameter first (through QMP as well).
> >
> > Possibly clearer:
> >
> > The tricky thing is that we need to know whether to call open/connect
> > in TAP initialization code, i.e. we need to know the value of
> > migration parameter "local" when creating the TAP device. For
> > incoming migration, we can know only for TAP devices created with QMP
> > after setting the migration parameter with QMP.
> >
> > > > >
> > > > > So the full picture is:
> > > > >
> > > > > On source, to start outgoing "local" migration you need:
> > > > >
> > > > > - migration parameter "local" set to true
> > > > > - "local-migration-supported" TAP option set to true (the
> > > > > default, starting from 11.2 QEMU Machine Types)
> > > > >
> > > > > If at least one of these options is not set, TAP backend
> > > > > doesn't participate in migration.
> > > > >
> > > > > On target, things are more difficult:
> > > > >
> > > > > Same, you need both "local" and "local-migration-supported"
> > > > > be set. And same, if one of them is not set, TAP backend
> > > > > is initialized as usual, and doesn't accept any incoming
> > > > > state.
> > > > >
> > > > > Additionally, if you are going to set "local", it must be
> > > > > set before creating the TAP device. If TAP device created
> > > > > with "local" unset, it initializes as usual. If you enable
> > > > > "local" after it and start incoming migration, it will fail
> > > > > in .pre_load handler of TAP backend.
> > > >
> > > > Out of curiosity, how would such a failure look like?
> > >
> > > TAP is initialized as usual, so the answer depends on the options.
> > >
> > > For incoming migration we should open TAP without "ifname" argument.
> > > If so (and "local" is not set), new TAP device will be created. It
> > > shouldn't trigger any additional problems I think.
> > >
> > >
> > > Checking with test:
> > >
> > > diff --git a/tests/functional/x86_64/test_tap_migration.py b/tests/functional/x86_64/test_tap_migration.py
> > > index bcc0a60dfe9..94982bc7b55 100755
> > > --- a/tests/functional/x86_64/test_tap_migration.py
> > > +++ b/tests/functional/x86_64/test_tap_migration.py
> > > @@ -316,8 +316,8 @@ def prepare_and_launch_vm(
> > > else:
> > > tap_name = TAP_ID
> > >
> > > - self.set_migration_capabilities(vm, local)
> > > self.add_virtio_net(vm, vhost, tap_name, local, incoming)
> > > + self.set_migration_capabilities(vm, local)
> > >
> > > def add_virtio_net(
> > > self, vm, vhost: bool, tap_name: str, local: bool, incoming: bool
> > >
> > >
> > > Leads to
> > >
> > > qemu-system-x86_64: TAP is already initialized and cannot receive incoming fd
> > > qemu-system-x86_64: warning: qemu_fclose: received fd 27 was never claimed
> > > qemu-system-x86_64: load of migration failed: Invalid argument: error while loading state for instance 0x0 of device 'netdev.1/0/net-tap': pre load hook failed for: 'net-tap', version_id: 0, minimum version_id: 0, ret: -22
> > >
> > > on target, and failed migration, as expected.
> >
> > How is the user supposed to reason their way from here to "oh, I need to
> > create my TAP device with QMP and after setting @local"?
>
> Not trivial, yes.
>
> I can update error message to
>
> TAP is already initialized and cannot receive incoming fd. For local migration, "local" migration parameter must be set _before_ creating TAP device.
Another reason to just make it a machine property globally
and be done with it, maybe?
> >
> > >
> > > >
> > > > > Moreover, there are interface restrictions: if you create TAP
> > > > > device when QEMU is in INCOMING state, and both "local"
> > > > > and "local-migration-supported" set, most of TAP options are
> > > > > not allowed, and script/downscript are required to be explicitly
> > > > > unset (set to "" or "no").
> > > > >
> > > > > Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
> >
> > [...]
> >
>
>
> --
> Best regards,
> Vladimir
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v18 12/14] net/tap: support local migration with virtio-net
2026-07-10 19:18 ` [PATCH v18 12/14] net/tap: support local migration with virtio-net Vladimir Sementsov-Ogievskiy
2026-07-13 7:57 ` Markus Armbruster
@ 2026-07-13 11:17 ` Daniel P. Berrangé
2026-07-13 12:35 ` Vladimir Sementsov-Ogievskiy
2026-07-13 19:36 ` Michael S. Tsirkin
2 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2026-07-13 11:17 UTC (permalink / raw)
To: Vladimir Sementsov-Ogievskiy
Cc: jasowang, mst, armbru, peterx, farosas, raphael.s.norwitz,
bchaney, qemu-devel, pbonzini, yc-core, mark.caveayland,
Philippe Mathieu-Daudé, Zhao Liu, Jason Wang, Eric Blake
On Fri, Jul 10, 2026 at 10:18:01PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> Support transferring of TAP state (including open fd).
>
> Add new property "local-migration-supported", which defines
> whether local-migration is actually supported for this TAP device.
> Starting from 11.2 QEMU Machine Types it's enabled by default.
>
> Note, that local-migration (including migrating opened FDs
> through migration channel, which must be UNIX socket), is
> enabled by global "local" migration parameters. But individual
> devices may have additional options to enable/disable it
> per device.
>
> The tricky thing, is that we need to know, should we do
> call open/connect in TAP initialization code. That means,
> that on incoming migration we want to know status of
> "local" migration parameter at time of creating TAP device.
> So, currently, for incoming local migration only TAP device
> created through QMP is supported, as you need to set migration
> parameter first (through QMP as well).
>
> So the full picture is:
>
> On source, to start outgoing "local" migration you need:
>
> - migration parameter "local" set to true
> - "local-migration-supported" TAP option set to true (the
> default, starting from 11.2 QEMU Machine Types)
>
> If at least one of these options is not set, TAP backend
> doesn't participate in migration.
>
> On target, things are more difficult:
>
> Same, you need both "local" and "local-migration-supported"
> be set. And same, if one of them is not set, TAP backend
> is initialized as usual, and doesn't accept any incoming
> state.
>
> Additionally, if you are going to set "local", it must be
> set before creating the TAP device. If TAP device created
> with "local" unset, it initializes as usual. If you enable
> "local" after it and start incoming migration, it will fail
> in .pre_load handler of TAP backend.
>
> Moreover, there are interface restrictions: if you create TAP
> device when QEMU is in INCOMING state, and both "local"
> and "local-migration-supported" set, most of TAP options are
> not allowed, and script/downscript are required to be explicitly
> unset (set to "" or "no").
>
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
> ---
>
> In v17 thread we considered making new qapi parameters "unstable".
> Still, it was because of doubtful "incoming-fds" parameter. In this
> v18 it's completely removed, and we may live only with "local"
> migration parameter + "local-migration-supported" TAP option.
> This way the series doesn't conflict with suggested by Peter
> [PATCH RFC 0/2] migration/vl: new -incoming config:* for early migration parameters
> So, I think, worth making one more iteration as "stable candidate".
> If still some doubts, not problem for me to resend with "unstable" features.
>
> hw/core/machine.c | 7 ++
> net/tap.c | 162 ++++++++++++++++++++++++++++++++++++++++++++--
> qapi/net.json | 23 ++++++-
> 3 files changed, 185 insertions(+), 7 deletions(-)
>
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index 15886a56b19..2a1abb29041 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -36,10 +36,16 @@
> #include "hw/virtio/virtio-pci.h"
> #include "hw/virtio/virtio-net.h"
> #include "hw/virtio/virtio-iommu.h"
> +#include "net/tap.h"
> #include "hw/acpi/generic_event_device.h"
> #include "qemu/audio.h"
> #include "hw/arm/smmuv3.h"
>
> +/*
> + * TODO: When hw_compat_11_1 appears, local-migration-supported=false
> + * for TYPE_TAP_NETDEV should be move to it from hw_compat_11_0.
> + */
> +
> GlobalProperty hw_compat_11_0[] = {
> { "chardev-vc", "encoding", "cp437" },
> { "tpm-crb", "cap-chunk", "off" },
> @@ -49,6 +55,7 @@ GlobalProperty hw_compat_11_0[] = {
> { TYPE_ARM_SMMUV3, "ssidsize", "0" },
> { TYPE_ARM_SMMUV3, "oas", "44" },
> { "migration", "switchover-ack-legacy", "on" },
> + { TYPE_TAP_NETDEV, "local-migration-supported", "false" },
So disabled by default for historical machines type versions, but....
> +static void tap_instance_init(Object *obj)
> +{
> + TAPState *s = TAP_NETDEV(obj);
> + s->local_migration_supported = true;
> +}
..enabled by default for all future machine type versions, but...
> @@ -1005,6 +1123,23 @@ int net_init_tap(const Netdev *netdev, const char *name,
> return -1;
> }
>
> + incoming_fds = tap->local_migration_supported && migrate_local() &&
> + runstate_check(RUN_STATE_INMIGRATE);
> +
> + if (incoming_fds &&
> + (tap->fd || tap->fds || tap->helper || tap->br || tap->ifname ||
> + tap->has_sndbuf || tap->has_vnet_hdr ||
> + !tap_is_explicit_no_script(tap->script) ||
> + !tap_is_explicit_no_script(tap->downscript))) {
> + error_setg(errp, "Local incoming migration of TAP device (-incoming, "
> + "migration parameter @local is set, "
> + "TAP parameter @local-migration-supported is set) "
> + "is incompatible with "
> + "fd=, fds=, helper=, br=, ifname=, sndbuf= and vnet_hdr=, "
> + "and requires explicit empty script= and downscript=");
> + return -1;
> + }
...not usable by default unless the mgmt app knows to avoid these settings,
but the user won't find out about this limit until they try to migrate and
it fails at runtime, months or years after the VM was first configured &
launched :-(
AFAICT, there's also no way for mgmt apps to determine which parameters
are forbidden, because it is merely exposed in API docs text, no way to
introspect
IMHO the sane way to do this is to not have any "local-migration-supported"
device flag at all, and also no "local" migration parameter. The src & dst
QEMU's so badly need to be able to auto-negotiate and just do the right
thing, otherwise this local migration optimization becomes a mgmt nightmare
long term :-(
And yet we don't have any auto-negotiate mechanism, and I don't want to
block your work :-(
I'm annoyed that we keep creating more and more pain for both ourselves
and our mgmt apps in migration for as long as we don't address this
fundamental migration protocol design flaw.
> diff --git a/qapi/net.json b/qapi/net.json
> index ada0329ef9d..2070979f20e 100644
> --- a/qapi/net.json
> +++ b/qapi/net.json
> @@ -435,6 +435,26 @@
> # @poll-us: maximum number of microseconds that could be spent on busy
> # polling for tap (since 2.7)
> #
> +# @local-migration-supported: enable local migration for this TAP
> +# backend. When set, local migration is enabled/disabled by
> +# migration parameter @local for this TAP backend. When unset,
> +# migration parameter @local is ignored for this TAP backend.
> +# To be able to do incoming local migration of TAP backend,
> +# migration parameter @local must be set _before_ creating the
> +# TAP backend. Otherwise, TAP backend is initialized as usual,
> +# opening/creating TAP devices in kernel. In this case further
> +# local incoming migration (with migration parameter @local set
> +# after creating TAP backend with @local-migration-supporeted
> +# parameter set) will simply fail.
> +# Moreover, when QEMU is in incoming migration state, migration
> +# parameter @local is set and @local-migration-supported is set,
> +# following options are not supported and must not be set:
> +# @ds, @fds, @helper, @br, @ifname, @sndbuf, @vnet_hdr.
> +# Additionally in this case @script and @downscipt must be
> +# explicitly disabled (empty strings or "no").
> +# (Since 11.2. Defaults to true for QEMU Machine Types >= 11.2,
> +# and to false for QEMU Machine Types < 11.2)
> +#
> # Since: 1.2
> ##
> { 'struct': 'NetdevTapOptions',
> @@ -453,7 +473,8 @@
> '*vhostfds': 'str',
> '*vhostforce': 'bool',
> '*queues': 'uint32',
> - '*poll-us': 'uint32'} }
> + '*poll-us': 'uint32',
> + '*local-migration-supported': 'bool' } }
>
> ##
> # @NetdevSocketOptions:
> --
> 2.43.0
>
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 12/14] net/tap: support local migration with virtio-net
2026-07-13 11:17 ` Daniel P. Berrangé
@ 2026-07-13 12:35 ` Vladimir Sementsov-Ogievskiy
2026-07-13 15:05 ` Daniel P. Berrangé
0 siblings, 1 reply; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-13 12:35 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: jasowang, mst, armbru, peterx, farosas, raphael.s.norwitz,
bchaney, qemu-devel, pbonzini, yc-core, mark.caveayland,
Philippe Mathieu-Daudé, Zhao Liu, Jason Wang, Eric Blake
On 13.07.26 14:17, Daniel P. Berrangé wrote:
> On Fri, Jul 10, 2026 at 10:18:01PM +0300, Vladimir Sementsov-Ogievskiy wrote:
>> Support transferring of TAP state (including open fd).
>>
>> Add new property "local-migration-supported", which defines
>> whether local-migration is actually supported for this TAP device.
>> Starting from 11.2 QEMU Machine Types it's enabled by default.
>>
>> Note, that local-migration (including migrating opened FDs
>> through migration channel, which must be UNIX socket), is
>> enabled by global "local" migration parameters. But individual
>> devices may have additional options to enable/disable it
>> per device.
>>
>> The tricky thing, is that we need to know, should we do
>> call open/connect in TAP initialization code. That means,
>> that on incoming migration we want to know status of
>> "local" migration parameter at time of creating TAP device.
>> So, currently, for incoming local migration only TAP device
>> created through QMP is supported, as you need to set migration
>> parameter first (through QMP as well).
>>
>> So the full picture is:
>>
>> On source, to start outgoing "local" migration you need:
>>
>> - migration parameter "local" set to true
>> - "local-migration-supported" TAP option set to true (the
>> default, starting from 11.2 QEMU Machine Types)
>>
>> If at least one of these options is not set, TAP backend
>> doesn't participate in migration.
>>
>> On target, things are more difficult:
>>
>> Same, you need both "local" and "local-migration-supported"
>> be set. And same, if one of them is not set, TAP backend
>> is initialized as usual, and doesn't accept any incoming
>> state.
>>
>> Additionally, if you are going to set "local", it must be
>> set before creating the TAP device. If TAP device created
>> with "local" unset, it initializes as usual. If you enable
>> "local" after it and start incoming migration, it will fail
>> in .pre_load handler of TAP backend.
>>
>> Moreover, there are interface restrictions: if you create TAP
>> device when QEMU is in INCOMING state, and both "local"
>> and "local-migration-supported" set, most of TAP options are
>> not allowed, and script/downscript are required to be explicitly
>> unset (set to "" or "no").
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
>> ---
>>
>> In v17 thread we considered making new qapi parameters "unstable".
>> Still, it was because of doubtful "incoming-fds" parameter. In this
>> v18 it's completely removed, and we may live only with "local"
>> migration parameter + "local-migration-supported" TAP option.
>> This way the series doesn't conflict with suggested by Peter
>> [PATCH RFC 0/2] migration/vl: new -incoming config:* for early migration parameters
>> So, I think, worth making one more iteration as "stable candidate".
>> If still some doubts, not problem for me to resend with "unstable" features.
>>
>> hw/core/machine.c | 7 ++
>> net/tap.c | 162 ++++++++++++++++++++++++++++++++++++++++++++--
>> qapi/net.json | 23 ++++++-
>> 3 files changed, 185 insertions(+), 7 deletions(-)
>>
>> diff --git a/hw/core/machine.c b/hw/core/machine.c
>> index 15886a56b19..2a1abb29041 100644
>> --- a/hw/core/machine.c
>> +++ b/hw/core/machine.c
>> @@ -36,10 +36,16 @@
>> #include "hw/virtio/virtio-pci.h"
>> #include "hw/virtio/virtio-net.h"
>> #include "hw/virtio/virtio-iommu.h"
>> +#include "net/tap.h"
>> #include "hw/acpi/generic_event_device.h"
>> #include "qemu/audio.h"
>> #include "hw/arm/smmuv3.h"
>>
>> +/*
>> + * TODO: When hw_compat_11_1 appears, local-migration-supported=false
>> + * for TYPE_TAP_NETDEV should be move to it from hw_compat_11_0.
>> + */
>> +
>> GlobalProperty hw_compat_11_0[] = {
>> { "chardev-vc", "encoding", "cp437" },
>> { "tpm-crb", "cap-chunk", "off" },
>> @@ -49,6 +55,7 @@ GlobalProperty hw_compat_11_0[] = {
>> { TYPE_ARM_SMMUV3, "ssidsize", "0" },
>> { TYPE_ARM_SMMUV3, "oas", "44" },
>> { "migration", "switchover-ack-legacy", "on" },
>> + { TYPE_TAP_NETDEV, "local-migration-supported", "false" },
>
> So disabled by default for historical machines type versions, but....
>
>> +static void tap_instance_init(Object *obj)
>> +{
>> + TAPState *s = TAP_NETDEV(obj);
>> + s->local_migration_supported = true;
>> +}
>
> ..enabled by default for all future machine type versions, but...
>
>
>> @@ -1005,6 +1123,23 @@ int net_init_tap(const Netdev *netdev, const char *name,
>> return -1;
>> }
>>
>> + incoming_fds = tap->local_migration_supported && migrate_local() &&
>> + runstate_check(RUN_STATE_INMIGRATE);
>> +
>> + if (incoming_fds &&
>> + (tap->fd || tap->fds || tap->helper || tap->br || tap->ifname ||
>> + tap->has_sndbuf || tap->has_vnet_hdr ||
>> + !tap_is_explicit_no_script(tap->script) ||
>> + !tap_is_explicit_no_script(tap->downscript))) {
>> + error_setg(errp, "Local incoming migration of TAP device (-incoming, "
>> + "migration parameter @local is set, "
>> + "TAP parameter @local-migration-supported is set) "
>> + "is incompatible with "
>> + "fd=, fds=, helper=, br=, ifname=, sndbuf= and vnet_hdr=, "
>> + "and requires explicit empty script= and downscript=");
>> + return -1;
>> + }
>
> ...not usable by default unless the mgmt app knows to avoid these settings,
> but the user won't find out about this limit until they try to migrate and
> it fails at runtime, months or years after the VM was first configured &
> launched :-(
No, it fail only for RUN_STATE_INMIGRATE, and only if "local" is enabled.
If mgmt enables "local" it must know about the restrictions of using it.
>
> AFAICT, there's also no way for mgmt apps to determine which parameters
> are forbidden, because it is merely exposed in API docs text, no way to
> introspect
Unfortunately yes.
>
> IMHO the sane way to do this is to not have any "local-migration-supported"
> device flag at all, and also no "local" migration parameter. The src & dst
> QEMU's so badly need to be able to auto-negotiate and just do the right
> thing, otherwise this local migration optimization becomes a mgmt nightmare
> long term :-(
"local" is needed because we can't know, does transfer support fd-passing.
To auto-negotiate it, we probably need to pass a test FD to the target, as
simply checking "is it a UNIX socket" may not be enough.
"local-migration-supported" may be auto negotiated of course, if we have
auto negotiation.
>
> And yet we don't have any auto-negotiate mechanism, and I don't want to
> block your work :-(
>
Thanks) But finally, in short term, could we proceed with it as is, or not?
Would it be better, if I drop the default =true for new machine types, keeping
it false?
--
Best regards,
Vladimir
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 12/14] net/tap: support local migration with virtio-net
2026-07-13 12:35 ` Vladimir Sementsov-Ogievskiy
@ 2026-07-13 15:05 ` Daniel P. Berrangé
2026-07-13 15:19 ` Vladimir Sementsov-Ogievskiy
0 siblings, 1 reply; 44+ messages in thread
From: Daniel P. Berrangé @ 2026-07-13 15:05 UTC (permalink / raw)
To: Vladimir Sementsov-Ogievskiy
Cc: jasowang, mst, armbru, peterx, farosas, raphael.s.norwitz,
bchaney, qemu-devel, pbonzini, yc-core, mark.caveayland,
Philippe Mathieu-Daudé, Zhao Liu, Jason Wang, Eric Blake
On Mon, Jul 13, 2026 at 03:35:06PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> On 13.07.26 14:17, Daniel P. Berrangé wrote:
> > On Fri, Jul 10, 2026 at 10:18:01PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > > Support transferring of TAP state (including open fd).
> > >
> > > Add new property "local-migration-supported", which defines
> > > whether local-migration is actually supported for this TAP device.
> > > Starting from 11.2 QEMU Machine Types it's enabled by default.
> > >
> > > Note, that local-migration (including migrating opened FDs
> > > through migration channel, which must be UNIX socket), is
> > > enabled by global "local" migration parameters. But individual
> > > devices may have additional options to enable/disable it
> > > per device.
> > >
> > > The tricky thing, is that we need to know, should we do
> > > call open/connect in TAP initialization code. That means,
> > > that on incoming migration we want to know status of
> > > "local" migration parameter at time of creating TAP device.
> > > So, currently, for incoming local migration only TAP device
> > > created through QMP is supported, as you need to set migration
> > > parameter first (through QMP as well).
> > >
> > > So the full picture is:
> > >
> > > On source, to start outgoing "local" migration you need:
> > >
> > > - migration parameter "local" set to true
> > > - "local-migration-supported" TAP option set to true (the
> > > default, starting from 11.2 QEMU Machine Types)
> > >
> > > If at least one of these options is not set, TAP backend
> > > doesn't participate in migration.
> > >
> > > On target, things are more difficult:
> > >
> > > Same, you need both "local" and "local-migration-supported"
> > > be set. And same, if one of them is not set, TAP backend
> > > is initialized as usual, and doesn't accept any incoming
> > > state.
> > >
> > > Additionally, if you are going to set "local", it must be
> > > set before creating the TAP device. If TAP device created
> > > with "local" unset, it initializes as usual. If you enable
> > > "local" after it and start incoming migration, it will fail
> > > in .pre_load handler of TAP backend.
> > >
> > > Moreover, there are interface restrictions: if you create TAP
> > > device when QEMU is in INCOMING state, and both "local"
> > > and "local-migration-supported" set, most of TAP options are
> > > not allowed, and script/downscript are required to be explicitly
> > > unset (set to "" or "no").
> > >
> > > Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
> > > ---
> > >
> > > In v17 thread we considered making new qapi parameters "unstable".
> > > Still, it was because of doubtful "incoming-fds" parameter. In this
> > > v18 it's completely removed, and we may live only with "local"
> > > migration parameter + "local-migration-supported" TAP option.
> > > This way the series doesn't conflict with suggested by Peter
> > > [PATCH RFC 0/2] migration/vl: new -incoming config:* for early migration parameters
> > > So, I think, worth making one more iteration as "stable candidate".
> > > If still some doubts, not problem for me to resend with "unstable" features.
> > >
> > > hw/core/machine.c | 7 ++
> > > net/tap.c | 162 ++++++++++++++++++++++++++++++++++++++++++++--
> > > qapi/net.json | 23 ++++++-
> > > 3 files changed, 185 insertions(+), 7 deletions(-)
> > >
> > > diff --git a/hw/core/machine.c b/hw/core/machine.c
> > > index 15886a56b19..2a1abb29041 100644
> > > --- a/hw/core/machine.c
> > > +++ b/hw/core/machine.c
> > > @@ -36,10 +36,16 @@
> > > #include "hw/virtio/virtio-pci.h"
> > > #include "hw/virtio/virtio-net.h"
> > > #include "hw/virtio/virtio-iommu.h"
> > > +#include "net/tap.h"
> > > #include "hw/acpi/generic_event_device.h"
> > > #include "qemu/audio.h"
> > > #include "hw/arm/smmuv3.h"
> > > +/*
> > > + * TODO: When hw_compat_11_1 appears, local-migration-supported=false
> > > + * for TYPE_TAP_NETDEV should be move to it from hw_compat_11_0.
> > > + */
> > > +
> > > GlobalProperty hw_compat_11_0[] = {
> > > { "chardev-vc", "encoding", "cp437" },
> > > { "tpm-crb", "cap-chunk", "off" },
> > > @@ -49,6 +55,7 @@ GlobalProperty hw_compat_11_0[] = {
> > > { TYPE_ARM_SMMUV3, "ssidsize", "0" },
> > > { TYPE_ARM_SMMUV3, "oas", "44" },
> > > { "migration", "switchover-ack-legacy", "on" },
> > > + { TYPE_TAP_NETDEV, "local-migration-supported", "false" },
> >
> > So disabled by default for historical machines type versions, but....
> >
> > > +static void tap_instance_init(Object *obj)
> > > +{
> > > + TAPState *s = TAP_NETDEV(obj);
> > > + s->local_migration_supported = true;
> > > +}
> >
> > ..enabled by default for all future machine type versions, but...
> >
> >
> > > @@ -1005,6 +1123,23 @@ int net_init_tap(const Netdev *netdev, const char *name,
> > > return -1;
> > > }
> > > + incoming_fds = tap->local_migration_supported && migrate_local() &&
> > > + runstate_check(RUN_STATE_INMIGRATE);
> > > +
> > > + if (incoming_fds &&
> > > + (tap->fd || tap->fds || tap->helper || tap->br || tap->ifname ||
> > > + tap->has_sndbuf || tap->has_vnet_hdr ||
> > > + !tap_is_explicit_no_script(tap->script) ||
> > > + !tap_is_explicit_no_script(tap->downscript))) {
> > > + error_setg(errp, "Local incoming migration of TAP device (-incoming, "
> > > + "migration parameter @local is set, "
> > > + "TAP parameter @local-migration-supported is set) "
> > > + "is incompatible with "
> > > + "fd=, fds=, helper=, br=, ifname=, sndbuf= and vnet_hdr=, "
> > > + "and requires explicit empty script= and downscript=");
> > > + return -1;
> > > + }
> >
> > ...not usable by default unless the mgmt app knows to avoid these settings,
> > but the user won't find out about this limit until they try to migrate and
> > it fails at runtime, months or years after the VM was first configured &
> > launched :-(
>
> No, it fail only for RUN_STATE_INMIGRATE, and only if "local" is enabled.
> If mgmt enables "local" it must know about the restrictions of using it.
This implies that with new machine types, mgmt has to know to turn
off the local migration feature which is rather unfortunate.
> > IMHO the sane way to do this is to not have any "local-migration-supported"
> > device flag at all, and also no "local" migration parameter. The src & dst
> > QEMU's so badly need to be able to auto-negotiate and just do the right
> > thing, otherwise this local migration optimization becomes a mgmt nightmare
> > long term :-(
>
> "local" is needed because we can't know, does transfer support fd-passing.
> To auto-negotiate it, we probably need to pass a test FD to the target, as
> simply checking "is it a UNIX socket" may not be enough.
Oh, true, I forget that the UNIX socket could just be a local conenction
to a MITM proxy
> > And yet we don't have any auto-negotiate mechanism, and I don't want to
> > block your work :-(
> >
>
> Thanks) But finally, in short term, could we proceed with it as is, or not?
>
> Would it be better, if I drop the default =true for new machine types, keeping
> it false?
I think I'd be more comfortable with this as non-default
in the machine type. As you say, the mgmt has to know
the limitations, so at that point having them turn on
the device property fits with the knowledge requirement.
Auto-enabling something is more applicable when the
behaviour is seemless without caveats.
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 12/14] net/tap: support local migration with virtio-net
2026-07-13 15:05 ` Daniel P. Berrangé
@ 2026-07-13 15:19 ` Vladimir Sementsov-Ogievskiy
0 siblings, 0 replies; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-13 15:19 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: jasowang, mst, armbru, peterx, farosas, raphael.s.norwitz,
bchaney, qemu-devel, pbonzini, yc-core, mark.caveayland,
Philippe Mathieu-Daudé, Zhao Liu, Jason Wang, Eric Blake
On 13.07.26 18:05, Daniel P. Berrangé wrote:
> On Mon, Jul 13, 2026 at 03:35:06PM +0300, Vladimir Sementsov-Ogievskiy wrote:
>> On 13.07.26 14:17, Daniel P. Berrangé wrote:
>>> On Fri, Jul 10, 2026 at 10:18:01PM +0300, Vladimir Sementsov-Ogievskiy wrote:
>>>> Support transferring of TAP state (including open fd).
>>>>
>>>> Add new property "local-migration-supported", which defines
>>>> whether local-migration is actually supported for this TAP device.
>>>> Starting from 11.2 QEMU Machine Types it's enabled by default.
>>>>
>>>> Note, that local-migration (including migrating opened FDs
>>>> through migration channel, which must be UNIX socket), is
>>>> enabled by global "local" migration parameters. But individual
>>>> devices may have additional options to enable/disable it
>>>> per device.
>>>>
>>>> The tricky thing, is that we need to know, should we do
>>>> call open/connect in TAP initialization code. That means,
>>>> that on incoming migration we want to know status of
>>>> "local" migration parameter at time of creating TAP device.
>>>> So, currently, for incoming local migration only TAP device
>>>> created through QMP is supported, as you need to set migration
>>>> parameter first (through QMP as well).
>>>>
>>>> So the full picture is:
>>>>
>>>> On source, to start outgoing "local" migration you need:
>>>>
>>>> - migration parameter "local" set to true
>>>> - "local-migration-supported" TAP option set to true (the
>>>> default, starting from 11.2 QEMU Machine Types)
>>>>
>>>> If at least one of these options is not set, TAP backend
>>>> doesn't participate in migration.
>>>>
>>>> On target, things are more difficult:
>>>>
>>>> Same, you need both "local" and "local-migration-supported"
>>>> be set. And same, if one of them is not set, TAP backend
>>>> is initialized as usual, and doesn't accept any incoming
>>>> state.
>>>>
>>>> Additionally, if you are going to set "local", it must be
>>>> set before creating the TAP device. If TAP device created
>>>> with "local" unset, it initializes as usual. If you enable
>>>> "local" after it and start incoming migration, it will fail
>>>> in .pre_load handler of TAP backend.
>>>>
>>>> Moreover, there are interface restrictions: if you create TAP
>>>> device when QEMU is in INCOMING state, and both "local"
>>>> and "local-migration-supported" set, most of TAP options are
>>>> not allowed, and script/downscript are required to be explicitly
>>>> unset (set to "" or "no").
>>>>
>>>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
>>>> ---
>>>>
>>>> In v17 thread we considered making new qapi parameters "unstable".
>>>> Still, it was because of doubtful "incoming-fds" parameter. In this
>>>> v18 it's completely removed, and we may live only with "local"
>>>> migration parameter + "local-migration-supported" TAP option.
>>>> This way the series doesn't conflict with suggested by Peter
>>>> [PATCH RFC 0/2] migration/vl: new -incoming config:* for early migration parameters
>>>> So, I think, worth making one more iteration as "stable candidate".
>>>> If still some doubts, not problem for me to resend with "unstable" features.
>>>>
>>>> hw/core/machine.c | 7 ++
>>>> net/tap.c | 162 ++++++++++++++++++++++++++++++++++++++++++++--
>>>> qapi/net.json | 23 ++++++-
>>>> 3 files changed, 185 insertions(+), 7 deletions(-)
>>>>
>>>> diff --git a/hw/core/machine.c b/hw/core/machine.c
>>>> index 15886a56b19..2a1abb29041 100644
>>>> --- a/hw/core/machine.c
>>>> +++ b/hw/core/machine.c
>>>> @@ -36,10 +36,16 @@
>>>> #include "hw/virtio/virtio-pci.h"
>>>> #include "hw/virtio/virtio-net.h"
>>>> #include "hw/virtio/virtio-iommu.h"
>>>> +#include "net/tap.h"
>>>> #include "hw/acpi/generic_event_device.h"
>>>> #include "qemu/audio.h"
>>>> #include "hw/arm/smmuv3.h"
>>>> +/*
>>>> + * TODO: When hw_compat_11_1 appears, local-migration-supported=false
>>>> + * for TYPE_TAP_NETDEV should be move to it from hw_compat_11_0.
>>>> + */
>>>> +
>>>> GlobalProperty hw_compat_11_0[] = {
>>>> { "chardev-vc", "encoding", "cp437" },
>>>> { "tpm-crb", "cap-chunk", "off" },
>>>> @@ -49,6 +55,7 @@ GlobalProperty hw_compat_11_0[] = {
>>>> { TYPE_ARM_SMMUV3, "ssidsize", "0" },
>>>> { TYPE_ARM_SMMUV3, "oas", "44" },
>>>> { "migration", "switchover-ack-legacy", "on" },
>>>> + { TYPE_TAP_NETDEV, "local-migration-supported", "false" },
>>>
>>> So disabled by default for historical machines type versions, but....
>>>
>>>> +static void tap_instance_init(Object *obj)
>>>> +{
>>>> + TAPState *s = TAP_NETDEV(obj);
>>>> + s->local_migration_supported = true;
>>>> +}
>>>
>>> ..enabled by default for all future machine type versions, but...
>>>
>>>
>>>> @@ -1005,6 +1123,23 @@ int net_init_tap(const Netdev *netdev, const char *name,
>>>> return -1;
>>>> }
>>>> + incoming_fds = tap->local_migration_supported && migrate_local() &&
>>>> + runstate_check(RUN_STATE_INMIGRATE);
>>>> +
>>>> + if (incoming_fds &&
>>>> + (tap->fd || tap->fds || tap->helper || tap->br || tap->ifname ||
>>>> + tap->has_sndbuf || tap->has_vnet_hdr ||
>>>> + !tap_is_explicit_no_script(tap->script) ||
>>>> + !tap_is_explicit_no_script(tap->downscript))) {
>>>> + error_setg(errp, "Local incoming migration of TAP device (-incoming, "
>>>> + "migration parameter @local is set, "
>>>> + "TAP parameter @local-migration-supported is set) "
>>>> + "is incompatible with "
>>>> + "fd=, fds=, helper=, br=, ifname=, sndbuf= and vnet_hdr=, "
>>>> + "and requires explicit empty script= and downscript=");
>>>> + return -1;
>>>> + }
>>>
>>> ...not usable by default unless the mgmt app knows to avoid these settings,
>>> but the user won't find out about this limit until they try to migrate and
>>> it fails at runtime, months or years after the VM was first configured &
>>> launched :-(
>>
>> No, it fail only for RUN_STATE_INMIGRATE, and only if "local" is enabled.
>> If mgmt enables "local" it must know about the restrictions of using it.
>
> This implies that with new machine types, mgmt has to know to turn
> off the local migration feature which is rather unfortunate.
Not sure I understand.
"local" migration parameter is disabled by default, regardless machine type.
Assume we are in a new machine type, and "local-migration-supported" is enabled
by default.
So, to enable "local" mgmt must know the details about restrictions in TAP
(which are now hidden under machine type). Is that what you mean?
It's a bit irrelevant now, as the only supporting device is TAP, and if
mgmt enables "local", it means that it want enable it exactly for TAP
device. Still in generic case (assume we already have some supporting
devices) it's a bad thing of course.
>
>>> IMHO the sane way to do this is to not have any "local-migration-supported"
>>> device flag at all, and also no "local" migration parameter. The src & dst
>>> QEMU's so badly need to be able to auto-negotiate and just do the right
>>> thing, otherwise this local migration optimization becomes a mgmt nightmare
>>> long term :-(
>>
>> "local" is needed because we can't know, does transfer support fd-passing.
>> To auto-negotiate it, we probably need to pass a test FD to the target, as
>> simply checking "is it a UNIX socket" may not be enough.
>
> Oh, true, I forget that the UNIX socket could just be a local conenction
> to a MITM proxy
>
>>> And yet we don't have any auto-negotiate mechanism, and I don't want to
>>> block your work :-(
>>>
>>
>> Thanks) But finally, in short term, could we proceed with it as is, or not?
>>
>> Would it be better, if I drop the default =true for new machine types, keeping
>> it false?
>
> I think I'd be more comfortable with this as non-default
> in the machine type. As you say, the mgmt has to know
> the limitations, so at that point having them turn on
> the device property fits with the knowledge requirement.
>
> Auto-enabling something is more applicable when the
> behaviour is seemless without caveats.
>
OK, that works for me, if no more opinions on that, I'll simply drop
machine-type-default in v19.
--
Best regards,
Vladimir
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v18 12/14] net/tap: support local migration with virtio-net
2026-07-10 19:18 ` [PATCH v18 12/14] net/tap: support local migration with virtio-net Vladimir Sementsov-Ogievskiy
2026-07-13 7:57 ` Markus Armbruster
2026-07-13 11:17 ` Daniel P. Berrangé
@ 2026-07-13 19:36 ` Michael S. Tsirkin
2026-07-14 6:43 ` Vladimir Sementsov-Ogievskiy
2 siblings, 1 reply; 44+ messages in thread
From: Michael S. Tsirkin @ 2026-07-13 19:36 UTC (permalink / raw)
To: Vladimir Sementsov-Ogievskiy
Cc: jasowang, armbru, peterx, farosas, raphael.s.norwitz, bchaney,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Philippe Mathieu-Daudé, Zhao Liu, Jason Wang, Eric Blake
On Fri, Jul 10, 2026 at 10:18:01PM +0300, Vladimir Sementsov-Ogievskiy wrote:
> Support transferring of TAP state (including open fd).
>
> Add new property "local-migration-supported", which defines
> whether local-migration is actually supported for this TAP device.
> Starting from 11.2 QEMU Machine Types it's enabled by default.
>
> Note, that local-migration (including migrating opened FDs
> through migration channel, which must be UNIX socket), is
> enabled by global "local" migration parameters. But individual
> devices may have additional options to enable/disable it
> per device.
>
> The tricky thing, is that we need to know, should we do
> call open/connect in TAP initialization code. That means,
> that on incoming migration we want to know status of
> "local" migration parameter at time of creating TAP device.
> So, currently, for incoming local migration only TAP device
> created through QMP is supported, as you need to set migration
> parameter first (through QMP as well).
>
> So the full picture is:
>
> On source, to start outgoing "local" migration you need:
>
> - migration parameter "local" set to true
> - "local-migration-supported" TAP option set to true (the
> default, starting from 11.2 QEMU Machine Types)
>
> If at least one of these options is not set, TAP backend
> doesn't participate in migration.
>
> On target, things are more difficult:
>
> Same, you need both "local" and "local-migration-supported"
> be set. And same, if one of them is not set, TAP backend
> is initialized as usual, and doesn't accept any incoming
> state.
>
> Additionally, if you are going to set "local", it must be
> set before creating the TAP device. If TAP device created
> with "local" unset, it initializes as usual. If you enable
> "local" after it and start incoming migration, it will fail
> in .pre_load handler of TAP backend.
>
> Moreover, there are interface restrictions: if you create TAP
> device when QEMU is in INCOMING state, and both "local"
> and "local-migration-supported" set, most of TAP options are
> not allowed, and script/downscript are required to be explicitly
> unset (set to "" or "no").
>
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
I am still confused why is this per device. I just do not
see where is it useful to set it for some devices
but not others. So it looks like a machine property
"incoming local migration" to me?
Sorry if I am being dense.
> ---
>
> In v17 thread we considered making new qapi parameters "unstable".
> Still, it was because of doubtful "incoming-fds" parameter. In this
> v18 it's completely removed, and we may live only with "local"
> migration parameter + "local-migration-supported" TAP option.
> This way the series doesn't conflict with suggested by Peter
> [PATCH RFC 0/2] migration/vl: new -incoming config:* for early migration parameters
> So, I think, worth making one more iteration as "stable candidate".
> If still some doubts, not problem for me to resend with "unstable" features.
>
> hw/core/machine.c | 7 ++
> net/tap.c | 162 ++++++++++++++++++++++++++++++++++++++++++++--
> qapi/net.json | 23 ++++++-
> 3 files changed, 185 insertions(+), 7 deletions(-)
>
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index 15886a56b19..2a1abb29041 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -36,10 +36,16 @@
> #include "hw/virtio/virtio-pci.h"
> #include "hw/virtio/virtio-net.h"
> #include "hw/virtio/virtio-iommu.h"
> +#include "net/tap.h"
> #include "hw/acpi/generic_event_device.h"
> #include "qemu/audio.h"
> #include "hw/arm/smmuv3.h"
>
> +/*
> + * TODO: When hw_compat_11_1 appears, local-migration-supported=false
> + * for TYPE_TAP_NETDEV should be move to it from hw_compat_11_0.
> + */
> +
> GlobalProperty hw_compat_11_0[] = {
> { "chardev-vc", "encoding", "cp437" },
> { "tpm-crb", "cap-chunk", "off" },
> @@ -49,6 +55,7 @@ GlobalProperty hw_compat_11_0[] = {
> { TYPE_ARM_SMMUV3, "ssidsize", "0" },
> { TYPE_ARM_SMMUV3, "oas", "44" },
> { "migration", "switchover-ack-legacy", "on" },
> + { TYPE_TAP_NETDEV, "local-migration-supported", "false" },
> };
> const size_t hw_compat_11_0_len = G_N_ELEMENTS(hw_compat_11_0);
>
> diff --git a/net/tap.c b/net/tap.c
> index 605e5867b01..adcac70b7ea 100644
> --- a/net/tap.c
> +++ b/net/tap.c
> @@ -38,12 +38,17 @@
> #include "monitor/monitor.h"
> #include "system/runstate.h"
> #include "system/system.h"
> +#include "migration/misc.h"
> #include "qapi/error.h"
> #include "qemu/cutils.h"
> #include "qemu/error-report.h"
> #include "qemu/main-loop.h"
> #include "qemu/sockets.h"
> #include "hw/virtio/vhost.h"
> +#include "hw/core/vmstate-if.h"
> +#include "migration/vmstate.h"
> +#include "qom/object.h"
> +#include "qom/compat-properties.h"
>
> #include "net/tap.h"
> #include "net/util.h"
> @@ -71,6 +76,8 @@ static const int kernel_feature_bits[] = {
>
> OBJECT_DECLARE_SIMPLE_TYPE(TAPState, TAP_NETDEV)
>
> +static const VMStateDescription vmstate_tap;
> +
> struct TAPState {
> Object parent_obj;
>
> @@ -95,6 +102,7 @@ struct TAPState {
> int queue_index;
> bool read_poll_detached;
> VMChangeStateEntry *vmstate;
> + bool local_migration_supported;
> };
>
> static void launch_script(const char *setup_script, const char *ifname,
> @@ -405,6 +413,8 @@ static void tap_cleanup(NetClientState *nc)
> tap_write_poll(s, false);
> close(s->fd);
> s->fd = -1;
> +
> + vmstate_unregister(VMSTATE_IF(s), &vmstate_tap, s);
> }
>
> static void tap_poll(NetClientState *nc, bool enable)
> @@ -441,6 +451,73 @@ static VHostNetState *tap_get_vhost_net(NetClientState *nc)
> return s->vhost_net;
> }
>
> +static bool tap_is_wait_incoming(NetClientState *nc)
> +{
> + TAPState *s = container_of(nc, TAPState, 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 bool tap_needed(void *opaque)
> +{
> + TAPState *s = opaque;
> +
> + return s->local_migration_supported && migrate_local();
> +}
> +
> +static const VMStateDescription vmstate_tap = {
> + .name = "net-tap",
> + .priority = MIG_PRI_BACKEND,
> + .pre_load = tap_pre_load,
> + .post_load = tap_post_load,
> + .needed = tap_needed,
> + .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()
> + }
> +};
>
> static char *tap_vmstate_if_get_id(VMStateIf *obj)
> {
> @@ -449,17 +526,42 @@ static char *tap_vmstate_if_get_id(VMStateIf *obj)
> return res;
> }
>
> +static bool tap_get_local_migration_supported_prop(Object *obj, Error **errp)
> +{
> + TAPState *s = TAP_NETDEV(obj);
> + return s->local_migration_supported;
> +}
> +
> +static void tap_set_local_migration_supported_prop(Object *obj, bool value,
> + Error **errp)
> +{
> + TAPState *s = TAP_NETDEV(obj);
> + s->local_migration_supported = value;
> +}
> +
> +static void tap_instance_init(Object *obj)
> +{
> + TAPState *s = TAP_NETDEV(obj);
> + s->local_migration_supported = true;
> +}
> +
> static void tap_class_init(ObjectClass *klass, const void *data)
> {
> VMStateIfClass *vc = VMSTATE_IF_CLASS(klass);
>
> vc->get_id = tap_vmstate_if_get_id;
> +
> + object_class_property_add_bool(klass, "local-migration-supported",
> + tap_get_local_migration_supported_prop,
> + tap_set_local_migration_supported_prop);
> }
>
> static const TypeInfo tap_netdev_info = {
> .name = TYPE_TAP_NETDEV,
> .parent = TYPE_OBJECT,
> .instance_size = sizeof(TAPState),
> + .instance_init = tap_instance_init,
> + .instance_post_init = object_apply_compat_props,
> .class_init = tap_class_init,
> .interfaces = (const InterfaceInfo[]) {
> { TYPE_VMSTATE_IF },
> @@ -492,13 +594,16 @@ 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,
> };
>
> static TAPState *new_tap(NetClientState *peer,
> const char *model,
> const char *name,
> - int queue_index)
> + int queue_index,
> + bool has_local_migration_supported,
> + bool local_migration_supported)
> {
> TAPState *s = TAP_NETDEV(object_new(TYPE_TAP_NETDEV));
>
> @@ -507,6 +612,12 @@ static TAPState *new_tap(NetClientState *peer,
>
> s->queue_index = queue_index;
>
> + if (has_local_migration_supported) {
> + s->local_migration_supported = local_migration_supported;
> + }
> +
> + vmstate_register(VMSTATE_IF(s), VMSTATE_INSTANCE_ID_ANY, &vmstate_tap, s);
> +
> return s;
> }
>
> @@ -515,10 +626,14 @@ static TAPState *net_tap_fd_init(NetClientState *peer,
> const char *name,
> int fd,
> int vnet_hdr,
> - int queue_index)
> + int queue_index,
> + bool has_local_migration_supported,
> + bool local_migration_supported)
> {
> NetOffloads ol = {};
> - TAPState *s = new_tap(peer, model, name, queue_index);
> + TAPState *s = new_tap(peer, model, name, queue_index,
> + has_local_migration_supported,
> + local_migration_supported);
>
> s->fd = fd;
> s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0;
> @@ -755,7 +870,7 @@ int net_init_bridge(const Netdev *netdev, const char *name,
> close(fd);
> return -1;
> }
> - s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr, 0);
> + s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr, 0, true, false);
>
> qemu_set_info_str(&s->nc, "helper=%s,br=%s", helper, br);
>
> @@ -835,7 +950,9 @@ static bool net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
> Error **errp)
> {
> TAPState *s = net_tap_fd_init(peer, tap->helper ? "bridge" : "tap",
> - name, fd, vnet_hdr, queue_index);
> + name, fd, vnet_hdr, queue_index,
> + tap->has_local_migration_supported,
> + tap->local_migration_supported);
> bool sndbuf_required = tap->has_sndbuf;
> int sndbuf =
> (tap->has_sndbuf && tap->sndbuf) ? MIN(tap->sndbuf, INT_MAX) : INT_MAX;
> @@ -983,6 +1100,7 @@ int net_init_tap(const Netdev *netdev, const char *name,
> /* for the no-fd, no-helper case */
> char ifname[128];
> int *fds = NULL, *vhost_fds = NULL;
> + bool incoming_fds;
>
> assert(netdev->type == NET_CLIENT_DRIVER_TAP);
> tap = &netdev->u.tap;
> @@ -1005,6 +1123,23 @@ int net_init_tap(const Netdev *netdev, const char *name,
> return -1;
> }
>
> + incoming_fds = tap->local_migration_supported && migrate_local() &&
> + runstate_check(RUN_STATE_INMIGRATE);
> +
> + if (incoming_fds &&
> + (tap->fd || tap->fds || tap->helper || tap->br || tap->ifname ||
> + tap->has_sndbuf || tap->has_vnet_hdr ||
> + !tap_is_explicit_no_script(tap->script) ||
> + !tap_is_explicit_no_script(tap->downscript))) {
> + error_setg(errp, "Local incoming migration of TAP device (-incoming, "
> + "migration parameter @local is set, "
> + "TAP parameter @local-migration-supported is set) "
> + "is incompatible with "
> + "fd=, fds=, helper=, br=, ifname=, sndbuf= and vnet_hdr=, "
> + "and requires explicit empty script= and downscript=");
> + return -1;
> + }
> +
> queues = tap_parse_fds_and_queues(tap, &fds, errp);
> if (queues < 0) {
> return -1;
> @@ -1023,7 +1158,22 @@ int net_init_tap(const Netdev *netdev, const char *name,
> goto fail;
> }
>
> - if (fds) {
> + if (incoming_fds) {
> + for (i = 0; i < queues; i++) {
> + TAPState *s = new_tap(peer, "tap", name, i,
> + tap->has_local_migration_supported,
> + tap->local_migration_supported);
> + qemu_set_info_str(&s->nc, "incoming");
> +
> + 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 ada0329ef9d..2070979f20e 100644
> --- a/qapi/net.json
> +++ b/qapi/net.json
> @@ -435,6 +435,26 @@
> # @poll-us: maximum number of microseconds that could be spent on busy
> # polling for tap (since 2.7)
> #
> +# @local-migration-supported: enable local migration for this TAP
> +# backend. When set, local migration is enabled/disabled by
> +# migration parameter @local for this TAP backend. When unset,
> +# migration parameter @local is ignored for this TAP backend.
> +# To be able to do incoming local migration of TAP backend,
> +# migration parameter @local must be set _before_ creating the
> +# TAP backend. Otherwise, TAP backend is initialized as usual,
> +# opening/creating TAP devices in kernel. In this case further
> +# local incoming migration (with migration parameter @local set
> +# after creating TAP backend with @local-migration-supporeted
> +# parameter set) will simply fail.
> +# Moreover, when QEMU is in incoming migration state, migration
> +# parameter @local is set and @local-migration-supported is set,
> +# following options are not supported and must not be set:
> +# @ds, @fds, @helper, @br, @ifname, @sndbuf, @vnet_hdr.
> +# Additionally in this case @script and @downscipt must be
> +# explicitly disabled (empty strings or "no").
> +# (Since 11.2. Defaults to true for QEMU Machine Types >= 11.2,
> +# and to false for QEMU Machine Types < 11.2)
> +#
> # Since: 1.2
> ##
> { 'struct': 'NetdevTapOptions',
> @@ -453,7 +473,8 @@
> '*vhostfds': 'str',
> '*vhostforce': 'bool',
> '*queues': 'uint32',
> - '*poll-us': 'uint32'} }
> + '*poll-us': 'uint32',
> + '*local-migration-supported': 'bool' } }
>
> ##
> # @NetdevSocketOptions:
> --
> 2.43.0
^ permalink raw reply [flat|nested] 44+ messages in thread* Re: [PATCH v18 12/14] net/tap: support local migration with virtio-net
2026-07-13 19:36 ` Michael S. Tsirkin
@ 2026-07-14 6:43 ` Vladimir Sementsov-Ogievskiy
0 siblings, 0 replies; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-14 6:43 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: jasowang, armbru, peterx, farosas, raphael.s.norwitz, bchaney,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Philippe Mathieu-Daudé, Zhao Liu, Jason Wang, Eric Blake
On 13.07.26 22:36, Michael S. Tsirkin wrote:
> On Fri, Jul 10, 2026 at 10:18:01PM +0300, Vladimir Sementsov-Ogievskiy wrote:
>> Support transferring of TAP state (including open fd).
>>
>> Add new property "local-migration-supported", which defines
>> whether local-migration is actually supported for this TAP device.
>> Starting from 11.2 QEMU Machine Types it's enabled by default.
>>
>> Note, that local-migration (including migrating opened FDs
>> through migration channel, which must be UNIX socket), is
>> enabled by global "local" migration parameters. But individual
>> devices may have additional options to enable/disable it
>> per device.
>>
>> The tricky thing, is that we need to know, should we do
>> call open/connect in TAP initialization code. That means,
>> that on incoming migration we want to know status of
>> "local" migration parameter at time of creating TAP device.
>> So, currently, for incoming local migration only TAP device
>> created through QMP is supported, as you need to set migration
>> parameter first (through QMP as well).
>>
>> So the full picture is:
>>
>> On source, to start outgoing "local" migration you need:
>>
>> - migration parameter "local" set to true
>> - "local-migration-supported" TAP option set to true (the
>> default, starting from 11.2 QEMU Machine Types)
>>
>> If at least one of these options is not set, TAP backend
>> doesn't participate in migration.
>>
>> On target, things are more difficult:
>>
>> Same, you need both "local" and "local-migration-supported"
>> be set. And same, if one of them is not set, TAP backend
>> is initialized as usual, and doesn't accept any incoming
>> state.
>>
>> Additionally, if you are going to set "local", it must be
>> set before creating the TAP device. If TAP device created
>> with "local" unset, it initializes as usual. If you enable
>> "local" after it and start incoming migration, it will fail
>> in .pre_load handler of TAP backend.
>>
>> Moreover, there are interface restrictions: if you create TAP
>> device when QEMU is in INCOMING state, and both "local"
>> and "local-migration-supported" set, most of TAP options are
>> not allowed, and script/downscript are required to be explicitly
>> unset (set to "" or "no").
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy<vsementsov@yandex-team.ru>
>
> I am still confused why is this per device. I just do not
> see where is it useful to set it for some devices
> but not others. So it looks like a machine property
> "incoming local migration" to me?
>
> Sorry if I am being dense.
1. We need at least "per device class": when local-migration-support is
implemented for some new device, we still need to migrate from/to older
version without this support.
Assume we have "local" migration parameter and support for TAP in 11.2.
Then, in 12.0 support for vhost-user-blk is implemented. If we have only
global "local" parameter, the migration will be broken between 11.2 and
12.0, with local=true, as in 11.2 it means only TAP local migration, but
in 12.0 it means both TAP and vhost-user-blk local migration.
Of course, now it's a bit redundant, as we have only TAP, and may define,
that TAP is always supported (as support is introduced together with
"local"). But there are another considerations:
2. Symmetry: if have a possiblity to disable feature only for vhost-user-blk,
seems good to have same possibility for TAP.
3. Disabling fd-migration per device may be useful to switch this specific
device to another backend.
--
Best regards,
Vladimir
^ permalink raw reply [flat|nested] 44+ messages in thread
* [PATCH v18 13/14] tests/functional: add skipWithoutSudo() decorator
2026-07-10 19:17 [PATCH v18 00/14] virtio-net: live-TAP local migration Vladimir Sementsov-Ogievskiy
` (11 preceding siblings ...)
2026-07-10 19:18 ` [PATCH v18 12/14] net/tap: support local migration with virtio-net Vladimir Sementsov-Ogievskiy
@ 2026-07-10 19:18 ` Vladimir Sementsov-Ogievskiy
2026-07-10 19:18 ` [PATCH v18 14/14] tests/functional: add test_tap_migration Vladimir Sementsov-Ogievskiy
13 siblings, 0 replies; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-10 19:18 UTC (permalink / raw)
To: jasowang, mst
Cc: armbru, peterx, farosas, raphael.s.norwitz, bchaney, vsementsov,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Thomas Huth, Lei Yang, Maksim Davydov, Thomas Huth,
Philippe Mathieu-Daudé
To be used in the next commit: that would be a test for TAP
networking, and it will need to setup TAP device.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Reviewed-by: Thomas Huth <thuth@redhat.com>
Tested-by: Lei Yang <leiyang@redhat.com>
Reviewed-by: Maksim Davydov <davydov-max@yandex-team.ru>
Reviewed-by: Ben Chaney <bchaney@akamai.com>
---
tests/functional/qemu_test/decorators.py | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/tests/functional/qemu_test/decorators.py b/tests/functional/qemu_test/decorators.py
index fcf236ecfdf..aa135acc785 100644
--- a/tests/functional/qemu_test/decorators.py
+++ b/tests/functional/qemu_test/decorators.py
@@ -6,6 +6,7 @@
import os
import platform
import resource
+import subprocess
from unittest import skipIf, skipUnless
from .cmd import which
@@ -177,3 +178,18 @@ def skipLockedMemoryTest(locked_memory):
ulimit_memory == resource.RLIM_INFINITY or ulimit_memory >= locked_memory * 1024,
f'Test required {locked_memory} kB of available locked memory',
)
+
+'''
+Decorator to skip execution of a test if passwordless
+sudo command is not available.
+'''
+def skipWithoutSudo():
+ proc = subprocess.run(["sudo", "-n", "/bin/true"],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ universal_newlines=True,
+ check=False)
+
+ return skipUnless(proc.returncode == 0,
+ f'requires password-less sudo access: {proc.stdout}')
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread* [PATCH v18 14/14] tests/functional: add test_tap_migration
2026-07-10 19:17 [PATCH v18 00/14] virtio-net: live-TAP local migration Vladimir Sementsov-Ogievskiy
` (12 preceding siblings ...)
2026-07-10 19:18 ` [PATCH v18 13/14] tests/functional: add skipWithoutSudo() decorator Vladimir Sementsov-Ogievskiy
@ 2026-07-10 19:18 ` Vladimir Sementsov-Ogievskiy
13 siblings, 0 replies; 44+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2026-07-10 19:18 UTC (permalink / raw)
To: jasowang, mst
Cc: armbru, peterx, farosas, raphael.s.norwitz, bchaney, vsementsov,
qemu-devel, berrange, pbonzini, yc-core, mark.caveayland,
Zhao Liu
Add test for a new local-migration migration of virtio-net/tap, with fd
passing through UNIX socket.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Reviewed-by: Ben Chaney <bchaney@akamai.com>
---
tests/functional/x86_64/meson.build | 1 +
tests/functional/x86_64/test_tap_migration.py | 455 ++++++++++++++++++
2 files changed, 456 insertions(+)
create mode 100755 tests/functional/x86_64/test_tap_migration.py
diff --git a/tests/functional/x86_64/meson.build b/tests/functional/x86_64/meson.build
index 27b31f2e966..eaf2ec0005a 100644
--- a/tests/functional/x86_64/meson.build
+++ b/tests/functional/x86_64/meson.build
@@ -47,4 +47,5 @@ tests_x86_64_system_thorough = [
'virtio_balloon',
'virtio_gpu',
'rebuild_vmfd',
+ 'tap_migration',
]
diff --git a/tests/functional/x86_64/test_tap_migration.py b/tests/functional/x86_64/test_tap_migration.py
new file mode 100755
index 00000000000..bcc0a60dfe9
--- /dev/null
+++ b/tests/functional/x86_64/test_tap_migration.py
@@ -0,0 +1,455 @@
+#!/usr/bin/env python3
+#
+# Functional test that tests TAP local migration
+# with fd passing
+#
+# Copyright (c) Yandex Technologies LLC, 2026
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import os
+import time
+import subprocess
+from subprocess import run
+import signal
+import ctypes
+import ctypes.util
+import unittest
+from contextlib import contextmanager, ExitStack
+from typing import Tuple
+
+from qemu_test import (
+ LinuxKernelTest,
+ Asset,
+ exec_command_and_wait_for_pattern,
+)
+from qemu_test.decorators import skipWithoutSudo
+
+
+GUEST_IP = "192.168.100.2"
+GUEST_IP_MASK = f"{GUEST_IP}/24"
+GUEST_MAC = "d6:0d:75:f8:0f:b7"
+HOST_IP = "192.168.100.1"
+HOST_IP_MASK = f"{HOST_IP}/24"
+TAP_ID = "tap0"
+TAP_ID2 = "tap1"
+TAP_MAC = "e6:1d:44:b5:03:5d"
+NETNS = f"qemu_test_ns_{os.getpid()}"
+
+
+def ip(args, check=True) -> None:
+ """Run ip command with sudo"""
+ run(["sudo", "ip"] + args, check=check)
+
+
+@contextmanager
+def switch_netns(netns_name):
+ libc = ctypes.CDLL(ctypes.util.find_library("c"))
+ netns_path = f"/var/run/netns/{netns_name}"
+
+ def switch_to_fd(fd, check: bool = False):
+ """Switch to netns by file descriptor"""
+ SYS_setns = 308
+ CLONE_NEWNET = 0x40000000
+ ret = libc.syscall(SYS_setns, fd, CLONE_NEWNET)
+ if check and ret != 0:
+ raise RuntimeError("syscall SETNS failed")
+
+ with ExitStack() as stack:
+ original_netns_fd = os.open("/proc/self/ns/net", os.O_RDONLY)
+ stack.callback(os.close, original_netns_fd)
+
+ ip(["netns", "add", netns_name])
+ stack.callback(ip, ["netns", "del", netns_name], check=False)
+
+ new_netns_fd = os.open(netns_path, os.O_RDONLY)
+ stack.callback(os.close, new_netns_fd)
+
+ switch_to_fd(new_netns_fd)
+ stack.callback(switch_to_fd, original_netns_fd, check=False)
+
+ yield
+
+
+def del_tap(tap_name: str = TAP_ID) -> None:
+ ip(["tuntap", "del", tap_name, "mode", "tap", "multi_queue"], check=False)
+
+
+def init_tap(tap_name: str = TAP_ID, with_ip: bool = True) -> None:
+ ip(["tuntap", "add", "dev", tap_name, "mode", "tap", "multi_queue"])
+ if with_ip:
+ ip(["link", "set", "dev", tap_name, "address", TAP_MAC])
+ ip(["addr", "add", HOST_IP_MASK, "dev", tap_name])
+ ip(["link", "set", tap_name, "up"])
+
+
+def switch_network_to_tap2() -> None:
+ ip(["link", "set", TAP_ID2, "down"])
+ ip(["link", "set", TAP_ID, "down"])
+ ip(["addr", "delete", HOST_IP_MASK, "dev", TAP_ID])
+ ip(["link", "set", "dev", TAP_ID2, "address", TAP_MAC])
+ ip(["addr", "add", HOST_IP_MASK, "dev", TAP_ID2])
+ ip(["link", "set", TAP_ID2, "up"])
+
+
+def parse_ping_line(line: str) -> float:
+ # suspect lines like
+ # [1748524876.590509] 64 bytes from 94.245.155.3 \
+ # (94.245.155.3): icmp_seq=1 ttl=250 time=101 ms
+ spl = line.split()
+ return float(spl[0][1:-1])
+
+
+def parse_ping_output(out) -> Tuple[bool, float, float]:
+ lines = [x for x in out.split("\n") if x.startswith("[")]
+
+ try:
+ first_no_ans = next(
+ (ind for ind in range(len(lines)) if lines[ind][20:26] == "no ans")
+ )
+ except StopIteration:
+ return False, parse_ping_line(lines[0]), parse_ping_line(lines[-1])
+
+ last_no_ans = next(
+ ind
+ for ind in range(len(lines) - 1, -1, -1)
+ if lines[ind][20:26] == "no ans"
+ )
+
+ return (
+ True,
+ parse_ping_line(lines[first_no_ans]),
+ parse_ping_line(lines[last_no_ans]),
+ )
+
+
+def wait_migration_finish(source_vm, target_vm):
+ migr_events = (
+ ("MIGRATION", {"data": {"status": "completed"}}),
+ ("MIGRATION", {"data": {"status": "failed"}}),
+ )
+
+ source_e = source_vm.events_wait(migr_events)["data"]
+ target_e = target_vm.events_wait(migr_events)["data"]
+
+ source_s = source_vm.cmd("query-status")["status"]
+ target_s = target_vm.cmd("query-status")["status"]
+
+ assert (
+ source_e["status"] == "completed"
+ and target_e["status"] == "completed"
+ and source_s == "postmigrate"
+ and target_s == "paused"
+ ), f"""Migration failed:
+ SRC status: {source_s}
+ SRC event: {source_e}
+ TGT status: {target_s}
+ TGT event:{target_e}"""
+
+
+@skipWithoutSudo()
+class TAPFdMigration(LinuxKernelTest):
+
+ ASSET_KERNEL = Asset(
+ (
+ "https://archives.fedoraproject.org/pub/archive/fedora/linux/releases"
+ "/31/Server/x86_64/os/images/pxeboot/vmlinuz"
+ ),
+ "d4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129",
+ )
+
+ ASSET_INITRD = Asset(
+ (
+ "https://archives.fedoraproject.org/pub/archive/fedora/linux/releases"
+ "/31/Server/x86_64/os/images/pxeboot/initrd.img"
+ ),
+ "277cd6c7adf77c7e63d73bbb2cded8ef9e2d3a2f100000e92ff1f8396513cd8b",
+ )
+
+ ASSET_ALPINE_ISO = Asset(
+ (
+ "https://dl-cdn.alpinelinux.org/"
+ "alpine/v3.22/releases/x86_64/alpine-standard-3.22.1-x86_64.iso"
+ ),
+ "96d1b44ea1b8a5a884f193526d92edb4676054e9fa903ad2f016441a0fe13089",
+ )
+
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+
+ try:
+ cls.netns_context = switch_netns(NETNS)
+ cls.netns_context.__enter__()
+ except (OSError, subprocess.CalledProcessError) as e:
+ raise unittest.SkipTest(f"can't switch network namespace: {e}")
+
+ @classmethod
+ def tearDownClass(cls):
+ if hasattr(cls, "netns_context"):
+ cls.netns_context.__exit__(None, None, None)
+ super().tearDownClass()
+
+ def setUp(self):
+ super().setUp()
+
+ init_tap()
+
+ self.outer_ping_proc = None
+ self.shm_path = None
+
+ def tearDown(self):
+ try:
+ del_tap(TAP_ID)
+ del_tap(TAP_ID2)
+
+ if self.outer_ping_proc:
+ self.stop_outer_ping()
+
+ if self.shm_path:
+ os.unlink(self.shm_path)
+ finally:
+ super().tearDown()
+
+ def start_outer_ping(self) -> None:
+ assert self.outer_ping_proc is None
+ self.outer_ping_log = self.scratch_file("ping.log")
+ with open(self.outer_ping_log, "w") as f:
+ self.outer_ping_proc = subprocess.Popen(
+ ["ping", "-i", "0", "-O", "-D", GUEST_IP],
+ text=True,
+ stdout=f,
+ )
+
+ def stop_outer_ping(self) -> str:
+ assert self.outer_ping_proc
+ self.outer_ping_proc.send_signal(signal.SIGINT)
+
+ self.outer_ping_proc.communicate(timeout=5)
+ self.outer_ping_proc = None
+
+ with open(self.outer_ping_log) as f:
+ return f.read()
+
+ def stop_ping_and_check(self, stop_time, resume_time):
+ ping_res = self.stop_outer_ping()
+
+ discon, a, b = parse_ping_output(ping_res)
+
+ if not discon:
+ text = (
+ f"STOP: {stop_time}, RESUME: {resume_time}," f"PING: {a} - {b}"
+ )
+ if a > stop_time or b < resume_time:
+ self.fail(f"PING failed: {text}")
+ self.log.info(f"PING: no packets lost: {text}")
+ return
+
+ text = (
+ f"STOP: {stop_time}, RESUME: {resume_time},"
+ f"PING: disconnect: {a} - {b}"
+ )
+ self.log.info(text)
+ eps = 0.05
+ if a < stop_time - eps or b > resume_time + eps:
+ self.fail(text)
+
+ def one_ping_from_guest(self, vm) -> None:
+ exec_command_and_wait_for_pattern(
+ self,
+ f"ping -c 1 -W 1 {HOST_IP}",
+ "1 packets transmitted, 1 packets received",
+ "1 packets transmitted, 0 packets received",
+ vm=vm,
+ )
+ self.wait_for_console_pattern("# ", vm=vm)
+
+ def one_ping_from_host(self) -> None:
+ run(
+ ["ping", "-c", "1", "-W", "1", GUEST_IP],
+ stdout=subprocess.DEVNULL,
+ check=True,
+ )
+
+ def setup_shared_memory(self):
+ self.shm_path = f"/dev/shm/qemu_test_{os.getpid()}"
+
+ try:
+ with open(self.shm_path, "wb") as f:
+ f.write(b"\0" * (1024 * 1024 * 1024)) # 1GB
+ except Exception as e:
+ self.fail(f"Failed to create shared memory file: {e}")
+
+ def prepare_and_launch_vm(
+ self, shm_path, vhost, incoming=False, vm=None, local=True
+ ):
+ if not vm:
+ vm = self.vm
+
+ vm.set_console()
+ vm.add_args("-accel", "kvm")
+ vm.add_args("-device", "pcie-pci-bridge,id=pci.1,bus=pcie.0")
+ vm.add_args("-m", "1G")
+
+ vm.add_args(
+ "-object",
+ f"memory-backend-file,id=ram0,size=1G,mem-path={shm_path},share=on",
+ )
+ vm.add_args("-machine", "memory-backend=ram0")
+
+ vm.add_args(
+ "-drive",
+ f"file={self.ASSET_ALPINE_ISO.fetch()},media=cdrom,format=raw",
+ )
+
+ vm.add_args("-S")
+
+ if incoming:
+ vm.add_args("-incoming", "defer")
+
+ vm_s = "target" if incoming else "source"
+ self.log.info(f"Launching {vm_s} VM")
+ vm.launch()
+
+ if not local:
+ tap_name = TAP_ID2 if incoming else TAP_ID
+ else:
+ tap_name = TAP_ID
+
+ self.set_migration_capabilities(vm, local)
+ self.add_virtio_net(vm, vhost, tap_name, local, incoming)
+
+ def add_virtio_net(
+ self, vm, vhost: bool, tap_name: str, local: bool, incoming: bool
+ ):
+ netdev_params = {
+ "id": "netdev.1",
+ "vhost": vhost,
+ "type": "tap",
+ "queues": 4,
+ "script": "no",
+ "downscript": "no",
+ "local-migration-supported": local,
+ }
+
+ if not (local and incoming):
+ netdev_params["vnet_hdr"] = True
+ netdev_params["ifname"] = tap_name
+
+ vm.cmd("netdev_add", netdev_params)
+
+ vm.cmd(
+ "device_add",
+ driver="virtio-net-pci",
+ romfile="",
+ id="vnet.1",
+ netdev="netdev.1",
+ mq=True,
+ vectors=18,
+ bus="pci.1",
+ mac=GUEST_MAC,
+ disable_legacy="off",
+ )
+
+ def set_migration_capabilities(self, vm, local=True):
+ vm.cmd(
+ "migrate-set-capabilities",
+ {
+ "capabilities": [
+ {"capability": "events", "state": True},
+ {"capability": "x-ignore-shared", "state": True},
+ ]
+ },
+ )
+ vm.cmd("migrate-set-parameters", {"local": local})
+
+ def setup_guest_network(self) -> None:
+ exec_command_and_wait_for_pattern(self, "ip addr", "# ")
+ exec_command_and_wait_for_pattern(
+ self,
+ f"ip addr add {GUEST_IP_MASK} dev eth0 && "
+ "ip link set eth0 up && echo OK",
+ "OK",
+ )
+ self.wait_for_console_pattern("# ")
+
+ def do_test_tap_fd_migration(self, vhost, local=True):
+ self.require_accelerator("kvm")
+ self.set_machine("q35")
+
+ socket_dir = self.socket_dir()
+ migration_socket = os.path.join(socket_dir.name, "migration.sock")
+
+ self.setup_shared_memory()
+
+ # Setup second TAP if needed
+ if not local:
+ del_tap(TAP_ID2)
+ init_tap(TAP_ID2, with_ip=False)
+
+ self.prepare_and_launch_vm(self.shm_path, vhost, local=local)
+ self.vm.cmd("cont")
+ self.wait_for_console_pattern("login:")
+ exec_command_and_wait_for_pattern(self, "root", "# ")
+
+ self.setup_guest_network()
+
+ self.one_ping_from_guest(self.vm)
+ self.one_ping_from_host()
+ self.start_outer_ping()
+
+ # Get some successful pings before migration
+ time.sleep(0.5)
+
+ target_vm = self.get_vm(name="target")
+ self.prepare_and_launch_vm(
+ self.shm_path,
+ vhost,
+ incoming=True,
+ vm=target_vm,
+ local=local,
+ )
+
+ target_vm.cmd("migrate-incoming", {"uri": f"unix:{migration_socket}"})
+
+ self.log.info("Starting migration")
+ freeze_start = time.time()
+ self.vm.cmd("migrate", {"uri": f"unix:{migration_socket}"})
+
+ self.log.info("Waiting for migration completion")
+ wait_migration_finish(self.vm, target_vm)
+
+ # Switch network to tap1 if not using local-migration
+ if not local:
+ switch_network_to_tap2()
+
+ target_vm.cmd("cont")
+ freeze_end = time.time()
+
+ self.vm.shutdown()
+
+ self.log.info("Verifying PING on target VM after migration")
+ self.one_ping_from_guest(target_vm)
+ self.one_ping_from_host()
+
+ # And a bit more pings after source shutdown
+ time.sleep(0.3)
+ self.stop_ping_and_check(freeze_start, freeze_end)
+
+ target_vm.shutdown()
+
+ def test_tap_fd_migration(self):
+ self.do_test_tap_fd_migration(False)
+
+ def test_tap_fd_migration_vhost(self):
+ self.do_test_tap_fd_migration(True)
+
+ def test_tap_new_tap_migration(self):
+ self.do_test_tap_fd_migration(False, local=False)
+
+ def test_tap_new_tap_migration_vhost(self):
+ self.do_test_tap_fd_migration(True, local=False)
+
+
+if __name__ == "__main__":
+ LinuxKernelTest.main()
--
2.43.0
^ permalink raw reply related [flat|nested] 44+ messages in thread