All Virtuozzo development lists (kernel + QEMU)
 help / color / mirror / Atom feed
* [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860
@ 2026-11-03 13:57 Andrey Drobyshev
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 01/35] VZ: migration/cpr-exec: fix Error leak in preserve_ram() Andrey Drobyshev
                   ` (35 more replies)
  0 siblings, 36 replies; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

v6 -> v7:

Fixes of the issues found by Sashiko AI review.

- Patch 4 ("migration/cpr-exec: require a file or exec migration URI"):
  the message says the check covers the incoming URI too

- Patch 6 ("migration/cpr-exec: close the persisted state if the
  channel fails to open"): the fd is parsed before its variable is
  unset

- Patch 7 (NEW) ("migration/cpr-exec: don't leak the dup'd memfd if
  g_setenv() fails"): the dup was leaked on that path and a failed
  dup() went unchecked; the patches after it are renumbered

- Patch 32 -> 33 ("VZ: vfio/region: preserve BAR mappings across
  cpr-exec"): CPR.rst no longer says cpr-transfer preserves VFIO
  devices

- Patch 34 -> 35 ("VZ: vfio/migration: don't wait for stop-copy-only
  device state"): the message covers devices without pre-copy, which
  the rule applies to as well

v6: http://lore.virtuozzo.com/20261102-vfio_v1-v6-0-9bc7cabc850d@virtuozzo.com
---
Akihiko Odaki (1):
      vfio/pci: Grow buffer in vfio_pci_host_match()

Andrey Drobyshev (21):
      VZ: migration/cpr-exec: fix Error leak in preserve_ram()
      migration/cpr-exec: restore RAM write access if exec fails
      vfio/region: create BAR dma-bufs with O_CLOEXEC
      migration/cpr-exec: require a file or exec migration URI
      migration: close the CPR state if the outgoing channel fails to open
      migration/cpr-exec: close the persisted state if the channel fails to open
      migration/cpr-exec: don't leak the dup'd memfd if g_setenv() fails
      migration: don't refuse a blocker over a migration of another mode
      kvm: tolerate ioctls after kvm_close()
      kvm: fix coalesced MMIO flush racing with kvm_close()
      migration/cpr: add cpr_is_in_progress()
      vfio/migration: don't migrate device state during CPR
      vfio/migration: don't assume the device is running on CPR incoming
      migration/ram: don't start dirty logging in CPR modes
      VZ: vfio/cpr: don't close KVM on cpr-exec
      VZ: migration/cpr-exec: abort cpr-exec if memory can't be preserved
      VZ: migration/cpr-exec: take the vhost-net devices back if exec fails
      VZ: migration/cpr-exec: don't exec if a vhost-net device can't be released
      VZ: vfio/region: preserve BAR mappings across cpr-exec #VSTOR-103860
      VZ: migration/cpr-exec: allow cpr-exec with VFIO devices #VSTOR-103860
      VZ: vfio/migration: don't wait for stop-copy-only device state #VSTOR-141821

Arun Menon (1):
      migration: Fix regression of passing error_fatal into vmstate_load_state()

Cédric Le Goater (2):
      vfio/pci: Reject invalid MSI-X Table and PBA BIR values
      vfio/pci: Fix information leak in vfio_rom_read()

Fabiano Rosas (3):
      migration: yank: Move register instance earlier
      migration: Free cpr-transfer MigrationAddress along with gsource
      migration: Move CPR HUP watch to cpr-transfer.c

Igor Mammedov (1):
      q35: Fix migration of SMRAM state

Mario Casquero (1):
      vfio/pci: Initialize rom_read_failed in vfio_pci_load_rom()

Markus Armbruster (1):
      migration: Plug memory leaks after migrate_set_error()

Peter Xu (1):
      migration: Fix crash on second migration when cancel early

Tycho Andersen (AMD) (1):
      hw/vfio: Fix liveness check in vfio_connect_kvm_msi_virq()

Vladimir Sementsov-Ogievskiy (1):
      migration: vmstate_save_state_v: fix double error_setg

Yanfei Xu (1):
      migration: ensure APIC is loaded prior to VFIO PCI devices

 accel/kvm/kvm-all.c           |  24 +++++++-
 docs/devel/migration/CPR.rst  |  12 ++--
 hw/display/virtio-gpu.c       |  22 +++++---
 hw/intc/apic_common.c         |   1 +
 hw/net/vhost_net.c            |  62 ++++++++++++++++++++-
 hw/pci-host/q35.c             |  25 ++++-----
 hw/pci/pci.c                  |  15 ++++-
 hw/s390x/virtio-ccw.c         |  17 +++++-
 hw/scsi/spapr_vscsi.c         |  10 +++-
 hw/vfio/cpr.c                 |  11 ++--
 hw/vfio/device.c              |  29 +++++++---
 hw/vfio/migration-multifd.c   |   2 +
 hw/vfio/migration.c           |  68 ++++++++++++++++++++---
 hw/vfio/pci.c                 |  25 ++++++---
 hw/vfio/region.c              |  51 ++++++++++++++++-
 hw/virtio/virtio-mmio.c       |  15 ++++-
 hw/virtio/virtio-pci.c        |  15 ++++-
 hw/virtio/virtio.c            |  10 +++-
 include/hw/vfio/vfio-cpr.h    |   1 +
 include/hw/vfio/vfio-device.h |   2 -
 include/hw/virtio/vhost.h     |   2 +
 include/migration/cpr.h       |   7 +++
 include/migration/vmstate.h   |   1 +
 include/net/vhost_net.h       |   3 +-
 migration/cpr-exec.c          |  54 +++++++++++++++---
 migration/cpr-transfer.c      |  33 +++++++++++
 migration/cpr.c               |  22 ++++++++
 migration/migration.c         | 126 ++++++++++++++++++++++++------------------
 migration/migration.h         |   5 ++
 migration/multifd.c           |   2 +
 migration/ram.c               |   7 ++-
 migration/vmstate.c           |   7 ++-
 tests/qtest/q35-test.c        |   6 --
 33 files changed, 546 insertions(+), 146 deletions(-)
---
base-commit: 1c84596876fb77b3e25d56cd4a4fa0c32f1faa32
change-id: 20261009-vfio_v1-a176cddd6b64

Best regards,
--  
Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 01/35] VZ: migration/cpr-exec: fix Error leak in preserve_ram()
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:00   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 02/35] migration/cpr-exec: restore RAM write access if exec fails Andrey Drobyshev
                   ` (34 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

migrate_set_error() stores its own copy of the error, so free ours.

Fixes: a3745506daf9 ("VZ: migration/cpr-exec: mark saved RAM blocks with MADV_DOEXEC before execve")
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 migration/cpr-exec.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
index edace3f0bd8..9fd3515753a 100644
--- a/migration/cpr-exec.c
+++ b/migration/cpr-exec.c
@@ -148,6 +148,7 @@ static bool preserve_ram(cpr_ramblock_t ram, char *name)
         error_report_err(error_copy(err));
         migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
         migrate_set_error(s, err);
+        error_free(err);
 
         return false;
     }

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 02/35] migration/cpr-exec: restore RAM write access if exec fails
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 01/35] VZ: migration/cpr-exec: fix Error leak in preserve_ram() Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:01   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 03/35] vfio/region: create BAR dma-bufs with O_CLOEXEC Andrey Drobyshev
                   ` (33 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

With -cpr-ram-rdonly the RAM blocks are made read-only right before
execve().  If execve() fails, the VM resumes in the old QEMU with
read-only RAM and crashes on the first write.  Make it writable again.

Fixes: 91da25f20471 ("migration/cpr-exec: add '-cpr-ram-rdonly' option to mark RAM blocks as RO #VSTOR-101579")
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 migration/cpr-exec.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
index 9fd3515753a..c4bdf2cb99f 100644
--- a/migration/cpr-exec.c
+++ b/migration/cpr-exec.c
@@ -224,6 +224,9 @@ static void cpr_exec_cb(void *opaque)
     error_setg_errno(&err, errno, "execvp %s failed", argv[0]);
     g_clear_pointer(&argv, g_strfreev);
     cpr_exec_unpreserve_fds();
+    if (cpr_ram_rdonly) {
+        cpr_ram_restore_rw();
+    }
 
     error_report_err(error_copy(err));
     migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 03/35] vfio/region: create BAR dma-bufs with O_CLOEXEC
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 01/35] VZ: migration/cpr-exec: fix Error leak in preserve_ram() Andrey Drobyshev
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 02/35] migration/cpr-exec: restore RAM write access if exec fails Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:02   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 04/35] migration/cpr-exec: require a file or exec migration URI Andrey Drobyshev
                   ` (32 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

The dma-buf fds are created without O_CLOEXEC, so they leak into any
exec'ed program.  With cpr-exec, every update leaks one fd per mmapped
BAR, as new QEMU creates its own.

Fixes: 96163e0dbcb4 ("hw/vfio/region: Create dmabuf for PCI BAR per region")
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 hw/vfio/region.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/hw/vfio/region.c b/hw/vfio/region.c
index 1deaef12fd0..b0332f27fcd 100644
--- a/hw/vfio/region.c
+++ b/hw/vfio/region.c
@@ -258,7 +258,7 @@ static bool vfio_region_create_dma_buf(VFIORegion *region, Error **errp)
     dma_buf = (void *)feature->data;
     *dma_buf = (struct vfio_device_feature_dma_buf) {
         .region_index = region->nr,
-        .open_flags = O_RDWR,
+        .open_flags = O_RDWR | O_CLOEXEC,
         .nr_ranges = region->nr_mmaps,
     };
 

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 04/35] migration/cpr-exec: require a file or exec migration URI
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (2 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 03/35] vfio/region: create BAR dma-bufs with O_CLOEXEC Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:02   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 05/35] migration: close the CPR state if the outgoing channel fails to open Andrey Drobyshev
                   ` (31 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

cpr-exec writes the device state to the migration URI and new QEMU
reads it back after the exec, so the URI must capture the data: a
file, or an exec command that stores it.  Nothing checks that.  With a
socket URI, e.g. a save or a live migration issued while the 'mode'
parameter is left at cpr-exec after a failed qemu-update, QEMU sends a
stream without the guest RAM to the peer, execs the stale cpr-exec
command, and the VM is lost.

Refuse cpr-exec with anything but a file or exec URI, the two types
the CPR documentation allows.  The check covers the incoming URI of
new QEMU too, where the mode is cpr-exec as well.

Fixes: a3eae205c601 ("migration: cpr-exec mode")
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 docs/devel/migration/CPR.rst | 8 ++++----
 migration/migration.c        | 8 ++++++++
 2 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/docs/devel/migration/CPR.rst b/docs/devel/migration/CPR.rst
index b6178568a85..2f34a6d7c9d 100644
--- a/docs/devel/migration/CPR.rst
+++ b/docs/devel/migration/CPR.rst
@@ -365,10 +365,10 @@ The VM must be started with the ``-machine aux-ram-share=on`` option.
 Outgoing:
   * Set the migration mode parameter to ``cpr-exec``.
   * Set the ``cpr-exec-command`` parameter.
-  * Issue the ``migrate`` command.  It is recommended that the URI be
-    a ``file`` type, but one can use other types such as ``exec``,
-    provided the command captures all the data from the outgoing side,
-    and provides all the data to the incoming side.
+  * Issue the ``migrate`` command.  The URI must be a ``file`` type,
+    or an ``exec`` type, provided the command captures all the data
+    from the outgoing side, and provides all the data to the incoming
+    side.  Other types are refused.
 
 Incoming:
   * You do not need to explicitly start new QEMU.  It is started as
diff --git a/migration/migration.c b/migration/migration.c
index 2f8ef20c036..39f70328fc7 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -258,6 +258,14 @@ migration_channels_and_transport_compatible(MigrationAddress *addr,
         return false;
     }
 
+    if (migrate_mode() == MIG_MODE_CPR_EXEC &&
+        addr->transport != MIGRATION_ADDRESS_TYPE_FILE &&
+        addr->transport != MIGRATION_ADDRESS_TYPE_EXEC) {
+        error_setg(errp,
+                   "cpr-exec mode requires a file or exec migration URI");
+        return false;
+    }
+
     return true;
 }
 

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 05/35] migration: close the CPR state if the outgoing channel fails to open
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (3 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 04/35] migration/cpr-exec: require a file or exec migration URI Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:03   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 06/35] migration/cpr-exec: close the persisted state if the " Andrey Drobyshev
                   ` (30 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

qmp_migrate() saves the CPR state before it opens the migration
channel.  If the channel is a file or an exec command and it can't be
opened, qmp_migrate_finish() returns right away and migration_cleanup()
never runs, so the CPR state file stays open.

Close it there, next to the yank instance, which has the same problem.

Fixes: 624e6e654e11 ("migration: cpr-transfer mode")
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 migration/migration.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/migration/migration.c b/migration/migration.c
index 39f70328fc7..132f0d403b7 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -2412,6 +2412,7 @@ static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested,
         if (!resume_requested) {
             yank_unregister_instance(MIGRATION_YANK_INSTANCE);
         }
+        cpr_state_close();
         migration_connect_set_error(s, local_err);
         error_propagate(errp, local_err);
         return;

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 06/35] migration/cpr-exec: close the persisted state if the channel fails to open
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (4 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 05/35] migration: close the CPR state if the outgoing channel fails to open Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:04   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 07/35] migration/cpr-exec: don't leak the dup'd memfd if g_setenv() fails Andrey Drobyshev
                   ` (29 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

cpr_exec_persist_state() dups the memfd holding the CPR state so that
the dup survives exec(), and remembers it in the environment.  Both
are released by cpr_exec_unpersist_state() alone, which runs only on
MIG_EVENT_PRECOPY_FAILED.  The migration channel failing to open
doesn't raise it, so the dup is leaked and the stale environment
variable is left behind, once per attempt.

Release them there, next to the CPR state.  That path runs in any
migration mode, so make cpr_exec_unpersist_state() a no-op if there's
nothing to release.

Fixes: a3eae205c60 ("migration: cpr-exec mode")
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 migration/cpr-exec.c  | 8 +++++---
 migration/migration.c | 1 +
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
index c4bdf2cb99f..b52f44b54ac 100644
--- a/migration/cpr-exec.c
+++ b/migration/cpr-exec.c
@@ -68,8 +68,8 @@ static int cpr_exec_find_state(void)
     int mfd;
 
     assert(val);
-    g_unsetenv(CPR_EXEC_STATE_NAME);
     assert(!qemu_strtoi(val, NULL, 10, &mfd));
+    g_unsetenv(CPR_EXEC_STATE_NAME);
     return mfd;
 }
 
@@ -83,9 +83,11 @@ void cpr_exec_unpersist_state(void)
     int mfd;
     const char *val = g_getenv(CPR_EXEC_STATE_NAME);
 
-    g_unsetenv(CPR_EXEC_STATE_NAME);
-    assert(val);
+    if (!val) {
+        return;
+    }
     assert(!qemu_strtoi(val, NULL, 10, &mfd));
+    g_unsetenv(CPR_EXEC_STATE_NAME);
     close(mfd);
 }
 
diff --git a/migration/migration.c b/migration/migration.c
index 132f0d403b7..7c2fd704890 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -2413,6 +2413,7 @@ static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested,
             yank_unregister_instance(MIGRATION_YANK_INSTANCE);
         }
         cpr_state_close();
+        cpr_exec_unpersist_state();
         migration_connect_set_error(s, local_err);
         error_propagate(errp, local_err);
         return;

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 07/35] migration/cpr-exec: don't leak the dup'd memfd if g_setenv() fails
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (5 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 06/35] migration/cpr-exec: close the persisted state if the " Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:04   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 08/35] migration: don't refuse a blocker over a migration of another mode Andrey Drobyshev
                   ` (28 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

cpr_exec_persist_state() dups the CPR state memfd for the exec and
records it in the environment.  If g_setenv() fails the dup isn't
closed, and the leak is inherited across exec.  The dup() result isn't
checked either.

Close the dup on that path and fail on a failed dup().

Fixes: a3eae205c60 ("migration: cpr-exec mode")
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 migration/cpr-exec.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
index b52f44b54ac..c0369b0b75e 100644
--- a/migration/cpr-exec.c
+++ b/migration/cpr-exec.c
@@ -51,11 +51,17 @@ bool cpr_exec_persist_state(QEMUFile *f, Error **errp)
     int mfd = dup(fioc->fd);
     char val[16];
 
+    if (mfd < 0) {
+        error_setg_errno(errp, errno, "dup of CPR state fd failed");
+        return false;
+    }
+
     /* Remember mfd in environment for post-exec load */
     qemu_clear_cloexec(mfd);
     snprintf(val, sizeof(val), "%d", mfd);
     if (!g_setenv(CPR_EXEC_STATE_NAME, val, 1)) {
         error_setg(errp, "Setting env %s = %s failed", CPR_EXEC_STATE_NAME, val);
+        close(mfd);
         return false;
     }
 

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 08/35] migration: don't refuse a blocker over a migration of another mode
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (6 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 07/35] migration/cpr-exec: don't leak the dup'd memfd if g_setenv() fails Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:05   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 09/35] migration: Plug memory leaks after migrate_set_error() Andrey Drobyshev
                   ` (27 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

migrate_add_blocker_modes() refuses a blocker while a migration is
running, whatever modes the blocker is for.  So, while e.g. a normal
live migration is running, adding a blocker for cpr-transfer only is
refused as well, and whatever operation adds it fails, although the
blocker doesn't concern that migration at all.

Only refuse a blocker if the running migration is of one of its modes.
Snapshots are checked as before.

Fixes: fa3673e497a ("migration: per-mode blockers")
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 migration/migration.c | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/migration/migration.c b/migration/migration.c
index 7c2fd704890..d5a21da3129 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -1866,12 +1866,16 @@ int migrate_init(MigrationState *s, Error **errp)
     return 0;
 }
 
-static bool is_busy(Error **reasonp, Error **errp)
+static bool is_busy(Error **reasonp, Error **errp, int modes)
 {
     ERRP_GUARD();
 
-    /* Snapshots are similar to migrations, so check RUN_STATE_SAVE_VM too. */
-    if (runstate_check(RUN_STATE_SAVE_VM) || migration_is_running()) {
+    /*
+     * Snapshots are similar to migrations, so check RUN_STATE_SAVE_VM too.
+     * A running migration only matters if the blocker applies to its mode.
+     */
+    if (runstate_check(RUN_STATE_SAVE_VM) ||
+        (migration_is_running() && (modes & BIT(migrate_mode())))) {
         error_propagate_prepend(errp, *reasonp,
                                 "disallowing migration blocker "
                                 "(migration/snapshot in progress) for: ");
@@ -1942,7 +1946,7 @@ int migrate_add_blocker_modes(Error **reasonp, Error **errp, MigMode mode, ...)
 
     if (is_only_migratable(reasonp, errp, modes)) {
         return -EACCES;
-    } else if (is_busy(reasonp, errp)) {
+    } else if (is_busy(reasonp, errp, modes)) {
         return -EBUSY;
     }
     return add_blockers(reasonp, errp, modes);
@@ -1952,7 +1956,7 @@ int migrate_add_blocker_internal(Error **reasonp, Error **errp)
 {
     int modes = BIT(MIG_MODE__MAX) - 1;
 
-    if (is_busy(reasonp, errp)) {
+    if (is_busy(reasonp, errp, modes)) {
         return -EBUSY;
     }
     return add_blockers(reasonp, errp, modes);

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 09/35] migration: Plug memory leaks after migrate_set_error()
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (7 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 08/35] migration: don't refuse a blocker over a migration of another mode Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:05   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 10/35] hw/vfio: Fix liveness check in vfio_connect_kvm_msi_virq() Andrey Drobyshev
                   ` (26 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

From: Markus Armbruster <armbru@redhat.com>

migrate_set_error(s, err) stores a copy of @err in @s.  The original
@err is not freed.  Most callers free it immediately.  Some callers
free it later, or pass it on.  And some leak it.  Fix those.

Perhaps migrate_set_error(s, err) should take ownership of @err.  The
callers that free it immediately would become simpler, and avoid a
copy and a deallocation.  The others would have to pass
error_copy(err).

andrey.drobyshev:
    This commit is cherry-picked as-is, with no modifications, from the
    upstream commit 93817ec39656.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Link: https://lore.kernel.org/r/20251115083500.2753895-2-armbru@redhat.com
Signed-off-by: Peter Xu <peterx@redhat.com>
---
 migration/cpr-exec.c | 3 ++-
 migration/multifd.c  | 2 ++
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
index c0369b0b75e..77db262c139 100644
--- a/migration/cpr-exec.c
+++ b/migration/cpr-exec.c
@@ -239,11 +239,12 @@ static void cpr_exec_cb(void *opaque)
     error_report_err(error_copy(err));
     migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
     migrate_set_error(s, err);
+    error_free(err);
+    err = NULL;
 
     /* Note, we can go from state COMPLETED to FAILED */
     migration_call_notifiers(MIG_EVENT_PRECOPY_FAILED, NULL);
 
-    err = NULL;
     if (!migration_block_activate(&err)) {
         /* error was already reported */
         error_free(err);
diff --git a/migration/multifd.c b/migration/multifd.c
index 5a723ae25d2..e369421b3ff 100644
--- a/migration/multifd.c
+++ b/migration/multifd.c
@@ -966,6 +966,7 @@ bool multifd_send_setup(void)
 
         if (!multifd_new_send_channel_create(p, &local_err)) {
             migrate_set_error(s, local_err);
+            error_free(local_err);
             ret = -1;
         }
     }
@@ -990,6 +991,7 @@ bool multifd_send_setup(void)
         ret = multifd_send_state->ops->send_setup(p, &local_err);
         if (ret) {
             migrate_set_error(s, local_err);
+            error_free(local_err);
             goto err;
         }
         assert(p->iov);

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 10/35] hw/vfio: Fix liveness check in vfio_connect_kvm_msi_virq()
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (8 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 09/35] migration: Plug memory leaks after migrate_set_error() Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:05   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 11/35] vfio/pci: Reject invalid MSI-X Table and PBA BIR values Andrey Drobyshev
                   ` (25 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

From: "Tycho Andersen (AMD)" <tycho@kernel.org>

While working on savevm/loadvm for a new vfio device, I encountered the
crash below. Since vfio_connect_kvm_msi_virq() didn't check the ->use flag
for the vector, it would pass an unused vector down to
vfio_cpr_load_vector_fd() which would crash.

Fix this by checking the ->use flag along with the virq number to detect
whether a vector is valid or not.

Thread 1 "qemu-system-x86" received signal SIGSEGV, Segmentation fault.
0x0000555555a891ef in vfio_cpr_load_vector_fd (vdev=vdev@entry=0x0,
    name=name@entry=0x555555eeb27e "kvm_interrupt", nr=nr@entry=1) at ../hw/vfio/cpr.c:44
44	    g_autofree char *fdname = STRDUP_VECTOR_FD_NAME(vdev, name);
(gdb) bt
    (vdev=vdev@entry=0x0, name=name@entry=0x555555eeb27e "kvm_interrupt", nr=nr@entry=1)
    at ../hw/vfio/cpr.c:44
    (vdev=0x0, e=e@entry=0x5555586971b4, name=name@entry=0x555555eeb27e "kvm_interrupt", nr=nr@entry=1, errp=errp@entry=0x0) at ../hw/vfio/pci.c:79
    at ../hw/vfio/pci.c:601
    at ../hw/vfio/pci.c:597
    at ../hw/vfio/pci.c:3088
    at ../hw/vfio/migration.c:278
    (f=f@entry=0x5555574a43d0, se=se@entry=0x5555591edd40, errp=errp@entry=0x7fffffffe130)
    at ../migration/savevm.c:971
    (f=f@entry=0x5555574a43d0, type=type@entry=4 '\004', errp=errp@entry=0x7fffffffe130)
    at ../migration/savevm.c:2654
    (f=f@entry=0x5555574a43d0, mis=mis@entry=0x5555571de5a0, errp=0x7fffffffe130,
    errp@entry=0x555557157c10 <error_fatal>) at ../migration/savevm.c:2973
    (f=f@entry=0x5555574a43d0, errp=errp@entry=0x555557157c10 <error_fatal>)
    at ../migration/savevm.c:3058
    (name=0x7fffffffecc9 "foo", vmstate=vmstate@entry=0x0, has_devices=has_devices@entry=false, devices=devices@entry=0x0, errp=errp@entry=0x555557157c10 <error_fatal>) at ../migration/savevm.c:3452

andrey.drobyshev:
    This commit is cherry-picked as-is, with no modifications, from the
    upstream commit f498c21b3595.

Fixes: 30edcb4d4e7a ("vfio-pci: preserve MSI")
Signed-off-by: Tycho Andersen (AMD) <tycho@kernel.org>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260727150038.2684512-1-tycho@kernel.org
Signed-off-by: Cédric Le Goater <clg@redhat.com>
---
 hw/vfio/pci.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index fd73a0fdf87..5e281b2e1d9 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -589,7 +589,7 @@ static void vfio_connect_kvm_msi_virq(VFIOMSIVector *vector, int nr)
 {
     const char *name = "kvm_interrupt";
 
-    if (vector->virq < 0) {
+    if (!vector->use || vector->virq < 0) {
         return;
     }
 

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 11/35] vfio/pci: Reject invalid MSI-X Table and PBA BIR values
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (9 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 10/35] hw/vfio: Fix liveness check in vfio_connect_kvm_msi_virq() Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:05   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 12/35] vfio/pci: Fix information leak in vfio_rom_read() Andrey Drobyshev
                   ` (24 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

From: Cédric Le Goater <clg@redhat.com>

The 3-bit MSI-X BIR fields permit values 0-7, but VFIOPCIDevice::bars[]
only contains BAR0-BAR5 (6 entries). An invalid BIR value of 6 or 7
can cause an out-of-bounds array access (CWE-129) in vfio_msix_early_setup().

Add range checks immediately after extracting the BIR values.

andrey.drobyshev:
    This commit is cherry-picked as-is, with no modifications, from the
    upstream commit 9daa7347ff25.

Reported-by: Feifan Qian <bea1e@proton.me>
Fixes: 65501a745dba ("vfio: vfio-pci device assignment driver")
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3878
Reviewed-by: Alex Williamson <alex@shazbot.org>
Link: https://lore.kernel.org/qemu-devel/20260706161334.2165482-1-clg@redhat.com
Signed-off-by: Cédric Le Goater <clg@redhat.com>
---
 hw/vfio/pci.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 5e281b2e1d9..d930ef46309 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -1791,6 +1791,14 @@ static bool vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp)
     msix->pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK;
     msix->entries = (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1;
 
+    if (msix->table_bar >= ARRAY_SIZE(vdev->bars) ||
+        msix->pba_bar >= ARRAY_SIZE(vdev->bars)) {
+        error_setg(errp, "invalid MSI-X BIR, table_bar=%d pba_bar=%d",
+                   msix->table_bar, msix->pba_bar);
+        g_free(msix);
+        return false;
+    }
+
     ret = vfio_device_get_irq_info(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX,
                                    &irq_info);
     if (ret < 0) {

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 12/35] vfio/pci: Fix information leak in vfio_rom_read()
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (10 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 11/35] vfio/pci: Reject invalid MSI-X Table and PBA BIR values Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:06   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 13/35] vfio/pci: Initialize rom_read_failed in vfio_pci_load_rom() Andrey Drobyshev
                   ` (23 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

From: Cédric Le Goater <clg@redhat.com>

vfio_rom_read() can leak uninitialized stack data to the guest when a
read occurs at the end boundary of the ROM BAR.

Fix this by initializing the val union to 0xff, matching the ROM
initialization pattern used in vfio_pci_load_rom().

andrey.drobyshev:
    This commit is cherry-picked as-is, with no modifications, from the
    upstream commit 402fd5a75659.

Reviewed-by: Philippe Mathieu-Daudé <philmd@mailo.com>
Reviewed-by: Mario Casquero <mcasquer@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260602074446.1206892-1-clg@redhat.com
Signed-off-by: Cédric Le Goater <clg@redhat.com>
---
 hw/vfio/pci.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index d930ef46309..fa2ca0d779d 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -1153,7 +1153,7 @@ static uint64_t vfio_rom_read(void *opaque, hwaddr addr, unsigned size)
         uint16_t word;
         uint32_t dword;
         uint64_t qword;
-    } val;
+    } val = { .qword = ~0ULL };
     uint64_t data = 0;
 
     /* Load the ROM lazily when the guest tries to read it */

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 13/35] vfio/pci: Initialize rom_read_failed in vfio_pci_load_rom()
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (11 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 12/35] vfio/pci: Fix information leak in vfio_rom_read() Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:06   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 14/35] vfio/pci: Grow buffer in vfio_pci_host_match() Andrey Drobyshev
                   ` (22 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

From: Mario Casquero <mcasquer@redhat.com>

When vfio_device_get_region_info() fails in vfio_pci_load_rom(),
the function returns without setting vdev->rom_read_failed to true,
and without allocating vdev->rom. This leaves vdev->rom as NULL.

andrey.drobyshev:
    This commit is cherry-picked as-is, with no modifications, from the
    upstream commit c822ecd80661.

Signed-off-by: Mario Casquero <mcasquer@redhat.com>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260602070857.356526-1-mcasquer@redhat.com
Signed-off-by: Cédric Le Goater <clg@redhat.com>
---
 hw/vfio/pci.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index fa2ca0d779d..bea478d521f 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -1039,7 +1039,7 @@ static void vfio_update_msi(VFIOPCIDevice *vdev)
     }
 }
 
-static void vfio_pci_load_rom(VFIOPCIDevice *vdev)
+static bool vfio_pci_load_rom(VFIOPCIDevice *vdev)
 {
     VFIODevice *vbasedev = &vdev->vbasedev;
     struct vfio_region_info *reg_info = NULL;
@@ -1053,7 +1053,7 @@ static void vfio_pci_load_rom(VFIOPCIDevice *vdev)
 
     if (ret != 0) {
         error_report("vfio: Error getting ROM info: %s", strerror(-ret));
-        return;
+        return false;
     }
 
     trace_vfio_pci_load_rom(vbasedev->name, (unsigned long)reg_info->size,
@@ -1064,12 +1064,11 @@ static void vfio_pci_load_rom(VFIOPCIDevice *vdev)
     vdev->rom_offset = reg_info->offset;
 
     if (!vdev->rom_size) {
-        vdev->rom_read_failed = true;
         error_report("vfio-pci: Cannot read device rom at %s", vbasedev->name);
         error_printf("Device option ROM contents are probably invalid "
                     "(check dmesg).\nSkip option ROM probe with rombar=0, "
                     "or load from file with romfile=\n");
-        return;
+        return false;
     }
 
     vdev->rom = g_malloc(size);
@@ -1125,6 +1124,8 @@ static void vfio_pci_load_rom(VFIOPCIDevice *vdev)
             data[6] = -csum;
         }
     }
+
+    return true;
 }
 
 /* "Raw" read of underlying config space. */
@@ -1158,7 +1159,7 @@ static uint64_t vfio_rom_read(void *opaque, hwaddr addr, unsigned size)
 
     /* Load the ROM lazily when the guest tries to read it */
     if (unlikely(!vdev->rom && !vdev->rom_read_failed)) {
-        vfio_pci_load_rom(vdev);
+        vdev->rom_read_failed = !vfio_pci_load_rom(vdev);
     }
 
     memcpy(&val, vdev->rom + addr,

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 14/35] vfio/pci: Grow buffer in vfio_pci_host_match()
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (12 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 13/35] vfio/pci: Initialize rom_read_failed in vfio_pci_load_rom() Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:06   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 15/35] q35: Fix migration of SMRAM state Andrey Drobyshev
                   ` (21 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>

Each field of PCIHostDeviceAddress is an unsigned int, therefore
while a valid address is limited to 13 characters, an invalid
address could exceed the specified format, up to:

        ffffffff:ffffffff:ffffffff.ffffffff<NUL>

This requires 36 characters with the terminator.

andrey.drobyshev:
    This commit is cherry-picked as-is, with no modifications, from the
    upstream commit 1785bf9685ec.

Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Reviewed-by: Alex Williamson <alex.williamson@nvidia.com>
Message-ID: <20260305-nvme-v4-2-b65b9de1839f@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
---
 hw/vfio/pci.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index bea478d521f..50498148275 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -2691,7 +2691,7 @@ void vfio_pci_post_reset(VFIOPCIDevice *vdev)
 
 bool vfio_pci_host_match(PCIHostDeviceAddress *addr, const char *name)
 {
-    char tmp[13];
+    char tmp[36];
 
     sprintf(tmp, "%04x:%02x:%02x.%1x", addr->domain,
             addr->bus, addr->slot, addr->function);

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 15/35] q35: Fix migration of SMRAM state
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (13 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 14/35] vfio/pci: Grow buffer in vfio_pci_host_match() Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:08   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 16/35] migration: ensure APIC is loaded prior to VFIO PCI devices Andrey Drobyshev
                   ` (20 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

From: Igor Mammedov <imammedo@redhat.com>

When migrating, dst QEMU by default has SMRAM unlocked,
and since wmask is not migrated, the migrated value of
MCH_HOST_BRIDGE_F_SMBASE in config space fall to prey of

  mch_update_smbase_smram()
    ...
    if (pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] == 0xff) {
        *reg = 0x00;

and is getting cleared and leads to unlocked smram
on dst even if on source it's been locked.

As Andrey has pointed out [1], we should derive wmask
from config and not other way around.

Drop offending chunk and resync wmask based on MCH_HOST_BRIDGE_F_SMBASE
register value. That would preserve the register during
migration and set smram regions into corresponding state.

What that changes is:
that it would let guest write junk values in register
(with no apparent effect) until it's stumbles upon
reserved 0x1 [|] 0x2 values, at which point it
would be only possible to lock register and trigger
switch to SMRAM blackhole in CPU AS.

While at it, fix up test by removing junk discard before negotiation hunk.

PS2:
Instead of adding a dedicated post_load handler for it,
reuse mch_update->mch_update_smbase_smram call chain
that is called on write/reset/post_load to be consistent
with how we handle mch registers.

PS3:
for prosterity here is erro message Andrey got due to this bug:
    qemu: vfio_container_dma_map(0x..., 0x0, 0xa0000, 0x....) = -22 (Invalid argument)
    qemu: hardware error: vfio: DMA mapping failed, unable to continue

1) https://patchew.org/QEMU/20251203180851.6390-1-arbn@yandex-team.com/

andrey.drobyshev:
    This commit is cherry-picked as-is, with no modifications, from the
    upstream commit 66cf169e29b0.

Fixes: f404220e279c ("q35: implement 128K SMRAM at default SMBASE address")
Reported-by: Andrey Ryabinin <arbn@yandex-team.com>
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Andrey Ryabinin <arbn@yandex-team.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-Id: <20251211165454.288476-1-imammedo@redhat.com>
---
 hw/pci-host/q35.c      | 25 +++++++++++--------------
 tests/qtest/q35-test.c |  6 ------
 2 files changed, 11 insertions(+), 20 deletions(-)

diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
index a708758d361..946342ba585 100644
--- a/hw/pci-host/q35.c
+++ b/hw/pci-host/q35.c
@@ -432,30 +432,27 @@ static void mch_update_smbase_smram(MCHPCIState *mch)
     }
 
     if (*reg == MCH_HOST_BRIDGE_F_SMBASE_QUERY) {
-        pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] =
-            MCH_HOST_BRIDGE_F_SMBASE_LCK;
+        pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] = MCH_HOST_BRIDGE_F_SMBASE_LCK;
         *reg = MCH_HOST_BRIDGE_F_SMBASE_IN_RAM;
         return;
     }
 
     /*
-     * default/reset state, discard written value
-     * which will disable SMRAM balackhole at SMBASE
+     * reg value can come from register write/reset/migration source,
+     * update wmask to be in sync with it regardless of source
      */
-    if (pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] == 0xff) {
-        *reg = 0x00;
+    if (*reg == MCH_HOST_BRIDGE_F_SMBASE_IN_RAM) {
+        pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] = MCH_HOST_BRIDGE_F_SMBASE_LCK;
+        return;
     }
-
-    memory_region_transaction_begin();
     if (*reg & MCH_HOST_BRIDGE_F_SMBASE_LCK) {
-        /* disable all writes */
-        pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] &=
-            ~MCH_HOST_BRIDGE_F_SMBASE_LCK;
+        /* lock register at 0x2 and disable all writes */
+        pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] = 0;
         *reg = MCH_HOST_BRIDGE_F_SMBASE_LCK;
-        lck = true;
-    } else {
-        lck = false;
     }
+
+    lck = *reg & MCH_HOST_BRIDGE_F_SMBASE_LCK;
+    memory_region_transaction_begin();
     memory_region_set_enabled(&mch->smbase_blackhole, lck);
     memory_region_set_enabled(&mch->smbase_window, lck);
     memory_region_transaction_commit();
diff --git a/tests/qtest/q35-test.c b/tests/qtest/q35-test.c
index 62fff49fc89..4e3a4457f62 100644
--- a/tests/qtest/q35-test.c
+++ b/tests/qtest/q35-test.c
@@ -206,12 +206,6 @@ static void test_smram_smbase_lock(void)
     qtest_writeb(qts, SMBASE, SMRAM_TEST_PATTERN);
     g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_PATTERN);
 
-    /* check that writing junk to 0x9c before before negotiating is ignored */
-    for (i = 0; i < 0xff; i++) {
-        qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, i);
-        g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0);
-    }
-
     /* enable SMRAM at SMBASE */
     qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, 0xff);
     g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0x01);

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 16/35] migration: ensure APIC is loaded prior to VFIO PCI devices
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (14 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 15/35] q35: Fix migration of SMRAM state Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:08   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 17/35] migration: Fix regression of passing error_fatal into vmstate_load_state() Andrey Drobyshev
                   ` (19 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

From: Yanfei Xu <yanfei.xu@bytedance.com>

The load procedure of VFIO PCI devices involves setting up IRT
for each VFIO PCI devices. This requires determining whether an
interrupt is single-destination interrupt to decide between
Posted Interrupt(PI) or remapping mode for the IRTE. However,
determining this may require accessing the VM's APIC registers.

For example:
ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irqs)
  ...
    kvm_arch_irq_bypass_add_producer
      kvm_x86_call(pi_update_irte)
        vmx_pi_update_irte
          kvm_intr_is_single_vcpu

If the LAPIC has not been loaded yet, interrupts will use remapping
mode. To prevent the fallback of interrupt mode, keep APIC is always
loaded prior to VFIO PCI devices.

andrey.drobyshev:
    This commit is cherry-picked as-is, with no modifications, from the
    upstream commit d943cef76090.

Signed-off-by: Yicong Shen <shenyicong.1023@bytedance.com>
Signed-off-by: Yanfei Xu <yanfei.xu@bytedance.com>
Reviewed-by: Peter Xu <peterx@redhat.com>
Link: https://lore.kernel.org/r/20250818131127.1021648-1-yanfei.xu@bytedance.com
Signed-off-by: Peter Xu <peterx@redhat.com>
---
 hw/intc/apic_common.c       | 1 +
 include/migration/vmstate.h | 1 +
 2 files changed, 2 insertions(+)

diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c
index 37a7a7019d3..394fe020134 100644
--- a/hw/intc/apic_common.c
+++ b/hw/intc/apic_common.c
@@ -379,6 +379,7 @@ static const VMStateDescription vmstate_apic_common = {
     .pre_load = apic_pre_load,
     .pre_save = apic_dispatch_pre_save,
     .post_load = apic_dispatch_post_load,
+    .priority = MIG_PRI_APIC,
     .fields = (const VMStateField[]) {
         VMSTATE_UINT32(apicbase, APICCommonState),
         VMSTATE_UINT8(id, APICCommonState),
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 6839117be87..c3d156c514f 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -163,6 +163,7 @@ typedef enum {
     MIG_PRI_IOMMU,              /* Must happen before PCI devices */
     MIG_PRI_PCI_BUS,            /* Must happen before IOMMU */
     MIG_PRI_VIRTIO_MEM,         /* Must happen before IOMMU */
+    MIG_PRI_APIC,               /* Must happen before PCI devices */
     MIG_PRI_GICV3_ITS,          /* Must happen before PCI devices */
     MIG_PRI_GICV3,              /* Must happen before the ITS */
     MIG_PRI_MAX,

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 17/35] migration: Fix regression of passing error_fatal into vmstate_load_state()
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (15 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 16/35] migration: ensure APIC is loaded prior to VFIO PCI devices Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:11   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 18/35] migration: vmstate_save_state_v: fix double error_setg Andrey Drobyshev
                   ` (18 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

From: Arun Menon <armenon@redhat.com>

error_fatal is passed to vmstate_load_state() and vmstate_save_state()
functions. This was introduced in commit c632ffbd74. This would exit(1)
on error, and therefore does not allow to propagate the error back to
the caller.

To maintain consistency with prior error handling i.e. either propagating
the error to the caller or reporting it, we must set the error within a
local Error object instead of using error_fatal.

Reviewed-by: Cornelia Huck <cohuck@redhat.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Link: https://lore.kernel.org/r/20251028-solve_error_fatal_regression-v2-1-dab24c808a28@redhat.com
[peterx: always uninit var ret, per Akihiko]
[peterx: touchups on line ordering, spacings etc.]

andrey.drobyshev:
    This commit was cherry-picked from the upstream commit 986c3292c6b8,
    adapted for the hci-dev base, which predates the upstream commit
    bcb536cabe10 ("error: Kill @error_warn"): virtio_gpu_load() has no
    'err' local there yet, so it is declared here.

Signed-off-by: Peter Xu <peterx@redhat.com>
---
 hw/display/virtio-gpu.c | 22 +++++++++++++++-------
 hw/pci/pci.c            | 15 +++++++++++++--
 hw/s390x/virtio-ccw.c   | 17 +++++++++++++++--
 hw/scsi/spapr_vscsi.c   | 10 ++++++++--
 hw/virtio/virtio-mmio.c | 15 +++++++++++++--
 hw/virtio/virtio-pci.c  | 15 +++++++++++++--
 hw/virtio/virtio.c      | 10 +++++++---
 7 files changed, 84 insertions(+), 20 deletions(-)

diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 477ec700a13..4bc8c778508 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -1223,7 +1223,8 @@ static int virtio_gpu_save(QEMUFile *f, void *opaque, size_t size,
 {
     VirtIOGPU *g = opaque;
     struct virtio_gpu_simple_resource *res;
-    int i;
+    Error *err = NULL;
+    int i, ret;
 
     /* in 2d mode we should never find unprocessed commands here */
     assert(QTAILQ_EMPTY(&g->cmdq));
@@ -1246,8 +1247,12 @@ static int virtio_gpu_save(QEMUFile *f, void *opaque, size_t size,
     }
     qemu_put_be32(f, 0); /* end of list */
 
-    return vmstate_save_state(f, &vmstate_virtio_gpu_scanouts, g, NULL,
-                              &error_fatal);
+    ret = vmstate_save_state(f, &vmstate_virtio_gpu_scanouts, g, NULL,
+                             &err);
+    if (ret < 0) {
+        error_report_err(err);
+    }
+    return ret;
 }
 
 static bool virtio_gpu_load_restore_mapping(VirtIOGPU *g,
@@ -1283,9 +1288,10 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size,
                            const VMStateField *field)
 {
     VirtIOGPU *g = opaque;
+    Error *err = NULL;
     struct virtio_gpu_simple_resource *res;
     uint32_t resource_id, pformat;
-    int i;
+    int i, ret;
 
     g->hostmem = 0;
 
@@ -1344,9 +1350,11 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size,
     }
 
     /* load & apply scanout state */
-    vmstate_load_state(f, &vmstate_virtio_gpu_scanouts, g, 1, &error_fatal);
-
-    return 0;
+    ret = vmstate_load_state(f, &vmstate_virtio_gpu_scanouts, g, 1, &err);
+    if (ret < 0) {
+        error_report_err(err);
+    }
+    return ret;
 }
 
 static int virtio_gpu_blob_save(QEMUFile *f, void *opaque, size_t size,
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 5d7d17f6529..50f062045a9 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -926,21 +926,32 @@ const VMStateDescription vmstate_pci_device = {
 
 void pci_device_save(PCIDevice *s, QEMUFile *f)
 {
+    Error *local_err = NULL;
+    int ret;
+
     /* Clear interrupt status bit: it is implicit
      * in irq_state which we are saving.
      * This makes us compatible with old devices
      * which never set or clear this bit. */
     s->config[PCI_STATUS] &= ~PCI_STATUS_INTERRUPT;
-    vmstate_save_state(f, &vmstate_pci_device, s, NULL, &error_fatal);
+    ret = vmstate_save_state(f, &vmstate_pci_device, s, NULL, &local_err);
+    if (ret < 0) {
+        error_report_err(local_err);
+    }
     /* Restore the interrupt status bit. */
     pci_update_irq_status(s);
 }
 
 int pci_device_load(PCIDevice *s, QEMUFile *f)
 {
+    Error *local_err = NULL;
     int ret;
+
     ret = vmstate_load_state(f, &vmstate_pci_device, s, s->version_id,
-                             &error_fatal);
+                             &local_err);
+    if (ret < 0) {
+        error_report_err(local_err);
+    }
     /* Restore the interrupt status bit. */
     pci_update_irq_status(s);
     return ret;
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
index 4cb1ced001a..4a3ffb84f8f 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -1130,13 +1130,26 @@ static int virtio_ccw_load_queue(DeviceState *d, int n, QEMUFile *f)
 static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f)
 {
     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
-    vmstate_save_state(f, &vmstate_virtio_ccw_dev, dev, NULL, &error_fatal);
+    Error *local_err = NULL;
+    int ret;
+
+    ret = vmstate_save_state(f, &vmstate_virtio_ccw_dev, dev, NULL, &local_err);
+    if (ret < 0) {
+        error_report_err(local_err);
+    }
 }
 
 static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
 {
     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
-    return vmstate_load_state(f, &vmstate_virtio_ccw_dev, dev, 1, &error_fatal);
+    Error *local_err = NULL;
+    int ret;
+
+    ret = vmstate_load_state(f, &vmstate_virtio_ccw_dev, dev, 1, &local_err);
+    if (ret < 0) {
+        error_report_err(local_err);
+    }
+    return ret;
 }
 
 static void virtio_ccw_pre_plugged(DeviceState *d, Error **errp)
diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c
index f0a7dd2b882..a6591319db7 100644
--- a/hw/scsi/spapr_vscsi.c
+++ b/hw/scsi/spapr_vscsi.c
@@ -628,10 +628,16 @@ static const VMStateDescription vmstate_spapr_vscsi_req = {
 static void vscsi_save_request(QEMUFile *f, SCSIRequest *sreq)
 {
     vscsi_req *req = sreq->hba_private;
-    assert(req->active);
+    Error *local_err = NULL;
+    int rc;
 
-    vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL, &error_fatal);
+    assert(req->active);
 
+    rc = vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL, &local_err);
+    if (rc < 0) {
+        error_report_err(local_err);
+        return;
+    }
     trace_spapr_vscsi_save_request(req->qtag, req->cur_desc_num,
                                    req->cur_desc_offset);
 }
diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c
index fb58c364527..c05c00bcd4a 100644
--- a/hw/virtio/virtio-mmio.c
+++ b/hw/virtio/virtio-mmio.c
@@ -612,15 +612,26 @@ static const VMStateDescription vmstate_virtio_mmio = {
 static void virtio_mmio_save_extra_state(DeviceState *opaque, QEMUFile *f)
 {
     VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
+    Error *local_err = NULL;
+    int ret;
 
-    vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL, &error_fatal);
+    ret = vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL, &local_err);
+    if (ret < 0) {
+        error_report_err(local_err);
+    }
 }
 
 static int virtio_mmio_load_extra_state(DeviceState *opaque, QEMUFile *f)
 {
     VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
+    Error *local_err = NULL;
+    int ret;
 
-    return vmstate_load_state(f, &vmstate_virtio_mmio, proxy, 1, &error_fatal);
+    ret = vmstate_load_state(f, &vmstate_virtio_mmio, proxy, 1, &local_err);
+    if (ret < 0) {
+        error_report_err(local_err);
+    }
+    return ret;
 }
 
 static bool virtio_mmio_has_extra_state(DeviceState *opaque)
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 7273da4c018..c276fbb017c 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -189,15 +189,26 @@ static bool virtio_pci_has_extra_state(DeviceState *d)
 static void virtio_pci_save_extra_state(DeviceState *d, QEMUFile *f)
 {
     VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+    Error *local_err = NULL;
+    int ret;
 
-    vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL, &error_fatal);
+    ret = vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL, &local_err);
+    if (ret < 0) {
+        error_report_err(local_err);
+    }
 }
 
 static int virtio_pci_load_extra_state(DeviceState *d, QEMUFile *f)
 {
     VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+    Error *local_err = NULL;
+    int ret;
 
-    return vmstate_load_state(f, &vmstate_virtio_pci, proxy, 1, &error_fatal);
+    ret = vmstate_load_state(f, &vmstate_virtio_pci, proxy, 1, &local_err);
+    if (ret < 0) {
+        error_report_err(local_err);
+    }
+    return ret;
 }
 
 static void virtio_pci_save_queue(DeviceState *d, int n, QEMUFile *f)
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index a7063d5555e..d4b2e67c274 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -3016,7 +3016,7 @@ int virtio_save(VirtIODevice *vdev, QEMUFile *f)
     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
     VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
     uint32_t guest_features_lo = (vdev->guest_features & 0xffffffff);
-    int i;
+    int i, ret;
     Error *local_err = NULL;
 
     if (k->save_config) {
@@ -3061,7 +3061,7 @@ int virtio_save(VirtIODevice *vdev, QEMUFile *f)
     }
 
     if (vdc->vmsd) {
-        int ret = vmstate_save_state(f, vdc->vmsd, vdev, NULL, &local_err);
+        ret = vmstate_save_state(f, vdc->vmsd, vdev, NULL, &local_err);
         if (ret) {
             error_report_err(local_err);
             return ret;
@@ -3069,7 +3069,11 @@ int virtio_save(VirtIODevice *vdev, QEMUFile *f)
     }
 
     /* Subsections */
-    return vmstate_save_state(f, &vmstate_virtio, vdev, NULL, &error_fatal);
+    ret = vmstate_save_state(f, &vmstate_virtio, vdev, NULL, &local_err);
+    if (ret < 0) {
+        error_report_err(local_err);
+    }
+    return ret;
 }
 
 /* A wrapper for use as a VMState .put function */

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 18/35] migration: vmstate_save_state_v: fix double error_setg
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (16 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 17/35] migration: Fix regression of passing error_fatal into vmstate_load_state() Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:11   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 19/35] migration: yank: Move register instance earlier Andrey Drobyshev
                   ` (17 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

From: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>

We may call error_setg twice on same errp if inner
vmstate_save_state_v() or vmstate_save_state() call fails. Next we will
crash on assertion in error_setv().

andrey.drobyshev:
    This commit is cherry-picked as-is, with no modifications, from the
    upstream commit d41ce10d0f5a.

Fixes: 848a0503422d043 "migration: Update error description outside migration.c"
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
Reviewed-by: Peter Xu <peterx@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260304212303.667141-2-vsementsov@yandex-team.ru
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/vmstate.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/migration/vmstate.c b/migration/vmstate.c
index aea41c09946..04903131257 100644
--- a/migration/vmstate.c
+++ b/migration/vmstate.c
@@ -523,6 +523,9 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
                 } else {
                     ret = inner_field->info->put(f, curr_elem, size,
                                                  inner_field, vmdesc_loop);
+                    if (ret < 0) {
+                        error_setg(errp, "put failed");
+                    }
                 }
 
                 written_bytes = qemu_file_transferred(f) - old_offset;
@@ -535,8 +538,8 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
                 }
 
                 if (ret) {
-                    error_setg(errp, "Save of field %s/%s failed",
-                                vmsd->name, field->name);
+                    error_prepend(errp, "Save of field %s/%s failed: ",
+                                  vmsd->name, field->name);
                     if (vmsd->post_save) {
                         vmsd->post_save(opaque);
                     }

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 19/35] migration: yank: Move register instance earlier
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (17 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 18/35] migration: vmstate_save_state_v: fix double error_setg Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:16   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 20/35] migration: Free cpr-transfer MigrationAddress along with gsource Andrey Drobyshev
                   ` (16 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

From: Fabiano Rosas <farosas@suse.de>

Move the register_instance call to migrate_prepare() so it can be
paired with the unregister_instance at migration_cleanup(). Otherwise,
the cleanup cannot be run when cpr_state_save() fails because the
instance is registered only after it.

When resuming from a paused postcopy migration, migrate_prepare()
returns early, but migration_cleanup() doesn't run, so the yank will
remain paired.

andrey.drobyshev:
    This commit was cherry-picked from the upstream commit 7e714392437c,
    adapted for the hci-dev base, which predates the upstream
    migration_connect_set_error() -> migration_connect_error_propagate()
    rename (77c82edadbe0): the error propagation calls in qmp_migrate()
    and qmp_migrate_finish() keep the older spelling.

    Also fixed here: upstream left the yank instance registered when
    qmp_migrate_finish() fails before connecting (nothing calls
    migration_cleanup() then), so the next migrate command aborted on
    yank_register_instance(&error_abort); and the unregister at the out:
    label ran on a resume, which never registered the instance.  Upstream
    fixed both in 6b587af5ec9d ("migration: Fold migration_cleanup() into
    migration_connect_error_propagate()"), which needs the above rework;
    this is the equivalent for our base.

    Also register the instance with errp, before migrate_init(), instead
    of with &error_abort: a duplicate instance, e.g. a previous migration
    still being cleaned up, then fails the migrate command instead of
    aborting QEMU.  And refuse migrate while an incoming postcopy
    migration is still in progress: the guest already runs then, but the
    incoming side still holds the yank instance.

Reviewed-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Prasad Pandit <pjp@fedoraproject.org>
Link: https://lore.kernel.org/qemu-devel/20260123141656.6765-11-farosas@suse.de
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/migration.c | 43 +++++++++++++++++++++++++++----------------
 1 file changed, 27 insertions(+), 16 deletions(-)

diff --git a/migration/migration.c b/migration/migration.c
index d5a21da3129..cc88f70ae59 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -2186,6 +2186,15 @@ static bool migrate_prepare(MigrationState *s, bool resume, Error **errp)
         return false;
     }
 
+    /*
+     * The guest already runs during incoming postcopy, but the incoming
+     * migration still holds the yank instance registered below.
+     */
+    if (migration_in_incoming_postcopy()) {
+        error_setg(errp, "Incoming postcopy migration is still in progress");
+        return false;
+    }
+
     if (runstate_check(RUN_STATE_POSTMIGRATE)) {
         error_setg(errp, "Can't migrate the vm that was paused due to "
                    "previous migration");
@@ -2243,15 +2252,19 @@ static bool migrate_prepare(MigrationState *s, bool resume, Error **errp)
         }
     }
 
+    if (!yank_register_instance(MIGRATION_YANK_INSTANCE, errp)) {
+        return false;
+    }
+
     if (migrate_init(s, errp)) {
+        yank_unregister_instance(MIGRATION_YANK_INSTANCE);
         return false;
     }
 
     return true;
 }
 
-static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested,
-                               Error **errp);
+static void qmp_migrate_finish(MigrationAddress *addr, Error **errp);
 
 static void migrate_hup_add(MigrationState *s, QIOChannel *ioc, GSourceFunc cb,
                             void *opaque)
@@ -2276,7 +2289,7 @@ static gboolean qmp_migrate_finish_cb(QIOChannel *channel,
 {
     MigrationAddress *addr = opaque;
 
-    qmp_migrate_finish(addr, false, NULL);
+    qmp_migrate_finish(addr, NULL);
 
     cpr_state_close();
     migrate_hup_delete(migrate_get_current());
@@ -2288,7 +2301,6 @@ void qmp_migrate(const char *uri, bool has_channels,
                  MigrationChannelList *channels, bool has_detach, bool detach,
                  bool has_resume, bool resume, Error **errp)
 {
-    bool resume_requested;
     Error *local_err = NULL;
     MigrationState *s = migrate_get_current();
     g_autoptr(MigrationChannel) channel = NULL;
@@ -2341,8 +2353,7 @@ void qmp_migrate(const char *uri, bool has_channels,
         return;
     }
 
-    resume_requested = has_resume && resume;
-    if (!migrate_prepare(s, resume_requested, errp)) {
+    if (!migrate_prepare(s, has_resume && resume, errp)) {
         /* Error detected, put into errp */
         return;
     }
@@ -2366,27 +2377,26 @@ void qmp_migrate(const char *uri, bool has_channels,
                         QAPI_CLONE(MigrationAddress, addr));
 
     } else {
-        qmp_migrate_finish(addr, resume_requested, errp);
+        qmp_migrate_finish(addr, errp);
     }
 
 out:
     if (local_err) {
+        /* A resume didn't register it, see migrate_prepare() */
+        if (s->state != MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP) {
+            yank_unregister_instance(MIGRATION_YANK_INSTANCE);
+        }
         migration_connect_set_error(s, local_err);
         error_propagate(errp, local_err);
     }
 }
 
-static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested,
-                               Error **errp)
+static void qmp_migrate_finish(MigrationAddress *addr, Error **errp)
 {
     MigrationState *s = migrate_get_current();
     Error *local_err = NULL;
-
-    if (!resume_requested) {
-        if (!yank_register_instance(MIGRATION_YANK_INSTANCE, errp)) {
-            return;
-        }
-    }
+    /* Before the start functions below change the state */
+    bool resume = s->state == MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP;
 
     if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) {
         SocketAddress *saddr = &addr->u.socket;
@@ -2413,7 +2423,8 @@ static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested,
     }
 
     if (local_err) {
-        if (!resume_requested) {
+        /* Nothing connected: migration_cleanup() won't unregister it */
+        if (!resume) {
             yank_unregister_instance(MIGRATION_YANK_INSTANCE);
         }
         cpr_state_close();

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 20/35] migration: Free cpr-transfer MigrationAddress along with gsource
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (18 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 19/35] migration: yank: Move register instance earlier Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:14   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 21/35] migration: Move CPR HUP watch to cpr-transfer.c Andrey Drobyshev
                   ` (15 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

From: Fabiano Rosas <farosas@suse.de>

When setting a callback on a Glib source and giving it a data pointer,
it's natural to also provide the destructor for the data in question.

Since migrate_hup_add() already needs to clone the MigrationAddress
when setting the qmp_migrate_finish_cb callback, also pass the
qapi_free_MigrationAddress as the GDestroyNotify callback.

With this the address doesn't need to be freed at the callback body,
making the management of that memory slightly simpler.

andrey.drobyshev:
    This commit was cherry-picked from the upstream commit 6a887119ce18,
    adapted for the hci-dev base: qmp_migrate_finish_cb() here still
    closes the cpr state and deletes the HUP source itself (upstream had
    already moved that into migration_cleanup()), so only the explicit
    qapi_free_MigrationAddress() call goes away from it.

Cc: Mark Kanda <mark.kanda@oracle.com>
Cc: Ben Chaney <bchaney@akamai.com>
Reviewed-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Prasad Pandit <pjp@fedoraproject.org>
Link: https://lore.kernel.org/qemu-devel/20260123141656.6765-23-farosas@suse.de
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 migration/migration.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/migration/migration.c b/migration/migration.c
index cc88f70ae59..b365e413a84 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -2269,9 +2269,11 @@ static void qmp_migrate_finish(MigrationAddress *addr, Error **errp);
 static void migrate_hup_add(MigrationState *s, QIOChannel *ioc, GSourceFunc cb,
                             void *opaque)
 {
-        s->hup_source = qio_channel_create_watch(ioc, G_IO_HUP);
-        g_source_set_callback(s->hup_source, cb, opaque, NULL);
-        g_source_attach(s->hup_source, NULL);
+    s->hup_source = qio_channel_create_watch(ioc, G_IO_HUP);
+    g_source_set_callback(s->hup_source, cb,
+                          QAPI_CLONE(MigrationAddress, opaque),
+                          (GDestroyNotify)qapi_free_MigrationAddress);
+    g_source_attach(s->hup_source, NULL);
 }
 
 static void migrate_hup_delete(MigrationState *s)
@@ -2293,7 +2295,6 @@ static gboolean qmp_migrate_finish_cb(QIOChannel *channel,
 
     cpr_state_close();
     migrate_hup_delete(migrate_get_current());
-    qapi_free_MigrationAddress(addr);
     return G_SOURCE_REMOVE;
 }
 
@@ -2374,7 +2375,7 @@ void qmp_migrate(const char *uri, bool has_channels,
      */
     if (migrate_mode() == MIG_MODE_CPR_TRANSFER) {
         migrate_hup_add(s, cpr_state_ioc(), (GSourceFunc)qmp_migrate_finish_cb,
-                        QAPI_CLONE(MigrationAddress, addr));
+                        addr);
 
     } else {
         qmp_migrate_finish(addr, errp);

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 21/35] migration: Move CPR HUP watch to cpr-transfer.c
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (19 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 20/35] migration: Free cpr-transfer MigrationAddress along with gsource Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:14   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 22/35] migration: Fix crash on second migration when cancel early Andrey Drobyshev
                   ` (14 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

From: Fabiano Rosas <farosas@suse.de>

Move this CPR-specific code into a cpr file. While here, give the
functions more significant names.

This makes the new idea (after cpr-transfer) of having two parts to
qmp_migrate slightly more obvious: either wait for the hangup or
continue directly.

andrey.drobyshev:
    This commit was cherry-picked from the upstream commit f523e5aa5f56,
    adapted for the hci-dev base, which predates the upstream qmp_migrate()
    rework around MigrationChannel: the watch is still armed with the
    plain MigrationAddress from qmp_migrate() rather than main_ch->addr,
    and qmp_migrate_finish_cb() keeps closing the cpr state and destroying
    the HUP source itself.

Reviewed-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Prasad Pandit <pjp@fedoraproject.org>
Link: https://lore.kernel.org/qemu-devel/20260123141656.6765-24-farosas@suse.de
Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
 include/migration/cpr.h  |  5 +++++
 migration/cpr-transfer.c | 23 +++++++++++++++++++++++
 migration/migration.c    | 29 ++++-------------------------
 3 files changed, 32 insertions(+), 25 deletions(-)

diff --git a/include/migration/cpr.h b/include/migration/cpr.h
index 8fb79eb0a28..ae441322ff5 100644
--- a/include/migration/cpr.h
+++ b/include/migration/cpr.h
@@ -9,6 +9,7 @@
 #define MIGRATION_CPR_H
 
 #include "qapi/qapi-types-migration.h"
+#include "io/channel.h"
 #include "qemu/queue.h"
 
 #define MIG_MODE_NONE           -1
@@ -76,6 +77,10 @@ int cpr_get_fd_param(const char *name, const char *fdname, int index, bool cpr,
 QEMUFile *cpr_transfer_output(MigrationChannel *channel, Error **errp);
 QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp);
 
+void cpr_transfer_add_hup_watch(MigrationState *s, QIOChannelFunc func,
+                                void *opaque);
+void cpr_transfer_source_destroy(MigrationState *s);
+
 void cpr_exec_init(void);
 QEMUFile *cpr_exec_output(Error **errp);
 QEMUFile *cpr_exec_input(Error **errp);
diff --git a/migration/cpr-transfer.c b/migration/cpr-transfer.c
index 00371d17c3a..61d5c9dce2b 100644
--- a/migration/cpr-transfer.c
+++ b/migration/cpr-transfer.c
@@ -6,7 +6,10 @@
  */
 
 #include "qemu/osdep.h"
+#include "qapi/clone-visitor.h"
 #include "qapi/error.h"
+#include "qapi/qapi-visit-migration.h"
+#include "io/channel.h"
 #include "io/channel-file.h"
 #include "io/channel-socket.h"
 #include "io/net-listener.h"
@@ -72,3 +75,23 @@ QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp)
         return NULL;
     }
 }
+
+void cpr_transfer_add_hup_watch(MigrationState *s, QIOChannelFunc func,
+                                void *opaque)
+{
+    s->hup_source = qio_channel_create_watch(cpr_state_ioc(), G_IO_HUP);
+    g_source_set_callback(s->hup_source,
+                          (GSourceFunc)func,
+                          QAPI_CLONE(MigrationAddress, opaque),
+                          (GDestroyNotify)qapi_free_MigrationAddress);
+    g_source_attach(s->hup_source, NULL);
+}
+
+void cpr_transfer_source_destroy(MigrationState *s)
+{
+    if (s->hup_source) {
+        g_source_destroy(s->hup_source);
+        g_source_unref(s->hup_source);
+        s->hup_source = NULL;
+    }
+}
diff --git a/migration/migration.c b/migration/migration.c
index b365e413a84..d892655a21e 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -110,7 +110,6 @@ static bool migration_object_check(MigrationState *ms, Error **errp);
 static bool migration_switchover_start(MigrationState *s, Error **errp);
 static bool close_return_path_on_source(MigrationState *s);
 static void migration_completion_end(MigrationState *s);
-static void migrate_hup_delete(MigrationState *s);
 
 static void migration_downtime_start(MigrationState *s)
 {
@@ -1508,7 +1507,7 @@ static void migration_cleanup(MigrationState *s)
 
     qemu_savevm_state_cleanup();
     cpr_state_close();
-    migrate_hup_delete(s);
+    cpr_transfer_source_destroy(s);
 
     close_return_path_on_source(s);
 
@@ -1669,7 +1668,7 @@ void migration_cancel(void)
         migrate_set_state(&s->state, MIGRATION_STATUS_CANCELLING,
                           MIGRATION_STATUS_CANCELLED);
         cpr_state_close();
-        migrate_hup_delete(s);
+        cpr_transfer_source_destroy(s);
     }
 }
 
@@ -2266,25 +2265,6 @@ static bool migrate_prepare(MigrationState *s, bool resume, Error **errp)
 
 static void qmp_migrate_finish(MigrationAddress *addr, Error **errp);
 
-static void migrate_hup_add(MigrationState *s, QIOChannel *ioc, GSourceFunc cb,
-                            void *opaque)
-{
-    s->hup_source = qio_channel_create_watch(ioc, G_IO_HUP);
-    g_source_set_callback(s->hup_source, cb,
-                          QAPI_CLONE(MigrationAddress, opaque),
-                          (GDestroyNotify)qapi_free_MigrationAddress);
-    g_source_attach(s->hup_source, NULL);
-}
-
-static void migrate_hup_delete(MigrationState *s)
-{
-    if (s->hup_source) {
-        g_source_destroy(s->hup_source);
-        g_source_unref(s->hup_source);
-        s->hup_source = NULL;
-    }
-}
-
 static gboolean qmp_migrate_finish_cb(QIOChannel *channel,
                                       GIOCondition cond,
                                       void *opaque)
@@ -2294,7 +2274,7 @@ static gboolean qmp_migrate_finish_cb(QIOChannel *channel,
     qmp_migrate_finish(addr, NULL);
 
     cpr_state_close();
-    migrate_hup_delete(migrate_get_current());
+    cpr_transfer_source_destroy(migrate_get_current());
     return G_SOURCE_REMOVE;
 }
 
@@ -2374,8 +2354,7 @@ void qmp_migrate(const char *uri, bool has_channels,
      * connection, so qmp_migrate_finish will fail to connect, and then recover.
      */
     if (migrate_mode() == MIG_MODE_CPR_TRANSFER) {
-        migrate_hup_add(s, cpr_state_ioc(), (GSourceFunc)qmp_migrate_finish_cb,
-                        addr);
+        cpr_transfer_add_hup_watch(s, qmp_migrate_finish_cb, addr);
 
     } else {
         qmp_migrate_finish(addr, errp);

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 22/35] migration: Fix crash on second migration when cancel early
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (20 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 21/35] migration: Move CPR HUP watch to cpr-transfer.c Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:16   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 23/35] kvm: tolerate ioctls after kvm_close() Andrey Drobyshev
                   ` (13 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

From: Peter Xu <peterx@redhat.com>

Marc-André reported an issue on QEMU crash when retrying a cancelled
migration during early setup phase, see "Link:" for more information, and
also easy way to reproduce.

This patch is a replacement of the prior fix proposed by not only switching
to migration_cleanup(), but also fixing it from CPR side, so that we track
hup_source properly to know if src QEMU is waiting or the HUP signal.

To put it simple: this chunk of special casing in migration_cancel() should
not affect normal migration, but only cpr-transfer migration to cover the
small window when the src QEMU is waiting for a HUP signal on cpr
channel (so that src QEMU can continue the migration on the main channel).

To achieve that, we'll also need to remember to detach the hup_source
whenenver invoked: after that point, we should always be able to cleanup
the migration.

It's not a generic operation to explicitly detach a gsource from its
context while in its dispatch() function.  But it should be safe, because
gsource disptch() will only happen with a boosted refcount for the
dispatcher so that the gsource will not be freed until the callback
completes. It's also safe to return G_SOURCE_REMOVE after the gsource is
detached, as glib will simply ignore the G_SOURCE_REMOVE.

One can refer to latest 2.86.5 glib code in g_main_dispatch() for that:

https://github.com/GNOME/glib/blob/2.86.5/glib/gmain.c#L3592

When at this, add a bunch of assertions to make sure nothing surprises us.

After this patch applied, the 2nd migration will not crash QEMU, instead
it'll be in CANCELLING until the socket connection times out (it will take
~2min on my Fedora default kernel).  During this process no 2nd migration
will be allowed, and after it timed out migration can be restarted.

It's because so far we don't have control over socket_connect_outgoing(),
or anything yet managed by a task executed in qio_task_run_in_thread().
Speeding up the cancellation to be left for future.

I also tested cpr-transfer by only providing cpr channel not the main
channel (with -incoming defer), kickoff migration on source, then cancel it
on source directly without providing the main channel.  It keeps working.

I wanted to add an unit test for that but it'll need to refactor current
cpr-transfer tests first; let's leave it for later.

andrey.drobyshev:
    This commit was cherry-picked from the upstream commit 4f08b870f8f0,
    adapted for the hci-dev base, which predates the upstream qmp_migrate()
    rework: the HUP callback is still qmp_migrate_finish_cb() rather than
    migration_connect_outgoing_cb(), so the early
    cpr_transfer_source_destroy() call lands there, ahead of
    qmp_migrate_finish() and the cpr_state_close() the callback keeps
    doing itself.

Link: https://lore.kernel.org/r/20260417184742.293061-1-marcandre.lureau@redhat.com
Reported-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Tested-by: Fabiano Rosas <farosas@suse.de>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
Link: https://lore.kernel.org/r/20260421175820.302795-1-peterx@redhat.com
Signed-off-by: Peter Xu <peterx@redhat.com>
---
 include/migration/cpr.h  |  1 +
 migration/cpr-transfer.c | 10 ++++++++++
 migration/migration.c    | 32 ++++++++++++++++++++++++--------
 migration/migration.h    |  5 +++++
 4 files changed, 40 insertions(+), 8 deletions(-)

diff --git a/include/migration/cpr.h b/include/migration/cpr.h
index ae441322ff5..4dd397a7c13 100644
--- a/include/migration/cpr.h
+++ b/include/migration/cpr.h
@@ -80,6 +80,7 @@ QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp);
 void cpr_transfer_add_hup_watch(MigrationState *s, QIOChannelFunc func,
                                 void *opaque);
 void cpr_transfer_source_destroy(MigrationState *s);
+bool cpr_transfer_source_active(MigrationState *s);
 
 void cpr_exec_init(void);
 QEMUFile *cpr_exec_output(Error **errp);
diff --git a/migration/cpr-transfer.c b/migration/cpr-transfer.c
index 61d5c9dce2b..9defe7bad78 100644
--- a/migration/cpr-transfer.c
+++ b/migration/cpr-transfer.c
@@ -6,6 +6,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/main-loop.h"
 #include "qapi/clone-visitor.h"
 #include "qapi/error.h"
 #include "qapi/qapi-visit-migration.h"
@@ -79,6 +80,7 @@ QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp)
 void cpr_transfer_add_hup_watch(MigrationState *s, QIOChannelFunc func,
                                 void *opaque)
 {
+    assert(bql_locked());
     s->hup_source = qio_channel_create_watch(cpr_state_ioc(), G_IO_HUP);
     g_source_set_callback(s->hup_source,
                           (GSourceFunc)func,
@@ -89,9 +91,17 @@ void cpr_transfer_add_hup_watch(MigrationState *s, QIOChannelFunc func,
 
 void cpr_transfer_source_destroy(MigrationState *s)
 {
+    assert(bql_locked());
     if (s->hup_source) {
         g_source_destroy(s->hup_source);
         g_source_unref(s->hup_source);
         s->hup_source = NULL;
     }
 }
+
+bool cpr_transfer_source_active(MigrationState *s)
+{
+    /* Whenever the HUP gsource is available, it's active. */
+    assert(bql_locked());
+    return s->hup_source;
+}
diff --git a/migration/migration.c b/migration/migration.c
index d892655a21e..821764afae9 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -1661,14 +1661,19 @@ void migration_cancel(void)
     }
 
     /*
-     * If qmp_migrate_finish has not been called, then there is no path that
-     * will complete the cancellation.  Do it now.
+     * This is cpr-transfer specific processing.
+     *
+     * If this is true, it means cpr-transfer migration is waiting for the
+     * destination to send HUP event on CPR channel to continue the next
+     * phase.  If so, do the cleanup proactively to avoid get stuck in
+     * CANCELLING state.
      */
-    if (setup && !s->to_dst_file) {
-        migrate_set_state(&s->state, MIGRATION_STATUS_CANCELLING,
-                          MIGRATION_STATUS_CANCELLED);
-        cpr_state_close();
-        cpr_transfer_source_destroy(s);
+    if (cpr_transfer_source_active(s)) {
+        assert(migrate_mode() == MIG_MODE_CPR_TRANSFER);
+        assert(setup && !s->to_dst_file);
+        migration_cleanup(s);
+        /* Now all things should have been released */
+        assert(!cpr_transfer_source_active(s));
     }
 }
 
@@ -2270,11 +2275,22 @@ static gboolean qmp_migrate_finish_cb(QIOChannel *channel,
                                       void *opaque)
 {
     MigrationAddress *addr = opaque;
+    MigrationState *s = migrate_get_current();
+
+    /*
+     * Detach and release the GSource right after use.  We rely on this to
+     * detect this small cpr-transfer window of "waiting for HUP event".
+     */
+    cpr_transfer_source_destroy(s);
 
     qmp_migrate_finish(addr, NULL);
 
     cpr_state_close();
-    cpr_transfer_source_destroy(migrate_get_current());
+
+    /*
+     * This is redundant as we do cpr_transfer_source_destroy() at the
+     * entry, but it's benign; glib will just skip the detach.
+     */
     return G_SOURCE_REMOVE;
 }
 
diff --git a/migration/migration.h b/migration/migration.h
index a7ea43e925d..ee15ca88461 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -509,6 +509,11 @@ struct MigrationState {
     /* Is this a rdma migration */
     bool rdma_migration;
 
+    /*
+     * When set, it means cpr-transfer is waiting for the HUP signal from
+     * destination to continue the 2nd step of migration via the main
+     * channel.
+     */
     GSource *hup_source;
 };
 

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 23/35] kvm: tolerate ioctls after kvm_close()
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (21 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 22/35] migration: Fix crash on second migration when cancel early Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:21   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 24/35] kvm: fix coalesced MMIO flush racing with kvm_close() Andrey Drobyshev
                   ` (12 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

Since commit 7ed0919119b0 ("migration: close kvm after cpr") a source
QEMU that has handed its VFIO devices over via cpr-transfer (or
cpr-exec) calls kvm_close() once the migration completes, so that the
destination can register its irq bypass consumers.  kvm_close() closes
the VM and vCPU descriptors and sets kvm_state to NULL, but the source
process keeps living with all its device models intact until the
management layer terminates it.

Nothing prevents those device models from raising interrupts in the
meantime, and the regular exit path even guarantees one: net_cleanup()
deletes the netdev behind an emulated virtio-net, the NIC sees the link
go down and calls virtio_notify_config(), MSI-X delivers through the
in-kernel APIC, and kvm_irqchip_send_msi() dereferences the NULL
kvm_state:

  #0  kvm_vm_ioctl (s=0x0, type=KVM_SIGNAL_MSI)   accel/kvm/kvm-all.c
  #1  kvm_irqchip_send_msi (s=0x0)
  #2  kvm_send_msi                                hw/i386/kvm/apic.c
  #3  kvm_apic_mem_write
  ...
  #9  pci_msi_trigger
  #11 msix_notify
  #12 virtio_pci_notify
  #14 virtio_notify_config
  #15 virtio_net_set_link_status
  #16 qemu_del_net_client
  #17 net_cleanup
  #18 qemu_cleanup

Every retired cpr-transfer source with a VFIO device thus dies with
SIGSEGV on SIGTERM/quit and leaves a core dump behind.  Without a VFIO
device kvm_close() never runs and the same exit is harmless.

Make the ioctl wrappers honour the closed state instead of assuming a
live KVMState: keep kvm_state allocated, with its closed descriptors as
the mark, and let kvm_ioctl(), kvm_vm_ioctl() and kvm_vcpu_ioctl()
return 0 once kvm_close() has been called, so any late interrupt or
query on the retired source is a no-op.  Returning an error instead
would still kill it: kvm_set_irq() aborts and kvm_irqchip_commit_routes()
asserts on any failure, and the ioeventfd callers abort on a stale errno.

Fixes: 7ed0919119b0 ("migration: close kvm after cpr")
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 accel/kvm/kvm-all.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index a39c1ba260a..8b5d342af52 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -644,7 +644,6 @@ void kvm_close(void)
         close(kvm_state->fd);
         kvm_state->fd = -1;
     }
-    kvm_state = NULL;
 }
 
 /*
@@ -3371,6 +3370,11 @@ int kvm_ioctl(KVMState *s, unsigned long type, ...)
     arg = va_arg(ap, void *);
     va_end(ap);
 
+    /* kvm_close() has been called: there is no KVM to talk to any more */
+    if (s->fd == -1) {
+        return 0;
+    }
+
     trace_kvm_ioctl(type, arg);
     ret = ioctl(s->fd, type, arg);
     if (ret == -1) {
@@ -3389,6 +3393,11 @@ int kvm_vm_ioctl(KVMState *s, unsigned long type, ...)
     arg = va_arg(ap, void *);
     va_end(ap);
 
+    /* kvm_close() has been called: there is no VM to talk to any more */
+    if (s->vmfd == -1) {
+        return 0;
+    }
+
     trace_kvm_vm_ioctl(type, arg);
     accel_ioctl_begin();
     ret = ioctl(s->vmfd, type, arg);
@@ -3409,6 +3418,11 @@ int kvm_vcpu_ioctl(CPUState *cpu, unsigned long type, ...)
     arg = va_arg(ap, void *);
     va_end(ap);
 
+    /* kvm_close() has been called: the vCPU fds are gone */
+    if (cpu->kvm_fd == -1) {
+        return 0;
+    }
+
     trace_kvm_vcpu_ioctl(cpu->cpu_index, type, arg);
     accel_cpu_ioctl_begin(cpu);
     ret = ioctl(cpu->kvm_fd, type, arg);

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 24/35] kvm: fix coalesced MMIO flush racing with kvm_close()
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (22 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 23/35] kvm: tolerate ioctls after kvm_close() Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:19   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 25/35] migration/cpr: add cpr_is_in_progress() Andrey Drobyshev
                   ` (11 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

kvm_close() destroys the vCPUs, which unmaps their kvm_run areas, and the
coalesced MMIO ring lives in the kvm_run area of the first vCPU.  Nothing
stops the RCU thread from flushing that ring at the same time: finalizing
a memory region of a freed flatview calls memory_region_transaction_begin()
and thus kvm_flush_coalesced_mmio_buffer().  kvm_state is still set at
that point, so the flush reads the ring after it has been unmapped:

  #0  kvm_flush_coalesced_mmio_buffer   accel/kvm/kvm-all.c
  #1  qemu_flush_coalesced_mmio_buffer
  #2  memory_region_transaction_begin
  #3  memory_region_finalize
  ...
  #10 flatview_destroy
  #11 call_rcu_thread

while the main thread is in:

  kvm_close -> cpu_remove_sync
  vfio_kvm_device_close
  vfio_cpr_kvm_close_notifier (MIG_EVENT_PRECOPY_DONE)

Seen as a SIGSEGV of the old QEMU during repeated cpr-exec of a VM with
a VFIO device.

Clear the ring pointer first, so that new flushes skip it, and wait for
the pending RCU callbacks before destroying the vCPUs.

Fixes: 7ed0919119b0 ("migration: close kvm after cpr")
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 accel/kvm/kvm-all.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index 8b5d342af52..294a87043ef 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -630,6 +630,14 @@ void kvm_close(void)
         return;
     }
 
+    /*
+     * Destroying vCPUs unmaps the coalesced MMIO ring, while the RCU thread
+     * may be flushing it from a memory region finalizer.  Stop new flushes
+     * and wait for the pending RCU callbacks first.
+     */
+    kvm_state->coalesced_mmio_ring = NULL;
+    drain_call_rcu();
+
     CPU_FOREACH(cpu) {
         cpu_remove_sync(cpu);
         close(cpu->kvm_fd);

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 25/35] migration/cpr: add cpr_is_in_progress()
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (23 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 24/35] kvm: fix coalesced MMIO flush racing with kvm_close() Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:21   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 26/35] vfio/migration: don't migrate device state during CPR Andrey Drobyshev
                   ` (10 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

cpr_incoming_needed() only looks at the migration mode.  That's fine for
vmstate .needed callbacks, which run during a save or load.  But code that
runs at any time, e.g. VM state change handlers, can't tell from it
whether a CPR migration is actually going on: on the source the mode is
just a parameter, set by migrate-set-parameters whether or not a migrate
follows.

Add cpr_is_in_progress(), which is true on the target until the incoming
migration is done, and on the source only while the outgoing one runs.

Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 include/migration/cpr.h |  1 +
 migration/cpr.c         | 22 ++++++++++++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/include/migration/cpr.h b/include/migration/cpr.h
index 4dd397a7c13..e73c5da1d9c 100644
--- a/include/migration/cpr.h
+++ b/include/migration/cpr.h
@@ -64,6 +64,7 @@ MigMode cpr_get_incoming_mode(void);
 void cpr_set_incoming_mode(MigMode mode);
 bool cpr_is_incoming(void);
 bool cpr_exec_is_incoming(void);
+bool cpr_is_in_progress(void);
 
 bool cpr_state_save(MigrationChannel *channel, Error **errp);
 int cpr_state_load(MigrationChannel *channel, Error **errp);
diff --git a/migration/cpr.c b/migration/cpr.c
index a149e790c10..3ea5d86bab4 100644
--- a/migration/cpr.c
+++ b/migration/cpr.c
@@ -549,6 +549,28 @@ bool cpr_exec_is_incoming(void)
     return incoming_mode == MIG_MODE_CPR_EXEC;
 }
 
+bool cpr_is_in_progress(void)
+{
+    /* The incoming mode on the target, the 'mode' parameter on the source */
+    MigMode mode = migrate_mode();
+
+    if (mode != MIG_MODE_CPR_TRANSFER && mode != MIG_MODE_CPR_EXEC) {
+        return false;
+    }
+
+    /* Target: the incoming mode is set until the incoming migration is done */
+    if (cpr_is_incoming()) {
+        return true;
+    }
+
+    /*
+     * Source: the parameter alone means nothing, it may just have been set.
+     * An outgoing CPR migration has the CPR state open, from
+     * cpr_state_save() to cpr_state_close().
+     */
+    return cpr_state_file != NULL;
+}
+
 bool cpr_state_save(MigrationChannel *channel, Error **errp)
 {
     int ret;

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 26/35] vfio/migration: don't migrate device state during CPR
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (24 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 25/35] migration/cpr: add cpr_is_in_progress() Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:26   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 27/35] vfio/migration: don't assume the device is running on CPR incoming Andrey Drobyshev
                   ` (9 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

cpr-transfer and cpr-exec hand the running VFIO device over to the new
QEMU as is: the device fd, its mappings and interrupts are preserved, and
the device itself is never stopped.

But if the device supports VFIO migration, its migration handlers still
run.  The old QEMU stops the device and saves its state, then the new
QEMU puts the very same device into RESUMING and loads that state back.
On top of that, vfio_pci_load_config() re-programs MSI-X and disables
vector 0.

For an NVIDIA vGPU this breaks the device after qemu-update: restoring
the state into the live vGPU fails and the guest loses the device.
Devices without migration support aren't affected, since no migration
handlers are registered for them.

Skip VFIO migration while a CPR migration is in progress: don't save or
load the device state, and don't change the device state on VM
stop/start.  All of it is gated on cpr_is_in_progress(), not on the mode
alone: the mode may merely be set, and then a plain stop/cont must still
change the device state, and a snapshot must still save it.

Also reset multifd_transfer in vfio_multifd_cleanup(): it's only set in
vfio_multifd_setup(), which CPR now skips, so a stale value from an
earlier migration would still let the multifd save thread run.

Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 hw/vfio/migration-multifd.c |  2 ++
 hw/vfio/migration.c         | 38 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c
index e4785031a73..452f772dd7a 100644
--- a/hw/vfio/migration-multifd.c
+++ b/hw/vfio/migration-multifd.c
@@ -567,6 +567,8 @@ void vfio_multifd_cleanup(VFIODevice *vbasedev)
     VFIOMigration *migration = vbasedev->migration;
 
     g_clear_pointer(&migration->multifd, vfio_multifd_free);
+    /* Decided anew by vfio_multifd_setup() of the next migration */
+    migration->multifd_transfer = false;
 }
 
 bool vfio_multifd_transfer_supported(void)
diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
index 4c06e3db936..b142c9f2289 100644
--- a/hw/vfio/migration.c
+++ b/hw/vfio/migration.c
@@ -19,6 +19,7 @@
 #include "hw/vfio/vfio-device.h"
 #include "hw/vfio/vfio-migration.h"
 #include "migration/misc.h"
+#include "migration/cpr.h"
 #include "migration/savevm.h"
 #include "migration/vmstate.h"
 #include "migration/qemu-file.h"
@@ -513,7 +514,6 @@ static void vfio_save_cleanup(void *opaque)
     Error *local_err = NULL;
     int ret;
 
-    /* Currently a NOP, done for symmetry with load_cleanup() */
     vfio_multifd_cleanup(vbasedev);
 
     /*
@@ -674,6 +674,16 @@ static void vfio_save_state(QEMUFile *f, void *opaque)
     Error *local_err = NULL;
     int ret;
 
+    /*
+     * .is_active doesn't cover the non-iterable stage, so the section is
+     * still sent: leave it empty, or new QEMU would load the config into
+     * the preserved device and re-program its MSI-X.
+     */
+    if (cpr_is_in_progress()) {
+        qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE);
+        return;
+    }
+
     if (vfio_multifd_transfer_enabled(vbasedev)) {
         if (vfio_load_config_after_iter(vbasedev)) {
             qemu_put_be64(f, VFIO_MIG_FLAG_DEV_CONFIG_LOAD_READY);
@@ -810,6 +820,11 @@ static bool vfio_switchover_ack_needed(void *opaque)
 {
     VFIODevice *vbasedev = opaque;
 
+    /* No device state is sent during CPR, so no ACK to wait for */
+    if (cpr_is_in_progress()) {
+        return false;
+    }
+
     return vfio_precopy_supported(vbasedev);
 }
 
@@ -824,7 +839,19 @@ static int vfio_switchover_start(void *opaque)
     return 0;
 }
 
+/*
+ * cpr-transfer and cpr-exec hand the running device over to the new QEMU
+ * as is: its fds, mappings and interrupts are preserved and it never stops.
+ * Device state migration must stay out of it, else the device is stopped,
+ * its state saved and then resumed into the very same device.
+ */
+static bool vfio_is_active(void *opaque)
+{
+    return !cpr_is_in_progress();
+}
+
 static const SaveVMHandlers savevm_vfio_handlers = {
+    .is_active = vfio_is_active,
     .save_prepare = vfio_save_prepare,
     .save_setup = vfio_save_setup,
     .save_cleanup = vfio_save_cleanup,
@@ -857,6 +884,11 @@ static void vfio_vmstate_change_prepare(void *opaque, bool running,
     Error *local_err = NULL;
     int ret;
 
+    /* The device is preserved, not a plain stop/cont: keep its state */
+    if (cpr_is_in_progress()) {
+        return;
+    }
+
     new_state = migration->device_state == VFIO_DEVICE_STATE_PRE_COPY ?
                     VFIO_DEVICE_STATE_PRE_COPY_P2P :
                     VFIO_DEVICE_STATE_RUNNING_P2P;
@@ -882,6 +914,10 @@ static void vfio_vmstate_change(void *opaque, bool running, RunState state)
     Error *local_err = NULL;
     int ret;
 
+    if (cpr_is_in_progress()) {
+        return;
+    }
+
     if (running) {
         new_state = VFIO_DEVICE_STATE_RUNNING;
     } else {

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 27/35] vfio/migration: don't assume the device is running on CPR incoming
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (25 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 26/35] vfio/migration: don't migrate device state during CPR Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:25   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 28/35] migration/ram: don't start dirty logging in CPR modes Andrey Drobyshev
                   ` (8 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

Since the device descriptor is preserved across CPR, the device keeps
whatever state the source left it in, while vfio_migration_init() on
the target still caches RUNNING.  For a VM paused before CPR, the
device is in STOP, so on 'cont' vfio_migration_set_state() finds the
cached and the requested state equal and skips the ioctl: the guest
runs with a stopped device.

On CPR incoming, cache STOP instead, and have the VM state handler,
which doesn't touch the device during CPR, note where the VM leaves
it: RUNNING once the target starts the VM, STOP if it stays paused.
At CPR the device can only be in one of the two.

Fixes: c29a65ed685 ("vfio/container: preserve descriptors")
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 hw/vfio/migration.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
index b142c9f2289..04d0de7cef0 100644
--- a/hw/vfio/migration.c
+++ b/hw/vfio/migration.c
@@ -914,6 +914,14 @@ static void vfio_vmstate_change(void *opaque, bool running, RunState state)
     Error *local_err = NULL;
     int ret;
 
+    /* CPR target only: the device kept its state, note where the VM left it */
+    if (cpr_is_incoming()) {
+        vbasedev->migration->device_state = running ?
+            VFIO_DEVICE_STATE_RUNNING : VFIO_DEVICE_STATE_STOP;
+        return;
+    }
+
+    /* CPR source only: the device keeps running across the exec */
     if (cpr_is_in_progress()) {
         return;
     }
@@ -1039,7 +1047,9 @@ static int vfio_migration_init(VFIODevice *vbasedev)
     vbasedev->migration = g_new0(VFIOMigration, 1);
     migration = vbasedev->migration;
     migration->vbasedev = vbasedev;
-    migration->device_state = VFIO_DEVICE_STATE_RUNNING;
+    /* A device inherited across CPR is in STOP unless the VM gets started */
+    migration->device_state = cpr_is_incoming() ? VFIO_DEVICE_STATE_STOP :
+                                                  VFIO_DEVICE_STATE_RUNNING;
     migration->data_fd = -1;
     migration->mig_flags = mig_flags;
 

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 28/35] migration/ram: don't start dirty logging in CPR modes
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (26 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 27/35] vfio/migration: don't assume the device is running on CPR incoming Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:27   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 29/35] VZ: vfio/cpr: don't close KVM on cpr-exec Andrey Drobyshev
                   ` (7 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

In the CPR modes guest RAM is preserved in place, not copied:
migrate_ram_is_ignored() is true for every block.  Yet ram_init_bitmaps()
still starts the global dirty log, so every listener starts tracking
pages nobody will send.  KVM write-protects all guest memory, and VFIO
starts DMA logging on the devices; with cpr-exec that logging is never
stopped before the exec, leaving e.g. the DMA page tracker of an NVIDIA
vGPU running after the update.

Don't start the dirty log for a migration that migrates no RAM, as is
already done for background snapshots.  ram_save_cleanup() only stops
the log if it was started.

Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 migration/ram.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/migration/ram.c b/migration/ram.c
index 399b43dd631..0f61942fd3d 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -2810,8 +2810,11 @@ static bool ram_init_bitmaps(RAMState *rs, Error **errp)
 
     WITH_RCU_READ_LOCK_GUARD() {
         ram_list_init_bitmaps();
-        /* We don't use dirty log with background snapshots */
-        if (!migrate_background_snapshot()) {
+        /*
+         * We don't use dirty log with background snapshots, nor in CPR modes,
+         * where no RAM is migrated (see migrate_ram_is_ignored()).
+         */
+        if (!migrate_background_snapshot() && !cpr_incoming_needed(NULL)) {
             ret = memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION, errp);
             if (!ret) {
                 goto out_unlock;

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 29/35] VZ: vfio/cpr: don't close KVM on cpr-exec
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (27 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 28/35] migration/ram: don't start dirty logging in CPR modes Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:29   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 30/35] VZ: migration/cpr-exec: abort cpr-exec if memory can't be preserved Andrey Drobyshev
                   ` (6 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

With VFIO devices, KVM is closed on the source when the migration
completes, so that the new QEMU can create its own KVM VM with the same
devices.  That is done from a migration notifier, which for cpr-exec
runs before cpr_exec_cb().  But cpr_exec_cb() may still give up, when
the memory can't be preserved or execve() fails, and resume the VM in
the old QEMU.  A VM without KVM can't resume: kvm_close() removes the
vCPU threads, and the first KVM ioctl of vm_start() aborts QEMU
(KVM_SET_CLOCK in kvmclock_vm_state_change()).

cpr-exec doesn't need it anyway: the KVM fds are close-on-exec, so the
kernel closes them during execve(), before the new QEMU runs, and
kvm_vm_release() tears down the irqfds then.  Keep the notifier for
cpr-transfer only, where the source process lives on.

Tested on the vfio-net rig: cpr-exec 5/5; a cpr-exec aborted in
preserve_ram() and one with a bogus cpr-exec-command both left the VM
running with no lost pings, and a later cpr-exec completed.

Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 hw/vfio/cpr.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c
index db462aabcbf..7ca761d5bf5 100644
--- a/hw/vfio/cpr.c
+++ b/hw/vfio/cpr.c
@@ -192,13 +192,16 @@ static int vfio_cpr_kvm_close_notifier(NotifierWithReturn *notifier,
     return 0;
 }
 
+/*
+ * Not for cpr-exec: the exec closes the KVM fds anyway, and until then
+ * cpr_exec_cb() may still give up and resume the VM, which needs KVM.
+ */
 void vfio_cpr_add_kvm_notifier(void)
 {
     if (!kvm_close_notifier.notify) {
-        migration_add_notifier_modes(&kvm_close_notifier,
-                                     vfio_cpr_kvm_close_notifier,
-                                     MIG_MODE_CPR_TRANSFER, MIG_MODE_CPR_EXEC,
-                                     -1);
+        migration_add_notifier_mode(&kvm_close_notifier,
+                                    vfio_cpr_kvm_close_notifier,
+                                    MIG_MODE_CPR_TRANSFER);
     }
 }
 

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 30/35] VZ: migration/cpr-exec: abort cpr-exec if memory can't be preserved
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (28 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 29/35] VZ: vfio/cpr: don't close KVM on cpr-exec Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:29   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 31/35] VZ: migration/cpr-exec: take the vhost-net devices back if exec fails Andrey Drobyshev
                   ` (5 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

cpr_exec_cb() marks all saved ramblocks with MADV_DOEXEC before exec, but
ignores the result.  If madvise() fails for some block, the walk stops
there and the remaining blocks are not marked, yet execvp() still runs.
The new QEMU then reuses addresses of guest RAM that wasn't preserved,
and the guest is lost.

Treat that as a failed cpr-exec: go to the same failure path as when
execvp() fails, so the VM keeps running in the old QEMU.

On that path also clear MADV_DOEXEC from all saved ramblocks again, just
like the preserved fds get close-on-exec back.

Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 migration/cpr-exec.c | 32 +++++++++++++++++++++++++-------
 1 file changed, 25 insertions(+), 7 deletions(-)

diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
index 77db262c139..005d0062271 100644
--- a/migration/cpr-exec.c
+++ b/migration/cpr-exec.c
@@ -163,6 +163,19 @@ static bool preserve_ram(cpr_ramblock_t ram, char *name)
     return true;
 }
 
+static bool unpreserve_ram(cpr_ramblock_t ram, char *name)
+{
+#ifdef MADV_DONTEXEC
+    qemu_madvise((void *) ram.addr, ram.length, MADV_DONTEXEC);
+#endif
+    return true;
+}
+
+static void cpr_exec_unpreserve_ram(void)
+{
+    cpr_walk_ramblock(unpreserve_ram);
+}
+
 static bool mark_ram_rdonly(cpr_ramblock_t ram, char *name)
 {
     /*
@@ -211,7 +224,10 @@ static void cpr_exec_cb(void *opaque)
      */
     cpr_exec_preserve_fds();
 
-    cpr_walk_ramblock(preserve_ram);
+    if (!cpr_walk_ramblock(preserve_ram)) {
+        /* preserve_ram() has already reported the error and failed migration */
+        goto fail;
+    }
     if (cpr_ram_rdonly) {
         cpr_walk_ramblock(mark_ram_rdonly);
     }
@@ -230,18 +246,20 @@ static void cpr_exec_cb(void *opaque)
      * or the system is very short on resources.
      */
     error_setg_errno(&err, errno, "execvp %s failed", argv[0]);
-    g_clear_pointer(&argv, g_strfreev);
-    cpr_exec_unpreserve_fds();
-    if (cpr_ram_rdonly) {
-        cpr_ram_restore_rw();
-    }
-
     error_report_err(error_copy(err));
     migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
     migrate_set_error(s, err);
     error_free(err);
     err = NULL;
 
+fail:
+    g_clear_pointer(&argv, g_strfreev);
+    cpr_exec_unpreserve_fds();
+    cpr_exec_unpreserve_ram();
+    if (cpr_ram_rdonly) {
+        cpr_ram_restore_rw();
+    }
+
     /* Note, we can go from state COMPLETED to FAILED */
     migration_call_notifiers(MIG_EVENT_PRECOPY_FAILED, NULL);
 

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 31/35] VZ: migration/cpr-exec: take the vhost-net devices back if exec fails
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (29 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 30/35] VZ: migration/cpr-exec: abort cpr-exec if memory can't be preserved Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:32   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 32/35] VZ: migration/cpr-exec: don't exec if a vhost-net device can't be released Andrey Drobyshev
                   ` (4 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

Right before execve(), cpr_exec_cb() releases the kernel vhost-net
devices with VHOST_RESET_OWNER, so that the new QEMU can take them.  If
execve() fails, the VM goes on in the old QEMU, but nobody takes the
devices back: the kernel refuses the ioctls of the next
vhost_net_start(), and the guest is left without network.

Take them back with VHOST_SET_OWNER when the exec fails, and set the
vring call and err fds again: VHOST_RESET_OWNER dropped them too, and
vhost_dev_init() registers them only once.  The devices are stopped at
that point, and vhost_net_start() sets up the rest again when the VM
resumes.  Only the devices that were actually released are taken back:
vhost_net_cpr_reset_owners() marks them.  vhost-vsock does the same in
its CPR notifier.

Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 hw/net/vhost_net.c        | 49 +++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/virtio/vhost.h |  2 ++
 include/net/vhost_net.h   |  1 +
 migration/cpr-exec.c      |  2 ++
 4 files changed, 54 insertions(+)

diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
index 56c697f4bcd..fae6b060c6f 100644
--- a/hw/net/vhost_net.c
+++ b/hw/net/vhost_net.c
@@ -594,6 +594,55 @@ void vhost_net_cpr_reset_owners(void)
             if (r < 0 && r != -ENOSYS) {
                 warn_report("cpr: failed to reset vhost-net owner for %s: %s",
                             nc->name, strerror(-r));
+            } else if (r == 0) {
+                net->cpr_owner_reset = true;
+            }
+        }
+    }
+}
+
+/*
+ * Take the ownership back when the exec fails and the VM goes on in the
+ * old QEMU.  VHOST_RESET_OWNER has reset the kernel device, but the devices
+ * are stopped, and vhost_net_start() sets everything up again when the VM
+ * resumes.
+ */
+void vhost_net_cpr_set_owners(void)
+{
+    NetClientState *nc;
+
+    QTAILQ_FOREACH(nc, &net_clients, next) {
+        VHostNetState *net = get_vhost_net(nc);
+
+        if (net && net->cpr_owner_reset) {
+            struct vhost_dev *dev = &net->dev;
+            int i, r = vhost_dev_set_owner(dev);
+
+            if (r < 0) {
+                warn_report("cpr: failed to set vhost-net owner for %s: %s",
+                            nc->name, strerror(-r));
+                continue;
+            }
+            net->cpr_owner_reset = false;
+
+            /* RESET_OWNER also dropped the vring fds set by vhost_dev_init() */
+            for (i = 0; i < dev->nvqs; i++) {
+                const VhostOps *ops = dev->vhost_ops;
+                struct vhost_virtqueue *vq = &dev->vqs[i];
+                struct vhost_vring_file file = {
+                    .index = ops->vhost_get_vq_index(dev, dev->vq_index + i),
+                    .fd = event_notifier_get_wfd(&vq->masked_notifier),
+                };
+
+                r = ops->vhost_set_vring_call(dev, &file);
+                if (r == 0 && ops->vhost_set_vring_err) {
+                    file.fd = event_notifier_get_fd(&vq->error_notifier);
+                    r = ops->vhost_set_vring_err(dev, &file);
+                }
+                if (r < 0) {
+                    warn_report("cpr: failed to set vring fds of %s: %s",
+                                nc->name, strerror(-r));
+                }
             }
         }
     }
diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
index a92439d3443..421f7139cfb 100644
--- a/include/hw/virtio/vhost.h
+++ b/include/hw/virtio/vhost.h
@@ -151,6 +151,8 @@ struct vhost_net {
     SaveAcketFeatures *save_acked_features;
     bool is_vhost_user;
     NetClientState *nc;
+    /* released for cpr-exec, take back if it fails */
+    bool cpr_owner_reset;
 };
 
 /**
diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h
index 39ce7ad0e3d..cdfefa81500 100644
--- a/include/net/vhost_net.h
+++ b/include/net/vhost_net.h
@@ -90,4 +90,5 @@ int vhost_net_virtqueue_restart(VirtIODevice *vdev, NetClientState *nc,
 void vhost_net_save_acked_features(NetClientState *nc);
 
 void vhost_net_cpr_reset_owners(void);
+void vhost_net_cpr_set_owners(void);
 #endif
diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
index 005d0062271..d853945b81f 100644
--- a/migration/cpr-exec.c
+++ b/migration/cpr-exec.c
@@ -259,6 +259,8 @@ fail:
     if (cpr_ram_rdonly) {
         cpr_ram_restore_rw();
     }
+    /* Still the same mm, take back the vhost-net devices we released */
+    vhost_net_cpr_set_owners();
 
     /* Note, we can go from state COMPLETED to FAILED */
     migration_call_notifiers(MIG_EVENT_PRECOPY_FAILED, NULL);

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 32/35] VZ: migration/cpr-exec: don't exec if a vhost-net device can't be released
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (30 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 31/35] VZ: migration/cpr-exec: take the vhost-net devices back if exec fails Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:30   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 33/35] VZ: vfio/region: preserve BAR mappings across cpr-exec #VSTOR-103860 Andrey Drobyshev
                   ` (3 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

vhost_net_cpr_reset_owners() only warns when VHOST_RESET_OWNER fails for
a device, and the exec goes on.  But the new QEMU can't take such a
device: it is still owned by the old mm, so VHOST_SET_OWNER fails with
EBUSY, and the guest is left without network after the update.

Stop at the first device that can't be released and abort the cpr-exec
instead.  The VM goes on in the old QEMU, with the devices released so
far taken back.  vhost-vsock already does that in its pre_save.

Fixes: 1278e4830052 ("VZ: migration/cpr-exec: vhost-net: send RESET_OWNER before cpr-exec")
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 hw/net/vhost_net.c      | 19 +++++++++++++------
 include/net/vhost_net.h |  2 +-
 migration/cpr-exec.c    |  7 ++++++-
 3 files changed, 20 insertions(+), 8 deletions(-)

diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
index fae6b060c6f..9110dc43d23 100644
--- a/hw/net/vhost_net.c
+++ b/hw/net/vhost_net.c
@@ -579,8 +579,12 @@ void vhost_net_config_mask(VHostNetState *net, VirtIODevice *dev, bool mask)
  * EBUSY.  Called right before execve(), when the VM is stopped and the
  * devices have already been stopped, so nothing is in flight.  The device is
  * fully re-initialized from the migrated state by the new QEMU.
+ *
+ * Stops at the first device that can't be released, since the new QEMU
+ * couldn't take it.  The caller then takes back the ones already released
+ * with vhost_net_cpr_set_owners().
  */
-void vhost_net_cpr_reset_owners(void)
+void vhost_net_cpr_reset_owners(Error **errp)
 {
     NetClientState *nc;
 
@@ -591,12 +595,15 @@ void vhost_net_cpr_reset_owners(void)
             net->dev.vhost_ops->backend_type == VHOST_BACKEND_TYPE_KERNEL) {
             int r = vhost_dev_reset_owner(&net->dev);
 
-            if (r < 0 && r != -ENOSYS) {
-                warn_report("cpr: failed to reset vhost-net owner for %s: %s",
-                            nc->name, strerror(-r));
-            } else if (r == 0) {
-                net->cpr_owner_reset = true;
+            if (r == -ENOSYS) {
+                continue;
+            }
+            if (r < 0) {
+                error_setg_errno(errp, -r, "cpr: failed to reset vhost-net "
+                                 "owner for %s", nc->name);
+                return;
             }
+            net->cpr_owner_reset = true;
         }
     }
 }
diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h
index cdfefa81500..679edefa1d3 100644
--- a/include/net/vhost_net.h
+++ b/include/net/vhost_net.h
@@ -89,6 +89,6 @@ int vhost_net_virtqueue_restart(VirtIODevice *vdev, NetClientState *nc,
 
 void vhost_net_save_acked_features(NetClientState *nc);
 
-void vhost_net_cpr_reset_owners(void);
+void vhost_net_cpr_reset_owners(Error **errp);
 void vhost_net_cpr_set_owners(void);
 #endif
diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
index d853945b81f..c0bdda931f9 100644
--- a/migration/cpr-exec.c
+++ b/migration/cpr-exec.c
@@ -236,7 +236,10 @@ static void cpr_exec_cb(void *opaque)
      * Hand the preserved vhost-net devices over to the new QEMU: they are
      * still owned by this mm, which is about to be replaced by execve().
      */
-    vhost_net_cpr_reset_owners();
+    vhost_net_cpr_reset_owners(&err);
+    if (err) {
+        goto fail_with_err;
+    }
 
     trace_cpr_exec();
     execvp(argv[0], argv);
@@ -246,6 +249,8 @@ static void cpr_exec_cb(void *opaque)
      * or the system is very short on resources.
      */
     error_setg_errno(&err, errno, "execvp %s failed", argv[0]);
+
+fail_with_err:
     error_report_err(error_copy(err));
     migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
     migrate_set_error(s, err);

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 33/35] VZ: vfio/region: preserve BAR mappings across cpr-exec #VSTOR-103860
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (31 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 32/35] VZ: migration/cpr-exec: don't exec if a vhost-net device can't be released Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:38   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 34/35] VZ: migration/cpr-exec: allow cpr-exec with VFIO devices #VSTOR-103860 Andrey Drobyshev
                   ` (2 subsequent siblings)
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

After cpr-exec, new QEMU mmaps the device BARs again on the preserved
device fd.  Some drivers don't allow that: the NVIDIA vGPU VFIO driver
refuses to map a VF BAR that is still recorded as mapped by old QEMU,
and that record is dropped only when the device fd is closed.  So BAR1
and BAR3 of the vGPU can't be mapped, and the guest loses the device.

Keep the mappings instead.  Save each BAR mapping in the CPR ramblock
list, so cpr_exec_cb() marks it with MADV_DOEXEC together with guest RAM,
and new QEMU finds it there and reuses it instead of calling mmap().
Same find-or-create pattern as for other preserved objects, e.g. vector
eventfds in vfio_notifier_init().  No saved mapping means old QEMU didn't
map the region, e.g. its mmap failed or the region is mapped lazily, so
new QEMU maps it afresh.

The ID is "<device name> region <index> mmaps[<index>]".  It doesn't
depend on the device path, which is gone when the display region is
finalized, so the entry is dropped together with the mapping.

The saved addresses only make sense for cpr-exec, which keeps the
address space.  A cpr-transfer target is another process and would have
to map the regions anew, which the drivers above refuse while the old
mappings exist.  So block cpr-transfer for VFIO devices.

This needs a kernel which supports MADV_DOEXEC on shared PFN mappings of
device memory.  Without it cpr-exec of a VM with VFIO devices fails.

Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 docs/devel/migration/CPR.rst |  4 ++--
 hw/vfio/device.c             | 24 ++++++++++++++++++++--
 hw/vfio/region.c             | 49 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/vfio/vfio-cpr.h   |  1 +
 4 files changed, 74 insertions(+), 4 deletions(-)

diff --git a/docs/devel/migration/CPR.rst b/docs/devel/migration/CPR.rst
index 2f34a6d7c9d..229a389dc2d 100644
--- a/docs/devel/migration/CPR.rst
+++ b/docs/devel/migration/CPR.rst
@@ -151,8 +151,8 @@ cpr-transfer mode
 
 This mode allows the user to transfer a guest to a new QEMU instance
 on the same host with minimal guest pause time, by preserving guest
-RAM in place, albeit with new virtual addresses in new QEMU.  Devices
-and their pinned memory pages are also preserved for VFIO and IOMMUFD.
+RAM in place, albeit with new virtual addresses in new QEMU.  VFIO and
+IOMMUFD devices are not supported in this mode: use cpr-exec for them.
 
 The user starts new QEMU on the same host as old QEMU, with command-
 line arguments to create the same machine, plus the ``-incoming``
diff --git a/hw/vfio/device.c b/hw/vfio/device.c
index 5a14a7d5c0a..2a7073ccec8 100644
--- a/hw/vfio/device.c
+++ b/hw/vfio/device.c
@@ -464,12 +464,32 @@ bool vfio_device_attach(char *name, VFIODevice *vbasedev,
                              TYPE_VFIO_IOMMU_IOMMUFD :
                              TYPE_VFIO_IOMMU_LEGACY;
 
-    return vfio_device_attach_by_iommu_type(iommu_type, name, vbasedev,
-                                            as, errp);
+    /*
+     * Only cpr-exec keeps the address space and so the region mappings,
+     * see vfio_region_mmap().  A cpr-transfer target would have to map the
+     * regions anew, which some drivers (NVIDIA vGPU) refuse.
+     */
+    error_setg(&vbasedev->cpr.mmap_blocker,
+               "cpr-transfer is not supported with VFIO devices; "
+               "use the cpr-exec mode");
+    if (migrate_add_blocker_modes(&vbasedev->cpr.mmap_blocker, errp,
+                                  MIG_MODE_CPR_TRANSFER, -1)) {
+        return false;
+    }
+
+    if (!vfio_device_attach_by_iommu_type(iommu_type, name, vbasedev,
+                                          as, errp)) {
+        migrate_del_blocker(&vbasedev->cpr.mmap_blocker);
+        return false;
+    }
+
+    return true;
 }
 
 void vfio_device_detach(VFIODevice *vbasedev)
 {
+    migrate_del_blocker(&vbasedev->cpr.mmap_blocker);
+
     if (!vbasedev->bcontainer) {
         return;
     }
diff --git a/hw/vfio/region.c b/hw/vfio/region.c
index b0332f27fcd..86a34b42c57 100644
--- a/hw/vfio/region.c
+++ b/hw/vfio/region.c
@@ -30,6 +30,7 @@
 #include "qemu/units.h"
 #include "monitor/monitor.h"
 #include "system/ramblock.h"
+#include "migration/cpr.h"
 #include "vfio-helpers.h"
 
 /*
@@ -227,8 +228,47 @@ int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region,
     return 0;
 }
 
+static char *vfio_region_cpr_name(VFIORegion *region, int index)
+{
+    return g_strdup_printf("%s region %d mmaps[%d]", region->vbasedev->name,
+                           region->nr, index);
+}
+
+/*
+ * A saved mapping is one preserved by cpr-exec, reuse it.  None means old
+ * QEMU didn't map the region, e.g. its mmap failed or it's mapped lazily,
+ * so map it afresh.
+ */
+static void *vfio_region_cpr_find(VFIORegion *region, int index)
+{
+    g_autofree char *name = vfio_region_cpr_name(region, index);
+    cpr_ramblock_t ram = cpr_find_ramblock(name);
+
+    if (ram.addr && ram.length != region->mmaps[index].size) {
+        error_report("CPR mode=exec is requested, but the mapping of %s "
+                     "can't be reused: addr=%p length=%zu, expected "
+                     "length=%zu. Aborting", name, (void *)ram.addr,
+                     ram.length, region->mmaps[index].size);
+        exit(1);
+    }
+
+    return (void *)(uintptr_t)ram.addr;
+}
+
+static void vfio_region_cpr_save(VFIORegion *region, int index)
+{
+    g_autofree char *name = vfio_region_cpr_name(region, index);
+    void *addr = region->mmaps[index].mmap;
+    size_t size = region->mmaps[index].size;
+
+    cpr_save_ramblock(name, (cpr_ramblock_t) { (uintptr_t)addr, size });
+}
+
 static void vfio_subregion_unmap(VFIORegion *region, int index)
 {
+    g_autofree char *cpr_name = vfio_region_cpr_name(region, index);
+
+    cpr_delete_ramblock(cpr_name);
     trace_vfio_region_unmap(memory_region_name(&region->mmaps[index].mem),
                             region->mmaps[index].offset,
                             region->mmaps[index].offset +
@@ -314,6 +354,11 @@ int vfio_region_mmap(VFIORegion *region)
         size_t align = MIN(1ULL << ctz64(region->mmaps[i].size), 1 * GiB);
         void *map_base, *map_align;
 
+        region->mmaps[i].mmap = vfio_region_cpr_find(region, i);
+        if (region->mmaps[i].mmap) {
+            goto mapped;
+        }
+
         /*
          * Align the mmap for more efficient mapping in the kernel.  Ideally
          * we'd know the PMD and PUD mapping sizes to use as discrete alignment
@@ -347,7 +392,9 @@ int vfio_region_mmap(VFIORegion *region)
             ret = -errno;
             goto no_mmap;
         }
+        vfio_region_cpr_save(region, i);
 
+mapped:
         name = g_strdup_printf("%s mmaps[%d]",
                                memory_region_name(region->mem), i);
         memory_region_init_ram_device_ptr(&region->mmaps[i].mem,
@@ -427,6 +474,8 @@ void vfio_region_finalize(VFIORegion *region)
 
     for (i = 0; i < region->nr_mmaps; i++) {
         if (region->mmaps[i].mmap) {
+            g_autofree char *cpr_name = vfio_region_cpr_name(region, i);
+            cpr_delete_ramblock(cpr_name);
             munmap(region->mmaps[i].mmap, region->mmaps[i].size);
         }
     }
diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h
index 4606da500a7..fa1699857ca 100644
--- a/include/hw/vfio/vfio-cpr.h
+++ b/include/hw/vfio/vfio-cpr.h
@@ -34,6 +34,7 @@ typedef struct VFIOContainerCPR {
 typedef struct VFIODeviceCPR {
     Error *mdev_blocker;
     Error *id_blocker;
+    Error *mmap_blocker;
     uint32_t hwpt_id;
     uint32_t ioas_id;
 } VFIODeviceCPR;

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 34/35] VZ: migration/cpr-exec: allow cpr-exec with VFIO devices #VSTOR-103860
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (32 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 33/35] VZ: vfio/region: preserve BAR mappings across cpr-exec #VSTOR-103860 Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:33   ` no-reply
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 35/35] VZ: vfio/migration: don't wait for stop-copy-only device state #VSTOR-141821 Andrey Drobyshev
  2026-11-04 13:11 ` [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Denis V. Lunev
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

Commit bb8da93ddfc4 ("VZ: migration/cpr-exec: enable checks prohibiting
cpr-exec #VSTOR-108564") introduced restrictions for cpr-exec migration
mode.  Among them was prohibition to do such migration in case of
presence of any VFIO devices.  Now we want to allow migration with such
devices in scope of VSTOR-103860.  Therefore, let's lift that restiction.

Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 hw/vfio/device.c              | 5 -----
 include/hw/vfio/vfio-device.h | 2 --
 migration/migration.c         | 3 ---
 3 files changed, 10 deletions(-)

diff --git a/hw/vfio/device.c b/hw/vfio/device.c
index 2a7073ccec8..d1b595adb71 100644
--- a/hw/vfio/device.c
+++ b/hw/vfio/device.c
@@ -36,11 +36,6 @@
 VFIODeviceList vfio_device_list =
     QLIST_HEAD_INITIALIZER(vfio_device_list);
 
-bool vfio_devices_present(void)
-{
-    return !QLIST_EMPTY(&vfio_device_list);
-}
-
 /*
  * We want to differentiate hot reset of multiple in-use devices vs
  * hot reset of a single in-use device. VFIO_DEVICE_RESET will already
diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h
index 935000f0074..fc28f580ba3 100644
--- a/include/hw/vfio/vfio-device.h
+++ b/include/hw/vfio/vfio-device.h
@@ -162,8 +162,6 @@ VFIODevice *vfio_get_vfio_device(Object *obj);
 typedef QLIST_HEAD(VFIODeviceList, VFIODevice) VFIODeviceList;
 extern VFIODeviceList vfio_device_list;
 
-bool vfio_devices_present(void);
-
 #ifdef CONFIG_LINUX
 /*
  * How devices communicate with the server.  The default option is through
diff --git a/migration/migration.c b/migration/migration.c
index 821764afae9..57aa0dea264 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -2115,9 +2115,6 @@ static bool migrate_cpr_is_blocked(Error **errp)
     } else if (cbw_filter_present()) {
         error_setg(errp, "CPR is prohibited: active block snapshots (CBW "
                    "filter) present");
-    } else if (vfio_devices_present()) {
-        error_setg(errp, "CPR is prohibited: passthrough (VFIO) "
-                   "devices present");
     } else {
         res = false;
     }

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* [PATCH QEMU HCI-8.1 v7 35/35] VZ: vfio/migration: don't wait for stop-copy-only device state #VSTOR-141821
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (33 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 34/35] VZ: migration/cpr-exec: allow cpr-exec with VFIO devices #VSTOR-103860 Andrey Drobyshev
@ 2026-11-03 13:57 ` Andrey Drobyshev
  2026-11-03 14:35   ` no-reply
  2026-11-04 13:11 ` [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Denis V. Lunev
  35 siblings, 1 reply; 72+ messages in thread
From: Andrey Drobyshev @ 2026-11-03 13:57 UTC (permalink / raw)
  To: svt-core; +Cc: andrey.drobyshev

A VFIO device reports the state it hands over once the guest is stopped
as pending data, and the migration switches over only when all pending
data fits into bandwidth x downtime-limit.  That state can't get any
smaller by iterating, so when it alone doesn't fit, the migration never
converges until the downtime limit is raised, which Nova does in steps
of minutes.  An NVIDIA vGPU reports 1.5-3 GiB of it, which never fits
the default limits.

Report the stop-copy size as pending only while the device still has
something to pre-copy.  Once it has drained, the rest of the pending
data, which iterating can shrink, decides the switchover on its own.

A device without pre-copy support has nothing to pre-copy from the
start, so its state isn't waited for either.  In both cases the
downtime limit no longer bounds the time that state takes to send:
when it fits, nothing is lost by not waiting, and when it doesn't,
waiting couldn't help.

Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
---
 hw/vfio/migration.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
index 04d0de7cef0..d66de7d31a2 100644
--- a/hw/vfio/migration.c
+++ b/hw/vfio/migration.c
@@ -569,16 +569,24 @@ static void vfio_state_pending_exact(void *opaque, uint64_t *must_precopy,
     VFIODevice *vbasedev = opaque;
     VFIOMigration *migration = vbasedev->migration;
     uint64_t stop_copy_size = VFIO_MIG_STOP_COPY_SIZE;
+    int ret;
 
     /*
-     * If getting pending migration size fails, VFIO_MIG_STOP_COPY_SIZE is
-     * reported so downtime limit won't be violated.
+     * If getting the pending sizes fails, VFIO_MIG_STOP_COPY_SIZE is
+     * reported, unconditionally, so the switchover isn't taken blindly.
      */
-    vfio_query_stop_copy_size(vbasedev, &stop_copy_size);
-    *must_precopy += stop_copy_size;
+    ret = vfio_query_stop_copy_size(vbasedev, &stop_copy_size);
 
     if (vfio_device_state_is_precopy(vbasedev)) {
-        vfio_query_precopy_size(migration);
+        ret |= vfio_query_precopy_size(migration);
+    }
+
+    /*
+     * Once there's nothing left to pre-copy, the stop-copy state can't
+     * shrink by iterating: don't let it hold up the switchover.
+     */
+    if (ret || migration->precopy_init_size || migration->precopy_dirty_size) {
+        *must_precopy += stop_copy_size;
     }
 
     trace_vfio_state_pending_exact(vbasedev->name, *must_precopy, *can_postcopy,

-- 
2.47.1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 01/35] VZ: migration/cpr-exec: fix Error leak in preserve_ram()
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 01/35] VZ: migration/cpr-exec: fix Error leak in preserve_ram() Andrey Drobyshev
@ 2026-11-03 14:00   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:00 UTC (permalink / raw)
  To: svt-core, Andrey Drobyshev; +Cc: andrey.drobyshev

> migrate_set_error() stores its own copy of the error, so free ours.
> 
> Fixes: a3745506daf9 ("VZ: migration/cpr-exec: mark saved RAM blocks with MADV_DOEXEC before execve")
> Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=1


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 02/35] migration/cpr-exec: restore RAM write access if exec fails
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 02/35] migration/cpr-exec: restore RAM write access if exec fails Andrey Drobyshev
@ 2026-11-03 14:01   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:01 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core; +Cc: andrey.drobyshev

> With -cpr-ram-rdonly the RAM blocks are made read-only right before
> execve().  If execve() fails, the VM resumes in the old QEMU with
> read-only RAM and crashes on the first write.  Make it writable again.
> 
> Fixes: 91da25f20471 ("migration/cpr-exec: add '-cpr-ram-rdonly' option to mark RAM blocks as RO #VSTOR-101579")
> Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=2


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 04/35] migration/cpr-exec: require a file or exec migration URI
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 04/35] migration/cpr-exec: require a file or exec migration URI Andrey Drobyshev
@ 2026-11-03 14:02   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:02 UTC (permalink / raw)
  To: svt-core, Andrey Drobyshev; +Cc: andrey.drobyshev

> cpr-exec writes the device state to the migration URI and new QEMU
> reads it back after the exec, so the URI must capture the data: a
> file, or an exec command that stores it.  Nothing checks that.  With a
> socket URI, e.g. a save or a live migration issued while the 'mode'
> parameter is left at cpr-exec after a failed qemu-update, QEMU sends a
> stream without the guest RAM to the peer, execs the stale cpr-exec
> command, and the VM is lost.
> 
> Refuse cpr-exec with anything but a file or exec URI, the two types
> the CPR documentation allows.  The check covers the incoming URI of
> new QEMU too, where the mode is cpr-exec as well.
> 
> Fixes: a3eae205c601 ("migration: cpr-exec mode")
> Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=4


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 03/35] vfio/region: create BAR dma-bufs with O_CLOEXEC
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 03/35] vfio/region: create BAR dma-bufs with O_CLOEXEC Andrey Drobyshev
@ 2026-11-03 14:02   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:02 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core; +Cc: andrey.drobyshev

> The dma-buf fds are created without O_CLOEXEC, so they leak into any
> exec'ed program.  With cpr-exec, every update leaks one fd per mmapped
> BAR, as new QEMU creates its own.
> 
> Fixes: 96163e0dbcb4 ("hw/vfio/region: Create dmabuf for PCI BAR per region")
> Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=3


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 05/35] migration: close the CPR state if the outgoing channel fails to open
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 05/35] migration: close the CPR state if the outgoing channel fails to open Andrey Drobyshev
@ 2026-11-03 14:03   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:03 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core; +Cc: andrey.drobyshev

> qmp_migrate() saves the CPR state before it opens the migration
> channel.  If the channel is a file or an exec command and it can't be
> opened, qmp_migrate_finish() returns right away and migration_cleanup()
> never runs, so the CPR state file stays open.
> 
> Close it there, next to the yank instance, which has the same problem.
> 
> Fixes: 624e6e654e11 ("migration: cpr-transfer mode")
> Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=5


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 06/35] migration/cpr-exec: close the persisted state if the channel fails to open
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 06/35] migration/cpr-exec: close the persisted state if the " Andrey Drobyshev
@ 2026-11-03 14:04   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:04 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core; +Cc: andrey.drobyshev

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] migration/cpr-exec: unsetenv reorder not described in commit message
--

commit d210939c2446d33dc691b9867615ff2066464565
Author: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

migration/cpr-exec: close the persisted state if the channel fails to open

This patch releases the dup'd CPR state memfd and clears the environment
variable when the migration channel fails to open, in qmp_migrate_finish().
It also makes cpr_exec_unpersist_state() return early when there is nothing
to release.

> diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
> --- a/migration/cpr-exec.c
> +++ b/migration/cpr-exec.c
> @@ -68,8 +68,8 @@ static int cpr_exec_find_state(void)
>      int mfd;
>  
>      assert(val);
> -    g_unsetenv(CPR_EXEC_STATE_NAME);
>      assert(!qemu_strtoi(val, NULL, 10, &mfd));
> +    g_unsetenv(CPR_EXEC_STATE_NAME);
>      return mfd;
>  }

[Severity: Low]
This isn't a bug, but the commit message doesn't mention this change.

In cpr_exec_find_state(), which runs on the incoming side after exec,
g_unsetenv() is now called after qemu_strtoi() instead of before it.

The commit message describes only the release on channel open failure and
the no-op behaviour of cpr_exec_unpersist_state().

Could the commit message mention this reorder and explain why it is needed?
Before the change, qemu_strtoi() parsed val after g_unsetenv() had already
removed the environment entry.

> @@ -83,9 +83,11 @@ void cpr_exec_unpersist_state(void)
>      int mfd;
>      const char *val = g_getenv(CPR_EXEC_STATE_NAME);
>  
> -    g_unsetenv(CPR_EXEC_STATE_NAME);
> -    assert(val);
> +    if (!val) {
> +        return;
> +    }
>      assert(!qemu_strtoi(val, NULL, 10, &mfd));
> +    g_unsetenv(CPR_EXEC_STATE_NAME);
>      close(mfd);
>  }

[Severity: Low]
This isn't a bug, but the same reorder is made here in
cpr_exec_unpersist_state(), and the commit message doesn't mention it
either.

Could the commit message cover both g_unsetenv() moves?

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=6

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 07/35] migration/cpr-exec: don't leak the dup'd memfd if g_setenv() fails
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 07/35] migration/cpr-exec: don't leak the dup'd memfd if g_setenv() fails Andrey Drobyshev
@ 2026-11-03 14:04   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:04 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core; +Cc: andrey.drobyshev

> cpr_exec_persist_state() dups the CPR state memfd for the exec and
> records it in the environment.  If g_setenv() fails the dup isn't
> closed, and the leak is inherited across exec.  The dup() result isn't
> checked either.
> 
> Close the dup on that path and fail on a failed dup().
> 
> Fixes: a3eae205c60 ("migration: cpr-exec mode")
> Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=7


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 08/35] migration: don't refuse a blocker over a migration of another mode
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 08/35] migration: don't refuse a blocker over a migration of another mode Andrey Drobyshev
@ 2026-11-03 14:05   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:05 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core; +Cc: andrey.drobyshev

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] migration: is_busy() keys off the runtime-mutable migrate_mode() parameter
--

commit 0bf28cc9f7ab6b39aca902a416aa850e785856d5
Author: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

migration: don't refuse a blocker over a migration of another mode

This patch changes is_busy() so that a running migration only causes a
migration blocker to be refused when the blocker covers the mode of that
migration. Snapshots (RUN_STATE_SAVE_VM) are still checked as before.

> diff --git a/migration/migration.c b/migration/migration.c
> index 7c2fd704890e..d5a21da3129a 100644
> --- a/migration/migration.c
> +++ b/migration/migration.c

[ ... ]

> -static bool is_busy(Error **reasonp, Error **errp)
> +static bool is_busy(Error **reasonp, Error **errp, int modes)
>  {
>      ERRP_GUARD();
>  
> -    /* Snapshots are similar to migrations, so check RUN_STATE_SAVE_VM too. */
> -    if (runstate_check(RUN_STATE_SAVE_VM) || migration_is_running()) {
> +    /*
> +     * Snapshots are similar to migrations, so check RUN_STATE_SAVE_VM too.
> +     * A running migration only matters if the blocker applies to its mode.
> +     */
> +    if (runstate_check(RUN_STATE_SAVE_VM) ||
> +        (migration_is_running() && (modes & BIT(migrate_mode())))) {

[Severity: Low]
Can this check be fooled by a mode change while a migration is running?

is_busy() now takes the mode from migrate_mode(), which returns
migrate_get_current()->parameters.mode unless cpr_get_incoming_mode() is
set. That is the current user-settable parameter. It is not necessarily the
mode the running migration was started with.

migrate_params_apply() in migration/options.c copies the mode whenever
has_mode is set:

    if (params->has_mode) {
        s->parameters.mode = params->mode;
    }

Unlike the capabilities path, this has no migration_is_running() guard.

Suppose a migration of mode X is running and migrate-set-parameters then
changes the mode to Y. A caller of migrate_add_blocker_modes() whose modes
mask contains X but not Y gets (modes & BIT(Y)) == 0. is_busy() then returns
false, and add_blockers() installs the blocker even though a migration of
mode X is in flight. Before this patch, any running migration caused -EBUSY.

Should the check use a mode recorded when the migration was started, rather
than the mutable parameter?

>          error_propagate_prepend(errp, *reasonp,
>                                  "disallowing migration blocker "
>                                  "(migration/snapshot in progress) for: ");

[ ... ]

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=8

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 09/35] migration: Plug memory leaks after migrate_set_error()
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 09/35] migration: Plug memory leaks after migrate_set_error() Andrey Drobyshev
@ 2026-11-03 14:05   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:05 UTC (permalink / raw)
  To: svt-core, Andrey Drobyshev; +Cc: andrey.drobyshev

> From: Markus Armbruster <armbru@redhat.com>
> 
> migrate_set_error(s, err) stores a copy of @err in @s.  The original
> @err is not freed.  Most callers free it immediately.  Some callers
> free it later, or pass it on.  And some leak it.  Fix those.
> 
> Perhaps migrate_set_error(s, err) should take ownership of @err.  The
> callers that free it immediately would become simpler, and avoid a
> copy and a deallocation.  The others would have to pass
> error_copy(err).
> 
> andrey.drobyshev:
>     This commit is cherry-picked as-is, with no modifications, from the
>     upstream commit 93817ec39656.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> Link: https://lore.kernel.org/r/20251115083500.2753895-2-armbru@redhat.com
> Signed-off-by: Peter Xu <peterx@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=9


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 10/35] hw/vfio: Fix liveness check in vfio_connect_kvm_msi_virq()
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 10/35] hw/vfio: Fix liveness check in vfio_connect_kvm_msi_virq() Andrey Drobyshev
@ 2026-11-03 14:05   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:05 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core; +Cc: andrey.drobyshev

> From: "Tycho Andersen (AMD)" <tycho@kernel.org>
> 
> While working on savevm/loadvm for a new vfio device, I encountered the
> crash below. Since vfio_connect_kvm_msi_virq() didn't check the ->use flag
> for the vector, it would pass an unused vector down to
> vfio_cpr_load_vector_fd() which would crash.
> 
> Fix this by checking the ->use flag along with the virq number to detect
> whether a vector is valid or not.
> 
> Thread 1 "qemu-system-x86" received signal SIGSEGV, Segmentation fault.
> 0x0000555555a891ef in vfio_cpr_load_vector_fd (vdev=vdev@entry=0x0,
>     name=name@entry=0x555555eeb27e "kvm_interrupt", nr=nr@entry=1) at ../hw/vfio/cpr.c:44
> 44	    g_autofree char *fdname = STRDUP_VECTOR_FD_NAME(vdev, name);
> (gdb) bt
> [ ... ]
> Fixes: 30edcb4d4e7a ("vfio-pci: preserve MSI")
> Signed-off-by: Tycho Andersen (AMD) <tycho@kernel.org>
> Reviewed-by: Cédric Le Goater <clg@redhat.com>
> Link: https://lore.kernel.org/qemu-devel/20260727150038.2684512-1-tycho@kernel.org
> Signed-off-by: Cédric Le Goater <clg@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=10


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 11/35] vfio/pci: Reject invalid MSI-X Table and PBA BIR values
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 11/35] vfio/pci: Reject invalid MSI-X Table and PBA BIR values Andrey Drobyshev
@ 2026-11-03 14:05   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:05 UTC (permalink / raw)
  To: svt-core, Andrey Drobyshev; +Cc: andrey.drobyshev

> From: Cédric Le Goater <clg@redhat.com>
> 
> The 3-bit MSI-X BIR fields permit values 0-7, but VFIOPCIDevice::bars[]
> only contains BAR0-BAR5 (6 entries). An invalid BIR value of 6 or 7
> can cause an out-of-bounds array access (CWE-129) in vfio_msix_early_setup().
> 
> Add range checks immediately after extracting the BIR values.
> 
> andrey.drobyshev:
>     This commit is cherry-picked as-is, with no modifications, from the
>     upstream commit 9daa7347ff25.
> 
> Reported-by: Feifan Qian <bea1e@proton.me>
> Fixes: 65501a745dba ("vfio: vfio-pci device assignment driver")
> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3878
> Reviewed-by: Alex Williamson <alex@shazbot.org>
> Link: https://lore.kernel.org/qemu-devel/20260706161334.2165482-1-clg@redhat.com
> Signed-off-by: Cédric Le Goater <clg@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=11


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 12/35] vfio/pci: Fix information leak in vfio_rom_read()
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 12/35] vfio/pci: Fix information leak in vfio_rom_read() Andrey Drobyshev
@ 2026-11-03 14:06   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:06 UTC (permalink / raw)
  To: svt-core, Andrey Drobyshev; +Cc: andrey.drobyshev

> From: Cédric Le Goater <clg@redhat.com>
> 
> vfio_rom_read() can leak uninitialized stack data to the guest when a
> read occurs at the end boundary of the ROM BAR.
> 
> Fix this by initializing the val union to 0xff, matching the ROM
> initialization pattern used in vfio_pci_load_rom().
> 
> andrey.drobyshev:
>     This commit is cherry-picked as-is, with no modifications, from the
>     upstream commit 402fd5a75659.
> 
> Reviewed-by: Philippe Mathieu-Daudé <philmd@mailo.com>
> Reviewed-by: Mario Casquero <mcasquer@redhat.com>
> Link: https://lore.kernel.org/qemu-devel/20260602074446.1206892-1-clg@redhat.com
> Signed-off-by: Cédric Le Goater <clg@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=12


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 14/35] vfio/pci: Grow buffer in vfio_pci_host_match()
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 14/35] vfio/pci: Grow buffer in vfio_pci_host_match() Andrey Drobyshev
@ 2026-11-03 14:06   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:06 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core; +Cc: andrey.drobyshev

> From: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
> 
> Each field of PCIHostDeviceAddress is an unsigned int, therefore
> while a valid address is limited to 13 characters, an invalid
> address could exceed the specified format, up to:
> 
>         ffffffff:ffffffff:ffffffff.ffffffff<NUL>
> 
> This requires 36 characters with the terminator.
> 
> andrey.drobyshev:
>     This commit is cherry-picked as-is, with no modifications, from the
>     upstream commit 1785bf9685ec.
> 
> Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
> Reviewed-by: Alex Williamson <alex.williamson@nvidia.com>
> Message-ID: <20260305-nvme-v4-2-b65b9de1839f@rsg.ci.i.u-tokyo.ac.jp>
> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=14


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 13/35] vfio/pci: Initialize rom_read_failed in vfio_pci_load_rom()
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 13/35] vfio/pci: Initialize rom_read_failed in vfio_pci_load_rom() Andrey Drobyshev
@ 2026-11-03 14:06   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:06 UTC (permalink / raw)
  To: svt-core, Andrey Drobyshev; +Cc: andrey.drobyshev

> From: Mario Casquero <mcasquer@redhat.com>
> 
> When vfio_device_get_region_info() fails in vfio_pci_load_rom(),
> the function returns without setting vdev->rom_read_failed to true,
> and without allocating vdev->rom. This leaves vdev->rom as NULL.
> 
> andrey.drobyshev:
>     This commit is cherry-picked as-is, with no modifications, from the
>     upstream commit c822ecd80661.
> 
> Signed-off-by: Mario Casquero <mcasquer@redhat.com>
> Reviewed-by: Cédric Le Goater <clg@redhat.com>
> Link: https://lore.kernel.org/qemu-devel/20260602070857.356526-1-mcasquer@redhat.com
> Signed-off-by: Cédric Le Goater <clg@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=13


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 16/35] migration: ensure APIC is loaded prior to VFIO PCI devices
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 16/35] migration: ensure APIC is loaded prior to VFIO PCI devices Andrey Drobyshev
@ 2026-11-03 14:08   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:08 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core; +Cc: andrey.drobyshev

> From: Yanfei Xu <yanfei.xu@bytedance.com>
> 
> The load procedure of VFIO PCI devices involves setting up IRT
> for each VFIO PCI devices. This requires determining whether an
> interrupt is single-destination interrupt to decide between
> Posted Interrupt(PI) or remapping mode for the IRTE. However,
> determining this may require accessing the VM's APIC registers.
> 
> For example:
> ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irqs)
>   ...
>     kvm_arch_irq_bypass_add_producer
>       kvm_x86_call(pi_update_irte)
>         vmx_pi_update_irte
>           kvm_intr_is_single_vcpu
> 
> If the LAPIC has not been loaded yet, interrupts will use remapping
> mode. To prevent the fallback of interrupt mode, keep APIC is always
> loaded prior to VFIO PCI devices.
> 
> andrey.drobyshev:
>     This commit is cherry-picked as-is, with no modifications, from the
>     upstream commit d943cef76090.
> 
> Signed-off-by: Yicong Shen <shenyicong.1023@bytedance.com>
> Signed-off-by: Yanfei Xu <yanfei.xu@bytedance.com>
> Reviewed-by: Peter Xu <peterx@redhat.com>
> Link: https://lore.kernel.org/r/20250818131127.1021648-1-yanfei.xu@bytedance.com
> Signed-off-by: Peter Xu <peterx@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=16


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 15/35] q35: Fix migration of SMRAM state
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 15/35] q35: Fix migration of SMRAM state Andrey Drobyshev
@ 2026-11-03 14:08   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:08 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core; +Cc: andrey.drobyshev

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] q35: mch_update_smbase_smram() skips region resync when SMBASE reg is 0x01
- [Low] q35: SMBASE register accepts junk, direct 0x01 and lock writes without 0xff negotiation
--

commit 1956c6383a778aa029b7ffec3205e273dc1764cf
Author: Igor Mammedov <imammedo@redhat.com>

q35: Fix migration of SMRAM state

This commit stops mch_update_smbase_smram() from clearing the migrated
SMBASE register value. wmask is now derived from the register value, so a
locked state survives migration. The test hunk that checked junk writes
before negotiation is removed.

Link: https://patchew.org/QEMU/20251203180851.6390-1-arbn@yandex-team.com/

> diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
> --- a/hw/pci-host/q35.c
> +++ b/hw/pci-host/q35.c

[ ... ]

> @@ -432,30 +432,27 @@ static void mch_update_smbase_smram(MCHPCIState *mch)
>      }
>  
>      if (*reg == MCH_HOST_BRIDGE_F_SMBASE_QUERY) {
> -        pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] =
> -            MCH_HOST_BRIDGE_F_SMBASE_LCK;
> +        pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] = MCH_HOST_BRIDGE_F_SMBASE_LCK;
>          *reg = MCH_HOST_BRIDGE_F_SMBASE_IN_RAM;
>          return;
>      }
>  
> -    /*
> -     * default/reset state, discard written value
> -     * which will disable SMRAM balackhole at SMBASE
> -     */
> -    if (pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] == 0xff) {
> -        *reg = 0x00;
> +    /*
> +     * reg value can come from register write/reset/migration source,
> +     * update wmask to be in sync with it regardless of source
> +     */
> +    if (*reg == MCH_HOST_BRIDGE_F_SMBASE_IN_RAM) {
> +        pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] = MCH_HOST_BRIDGE_F_SMBASE_LCK;
> +        return;
>      }

[Severity: Medium]
Can this early return leave the SMBASE regions enabled when the register
becomes 0x01?

The return skips the memory_region_set_enabled() calls for smbase_blackhole
and smbase_window. Before the patch, a value of 0x01 fell through and
disabled both regions.

The path is mch_post_load()->mch_update()->mch_update_smbase_smram(). On a
live COLO secondary, no reset precedes it:

migration/colo.c:colo_incoming_thread()
    ret = qemu_load_device_state(fb);

Consider this sequence:

- The secondary holds reg=0x02 with both regions enabled.
- The primary reboots, and the guest renegotiates to 0x01 within one
  checkpoint interval.
- The next checkpoint loads 0x01.

post_load then sets wmask and returns, so the regions stay enabled while
the register reads 0x01 (negotiated, not locked). Guest-visible SMRAM at
SMBASE then disagrees with the register.

Should the IN_RAM branch also disable the two regions before returning?

The snapshot-load path is not affected, because load_snapshot() calls
qemu_system_reset() before qemu_loadvm_state(). A fresh incoming migration
is also not affected, because the regions start disabled.

> -
> -    memory_region_transaction_begin();
>      if (*reg & MCH_HOST_BRIDGE_F_SMBASE_LCK) {
> -        /* disable all writes */
> -        pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] &=
> -            ~MCH_HOST_BRIDGE_F_SMBASE_LCK;
> +        /* lock register at 0x2 and disable all writes */
> +        pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] = 0;
>          *reg = MCH_HOST_BRIDGE_F_SMBASE_LCK;
> -        lck = true;
> -    } else {
> -        lck = false;
>      }

[Severity: Low]
Is it intended that mch_update_smbase_smram() no longer has a "not yet
negotiated" state?

In the reset state (reg=0, wmask=0xff), a guest config write to the SMBASE
register now behaves differently, via mch_write_config()->mch_update():

- A value with bit 0x2 set, such as 0x03 or 0x06, takes the "*reg &
  MCH_HOST_BRIDGE_F_SMBASE_LCK" branch. It sets wmask to 0, stores 0x02,
  and enables smbase_blackhole and smbase_window. The guest never saw the
  0x01 reply.
- A write of exactly 0x01 takes the IN_RAM branch and sets wmask to
  MCH_HOST_BRIDGE_F_SMBASE_LCK without a prior 0xff query.
- Other values, such as 0x04, are stored and read back instead of
  reading 0.

The commit message mentions the last case ("let guest write junk values").
It does not mention the first two.

Could the commit message describe the lock-without-negotiation and direct
0x01 cases as well?

> diff --git a/tests/qtest/q35-test.c b/tests/qtest/q35-test.c
> --- a/tests/qtest/q35-test.c
> +++ b/tests/qtest/q35-test.c

[ ... ]

> @@ -206,12 +206,6 @@ static void test_smram_smbase_lock(void)
>      qtest_writeb(qts, SMBASE, SMRAM_TEST_PATTERN);
>      g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_PATTERN);
>  
> -    /* check that writing junk to 0x9c before before negotiating is ignored */
> -    for (i = 0; i < 0xff; i++) {
> -        qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, i);
> -        g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0);
> -    }
> -
>      /* enable SMRAM at SMBASE */
>      qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, 0xff);
>      g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0x01);

[Severity: Low]
This isn't a bug, but the removed junk-write check is not replaced by any
test of the new behavior. Would it be worth adding coverage for the new
pre-negotiation write semantics and for the migrated register state
(locked and negotiated) in test_smram_smbase_lock()?

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=15

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 17/35] migration: Fix regression of passing error_fatal into vmstate_load_state()
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 17/35] migration: Fix regression of passing error_fatal into vmstate_load_state() Andrey Drobyshev
@ 2026-11-03 14:11   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:11 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core; +Cc: andrey.drobyshev

> From: Arun Menon <armenon@redhat.com>
> 
> error_fatal is passed to vmstate_load_state() and vmstate_save_state()
> functions. This was introduced in commit c632ffbd74. This would exit(1)
> on error, and therefore does not allow to propagate the error back to
> the caller.
> 
> To maintain consistency with prior error handling i.e. either propagating
> the error to the caller or reporting it, we must set the error within a
> local Error object instead of using error_fatal.
> 
> Reviewed-by: Cornelia Huck <cohuck@redhat.com>
> Signed-off-by: Arun Menon <armenon@redhat.com>
> Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
> Link: https://lore.kernel.org/r/20251028-solve_error_fatal_regression-v2-1-dab24c808a28@redhat.com
> [peterx: always uninit var ret, per Akihiko]
> [peterx: touchups on line ordering, spacings etc.]
> 
> andrey.drobyshev:
>     This commit was cherry-picked from the upstream commit 986c3292c6b8,
>     adapted for the hci-dev base, which predates the upstream commit
>     bcb536cabe10 ("error: Kill @error_warn"): virtio_gpu_load() has no
>     'err' local there yet, so it is declared here.
> 
> Signed-off-by: Peter Xu <peterx@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=17


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 18/35] migration: vmstate_save_state_v: fix double error_setg
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 18/35] migration: vmstate_save_state_v: fix double error_setg Andrey Drobyshev
@ 2026-11-03 14:11   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:11 UTC (permalink / raw)
  To: svt-core, Andrey Drobyshev; +Cc: andrey.drobyshev

> From: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
> 
> We may call error_setg twice on same errp if inner
> vmstate_save_state_v() or vmstate_save_state() call fails. Next we will
> crash on assertion in error_setv().
> 
> andrey.drobyshev:
>     This commit is cherry-picked as-is, with no modifications, from the
>     upstream commit d41ce10d0f5a.
> 
> Fixes: 848a0503422d043 "migration: Update error description outside migration.c"
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
> Reviewed-by: Fabiano Rosas <farosas@suse.de>
> Reviewed-by: Peter Xu <peterx@redhat.com>
> Link: https://lore.kernel.org/qemu-devel/20260304212303.667141-2-vsementsov@yandex-team.ru
> Signed-off-by: Fabiano Rosas <farosas@suse.de>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=18


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 20/35] migration: Free cpr-transfer MigrationAddress along with gsource
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 20/35] migration: Free cpr-transfer MigrationAddress along with gsource Andrey Drobyshev
@ 2026-11-03 14:14   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:14 UTC (permalink / raw)
  To: svt-core, Andrey Drobyshev; +Cc: andrey.drobyshev

> From: Fabiano Rosas <farosas@suse.de>
> 
> When setting a callback on a Glib source and giving it a data pointer,
> it's natural to also provide the destructor for the data in question.
> 
> Since migrate_hup_add() already needs to clone the MigrationAddress
> when setting the qmp_migrate_finish_cb callback, also pass the
> qapi_free_MigrationAddress as the GDestroyNotify callback.
> 
> With this the address doesn't need to be freed at the callback body,
> making the management of that memory slightly simpler.
> 
> andrey.drobyshev:
>     This commit was cherry-picked from the upstream commit 6a887119ce18,
>     adapted for the hci-dev base: qmp_migrate_finish_cb() here still
>     closes the cpr state and deletes the HUP source itself (upstream had
>     already moved that into migration_cleanup()), so only the explicit
>     qapi_free_MigrationAddress() call goes away from it.
> 
> Cc: Mark Kanda <mark.kanda@oracle.com>
> Cc: Ben Chaney <bchaney@akamai.com>
> Reviewed-by: Peter Xu <peterx@redhat.com>
> Reviewed-by: Prasad Pandit <pjp@fedoraproject.org>
> Link: https://lore.kernel.org/qemu-devel/20260123141656.6765-23-farosas@suse.de
> Signed-off-by: Fabiano Rosas <farosas@suse.de>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=20


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 21/35] migration: Move CPR HUP watch to cpr-transfer.c
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 21/35] migration: Move CPR HUP watch to cpr-transfer.c Andrey Drobyshev
@ 2026-11-03 14:14   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:14 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core; +Cc: andrey.drobyshev

> From: Fabiano Rosas <farosas@suse.de>
> 
> Move this CPR-specific code into a cpr file. While here, give the
> functions more significant names.
> 
> This makes the new idea (after cpr-transfer) of having two parts to
> qmp_migrate slightly more obvious: either wait for the hangup or
> continue directly.
> 
> andrey.drobyshev:
>     This commit was cherry-picked from the upstream commit f523e5aa5f56,
>     adapted for the hci-dev base, which predates the upstream qmp_migrate()
>     rework around MigrationChannel: the watch is still armed with the
>     plain MigrationAddress from qmp_migrate() rather than main_ch->addr,
>     and qmp_migrate_finish_cb() keeps closing the cpr state and destroying
>     the HUP source itself.
> 
> Reviewed-by: Peter Xu <peterx@redhat.com>
> Reviewed-by: Prasad Pandit <pjp@fedoraproject.org>
> Link: https://lore.kernel.org/qemu-devel/20260123141656.6765-24-farosas@suse.de
> Signed-off-by: Fabiano Rosas <farosas@suse.de>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=21


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 19/35] migration: yank: Move register instance earlier
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 19/35] migration: yank: Move register instance earlier Andrey Drobyshev
@ 2026-11-03 14:16   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:16 UTC (permalink / raw)
  To: svt-core, Andrey Drobyshev; +Cc: andrey.drobyshev

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] migration: qmp_migrate() error path after migrate_init() leaks s->vmdesc
--

commit 1e8fe270d4e6c35191003521d0feb94edc80e4c3
Author: Fabiano Rosas <farosas@suse.de>

migration: yank: Move register instance earlier

This patch moves yank_register_instance() from qmp_migrate_finish() into
migrate_prepare(), so it is registered before cpr_state_save(). It also
adds unregister calls on the qmp_migrate() out: path and in
qmp_migrate_finish() failure handling, and refuses migrate during
incoming postcopy.

Link: https://lore.kernel.org/qemu-devel/20260123141656.6765-11-farosas@suse.de

> diff --git a/migration/migration.c b/migration/migration.c
> --- a/migration/migration.c
> +++ b/migration/migration.c

[ ... ]

>  out:
>      if (local_err) {
> +        /* A resume didn't register it, see migrate_prepare() */
> +        if (s->state != MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP) {
> +            yank_unregister_instance(MIGRATION_YANK_INSTANCE);
> +        }
>          migration_connect_set_error(s, local_err);
>          error_propagate(errp, local_err);
>      }
>  }

[Severity: Low]
Can this error path leak s->vmdesc?

In qmp_migrate(), migrate_prepare() calls migrate_init(), which does the
following when should_send_vmdesc() is true:

    if (should_send_vmdesc()) {
        s->vmdesc = json_writer_new(false);
    }

If cpr_state_save() then fails and control reaches the out: label, only
the yank instance is unregistered and migration_connect_set_error() is
called, which just updates the state and the error.

migration_cleanup() is never called from here, and it is the only caller
of migration_cleanup_json_writer() that frees s->vmdesc after a failed
setup.

The next migrate_init() then assigns a new json_writer_new() to
s->vmdesc without freeing the old one, so the old writer would be leaked.

The same shape appears in qmp_migrate_finish() when the connect fails,
since that path also ends at migration_connect_set_error() without
running migration_cleanup().

The commit message says the goal is to pair the registration with
migration_cleanup(), but this out: block still does not free s->vmdesc.
Should the failure paths release it, for example via
migration_cleanup_json_writer()?

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=19

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 22/35] migration: Fix crash on second migration when cancel early
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 22/35] migration: Fix crash on second migration when cancel early Andrey Drobyshev
@ 2026-11-03 14:16   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:16 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core; +Cc: andrey.drobyshev

> From: Peter Xu <peterx@redhat.com>
> 
> Marc-André reported an issue on QEMU crash when retrying a cancelled
> migration during early setup phase, see "Link:" for more information, and
> also easy way to reproduce.
> 
> This patch is a replacement of the prior fix proposed by not only switching
> to migration_cleanup(), but also fixing it from CPR side, so that we track
> hup_source properly to know if src QEMU is waiting or the HUP signal.
> 
> To put it simple: this chunk of special casing in migration_cancel() should
> not affect normal migration, but only cpr-transfer migration to cover the
> small window when the src QEMU is waiting for a HUP signal on cpr
> channel (so that src QEMU can continue the migration on the main channel).
> 
> [ ... ]
> Reported-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Tested-by: Fabiano Rosas <farosas@suse.de>
> Reviewed-by: Fabiano Rosas <farosas@suse.de>
> Link: https://lore.kernel.org/r/20260421175820.302795-1-peterx@redhat.com
> Signed-off-by: Peter Xu <peterx@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=22


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 24/35] kvm: fix coalesced MMIO flush racing with kvm_close()
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 24/35] kvm: fix coalesced MMIO flush racing with kvm_close() Andrey Drobyshev
@ 2026-11-03 14:19   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:19 UTC (permalink / raw)
  To: svt-core, Andrey Drobyshev; +Cc: andrey.drobyshev

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] kvm: lockless coalesced_mmio_ring cleared with plain store racing with plain-load flush
--

commit a37b91db7da68f58e8daa300e29ffcbb6cb539f1
Author: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

kvm: fix coalesced MMIO flush racing with kvm_close()

This patch clears kvm_state->coalesced_mmio_ring and calls
drain_call_rcu() at the start of kvm_close(), before the vCPUs are
destroyed. The aim is to stop the RCU thread from flushing the coalesced
MMIO ring after the vCPU kvm_run area has been unmapped.

> diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
> --- a/accel/kvm/kvm-all.c
> +++ b/accel/kvm/kvm-all.c

[ ... ]

> @@ -630,6 +630,14 @@ void kvm_close(void)
>          return;
>      }
>  
> +    /*
> +     * Destroying vCPUs unmaps the coalesced MMIO ring, while the RCU thread
> +     * may be flushing it from a memory region finalizer.  Stop new flushes
> +     * and wait for the pending RCU callbacks first.
> +     */
> +    kvm_state->coalesced_mmio_ring = NULL;
> +    drain_call_rcu();
> +

[Severity: Medium]
Can this plain store race with the lockless reader in
kvm_flush_coalesced_mmio_buffer()?  That function does:

    if (s->coalesced_mmio_ring) {
        struct kvm_coalesced_mmio_ring *ring = s->coalesced_mmio_ring;
        while (ring->first != ring->last) {

The check and the assignment to ring are two separate plain loads, with
no qatomic_read() or READ_ONCE().  If the RCU thread runs
flatview_destroy() -> memory_region_finalize() ->
memory_region_transaction_begin() -> qemu_flush_coalesced_mmio_buffer()
-> kvm_flush_coalesced_mmio_buffer() while the main thread stores NULL
here, the store could land between the test and the reload.  Then ring
would be NULL and ring->first would dereference it.

The only guard in the flush function is the plain bool
coalesced_flush_in_progress, and the RCU thread does not take the BQL, so
nothing orders the two sides.

drain_call_rcu() runs only after the store.  It covers a reader that
already holds a non-NULL pointer, but it does not cover a reader that
reloads NULL.  The finding is speculative, since compilers normally merge
the two loads, but it is a data race in C terms.

The same plain-store pattern exists in do_kvm_destroy_vcpu():

    if (s->coalesced_mmio_ring == (void *)cpu->kvm_run + PAGE_SIZE) {
        s->coalesced_mmio_ring = NULL;
    }

Would it be better to use qatomic_set() for the stores in kvm_close() and
do_kvm_destroy_vcpu(), and a single qatomic_read() into a local variable
in kvm_flush_coalesced_mmio_buffer()?  Alternatively, could drain_call_rcu()
be called first and the pointer cleared afterwards?

>      CPU_FOREACH(cpu) {
>          cpu_remove_sync(cpu);
>          close(cpu->kvm_fd);

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=24

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 25/35] migration/cpr: add cpr_is_in_progress()
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 25/35] migration/cpr: add cpr_is_in_progress() Andrey Drobyshev
@ 2026-11-03 14:21   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:21 UTC (permalink / raw)
  To: svt-core, Andrey Drobyshev; +Cc: andrey.drobyshev

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] migration/cpr: cpr_is_in_progress() false on cpr-transfer source after HUP closes cpr_state_file
--

commit e23ec35308ad2aa64ca670bb065479111e90c011
Author: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

migration/cpr: add cpr_is_in_progress()

This adds cpr_is_in_progress(). It returns true on the target until the
incoming migration is done. On the source it returns true only while the
outgoing CPR migration runs, which it detects by checking whether
cpr_state_file is open.

> diff --git a/migration/cpr.c b/migration/cpr.c
> index a149e790c1035..3ea5d86bab483 100644
> --- a/migration/cpr.c
> +++ b/migration/cpr.c

[ ... ]

> @@ -549,6 +549,28 @@ bool cpr_exec_is_incoming(void)
>      return incoming_mode == MIG_MODE_CPR_EXEC;
>  }
>  
> +bool cpr_is_in_progress(void)
> +{
> +    /* The incoming mode on the target, the 'mode' parameter on the source */
> +    MigMode mode = migrate_mode();
> +
> +    if (mode != MIG_MODE_CPR_TRANSFER && mode != MIG_MODE_CPR_EXEC) {
> +        return false;
> +    }
> +
> +    /* Target: the incoming mode is set until the incoming migration is done */
> +    if (cpr_is_incoming()) {
> +        return true;
> +    }
> +
> +    /*
> +     * Source: the parameter alone means nothing, it may just have been set.
> +     * An outgoing CPR migration has the CPR state open, from
> +     * cpr_state_save() to cpr_state_close().
> +     */
> +    return cpr_state_file != NULL;
> +}

[Severity: High]
Is cpr_state_file a reliable signal for "the outgoing migration is
running" in cpr-transfer mode? The commit message says the function is
true on the source "only while the outgoing one runs", but the comment
ties it to the lifetime of the CPR state file instead.

In cpr-transfer mode, qmp_migrate() calls cpr_state_save(), which sets
cpr_state_file, and then adds a HUP watch:

    cpr_transfer_add_hup_watch(s, qmp_migrate_finish_cb, addr)

When the target closes its socket after loading the CPR state, the
callback runs:

migration/migration.c:qmp_migrate_finish_cb() {
    ...
    qmp_migrate_finish(addr, NULL);

    cpr_state_close();
}

cpr_state_close() calls qemu_fclose() and sets cpr_state_file to NULL.
qmp_migrate_finish() only starts the outgoing connection. The migration
thread, RAM transfer, vm_stop and the device state save all run after
this point.

From then on, on a cpr-transfer source, migrate_mode() is still
MIG_MODE_CPR_TRANSFER and cpr_is_incoming() is false. cpr_state_file is
NULL, so cpr_is_in_progress() returns false for most of the outgoing
migration.

Would callers that depend on this then take the normal live-migration
path? In the later VFIO changes in hw/vfio/migration.c:

    vfio_vmstate_change():
        /* CPR source only: the device keeps running across the exec */
        if (cpr_is_in_progress()) {
            return;
        }

    vfio_is_active():
        return !cpr_is_in_progress();

If the guard does not trigger at vm_stop time, the preserved VFIO device
could be put into STOP or STOP_COPY. vfio_is_active() would also return
true, so the VFIO device state would be saved and migrated.

cpr-exec appears unaffected, since it keeps the file open until
migration_cleanup(). Could the source-side check use a state that lasts
until the outgoing migration finishes, for example the migration state,
instead of cpr_state_file?

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=25

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 23/35] kvm: tolerate ioctls after kvm_close()
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 23/35] kvm: tolerate ioctls after kvm_close() Andrey Drobyshev
@ 2026-11-03 14:21   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:21 UTC (permalink / raw)
  To: svt-core, Andrey Drobyshev; +Cc: andrey.drobyshev

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Medium] kvm: kvm_vm_ioctl() returns 0 after kvm_close() for fd-returning ioctls, yielding fd 0
- [Medium] kvm: ioctl wrappers return 0 after kvm_close() without filling outputs
- [Medium] kvm: ioctl wrappers return 0 after kvm_close(), callers treat it as a valid fd
- [Low] kvm: closed-state checks in ioctl wrappers race with kvm_close()
--

commit e7e22762666ee0142be58442e3f90a44a9a53d12
Author: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

kvm: tolerate ioctls after kvm_close()

This patch keeps kvm_state allocated after kvm_close() instead of setting
it to NULL. It makes kvm_ioctl(), kvm_vm_ioctl() and kvm_vcpu_ioctl()
return 0 once the file descriptors have been closed. The aim is to stop a
retired cpr-transfer source from crashing on late interrupts.

> diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
> --- a/accel/kvm/kvm-all.c
> +++ b/accel/kvm/kvm-all.c

[ ... ]

> @@ -644,7 +644,6 @@ void kvm_close(void)
>          close(kvm_state->fd);
>          kvm_state->fd = -1;
>      }
> -    kvm_state = NULL;
>  }

[Severity: Low]
Can the closed-state checks added below race with kvm_close()?

kvm_close() stores -1 into kvm_state->vmfd and kvm_state->fd with plain
stores after close(). It does not call accel_ioctl_inhibit_begin(), so it
does not wait for in-flight ioctls from threads that don't hold the BQL.

A thread without the BQL could pass the new s->vmfd == -1 test in
kvm_vm_ioctl(). kvm_close() could then close the descriptor. The first
thread would re-read s->vmfd for ioctl() and either get -EBADF or issue
the ioctl on a recycled descriptor number.

kvm_set_irq() and the ioeventfd callers treat that error as fatal. Is
there any synchronization that excludes this?

Also, kvm_enabled() and kvm_dirty_ring_enabled() keep reporting a live KVM
now that kvm_state is no longer set to NULL. Only the three ioctl wrappers
check for the closed state.

> @@ -3371,6 +3370,11 @@ int kvm_ioctl(KVMState *s, unsigned long type, ...)
>      arg = va_arg(ap, void *);
>      va_end(ap);
>  
> +    /* kvm_close() has been called: there is no KVM to talk to any more */
> +    if (s->fd == -1) {
> +        return 0;
> +    }
> +

[Severity: Medium]
Is returning 0 here safe for callers that read output from the ioctl?

This stub never writes the caller's output buffers or return value.
KVM_CHECK_EXTENSION, KVM_GET_VCPU_MMAP_SIZE and KVM_GET_SUPPORTED_* all
report success with no data. kvm_check_extension() would read the 0 as
"extension unsupported".

>      trace_kvm_ioctl(type, arg);
>      ret = ioctl(s->fd, type, arg);

[ ... ]

> @@ -3389,6 +3393,11 @@ int kvm_vm_ioctl(KVMState *s, unsigned long type, ...)
>      arg = va_arg(ap, void *);
>      va_end(ap);
>  
> +    /* kvm_close() has been called: there is no VM to talk to any more */
> +    if (s->vmfd == -1) {
> +        return 0;
> +    }
> +

[Severity: Medium]
Can this return 0 be mistaken for a valid file descriptor?

Several VM ioctls return a new fd as their result: KVM_GET_STATS_FD,
KVM_CREATE_GUEST_MEMFD, KVM_CREATE_VCPU and KVM_CREATE_DEVICE (through
cd.fd). After kvm_close() these now return 0, and the callers take that as
fd 0, which is stdin.

The most reachable case is query_stats_cb():

    stats_fd = kvm_vm_ioctl(s, KVM_GET_STATS_FD, NULL);
    if (stats_fd == -1) {
        error_setg_errno(errp, errno, "KVM stats: ioctl failed");
        return;
    }
    query_stats(result, target, names, stats_fd, NULL, errp);
    close(stats_fd);

The check for -1 passes, so a QMP query-stats reads from fd 0 and then
calls close(0). query_stats_schemas_cb() has the same pattern.

Other callers:

    kvm_create_guest_memfd():
        fd = kvm_vm_ioctl(kvm_state, KVM_CREATE_GUEST_MEMFD, &guest_memfd);
        if (fd < 0) {

    kvm_create_vcpu():
        kvm_fd = kvm_vm_ioctl(s, KVM_CREATE_VCPU, vcpu_id);
        if (kvm_fd < 0) {

kvm_create_guest_memfd() accepts 0 as a guest_memfd. kvm_create_vcpu()
sets cpu->kvm_fd = 0.

In hw/vfio/helpers.c, vfio_kvm_device_add_fd() has this:

    if (kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd)) {
        ...
    }
    vfio_kvm_device_fd = cd.fd;

cd.fd is only initialised for .type, so it stays 0. vfio_kvm_device_fd
becomes 0, and a later vfio_kvm_device_close() calls close(0). It can also
be reached after vfio_kvm_device_close() has reset vfio_kvm_device_fd to
-1, because kvm_enabled() is still true after kvm_close().

openpic_kvm.c and s390_flic_kvm.c use cd.fd after a zero-initialised
KVM_CREATE_DEVICE call in the same way. kvm_create_device() sets
create_dev.fd = -1, but it would still return success with -1 as the fd.

Before this patch these calls dereferenced a NULL kvm_state. Should the
fd-returning requests return an error instead?

Also, for requests that fill an output struct, KVM_GET_CLOCK,
KVM_GET_IRQCHIP, KVM_GET_PIT2 and KVM_GET_DIRTY_LOG return success with
the buffer untouched. Can kvm_update_clock() in hw/i386/kvm/clock.c then
read data.clock from an uninitialised stack struct? Can kvm_pic_get() and
kvm_ioapic_get() copy uninitialised stack data into device state?
kvm_get_current_ns() in target/i386/kvm/xen-emu.c has the same pattern as
kvm_update_clock().

The commit message justifies the 0 only for the interrupt, irqfd and
ioeventfd callers. Does it cover callers that use the return value as a
handle or read the output buffer?

>      trace_kvm_vm_ioctl(type, arg);
>      accel_ioctl_begin();
>      ret = ioctl(s->vmfd, type, arg);

[ ... ]

> @@ -3409,6 +3418,11 @@ int kvm_vcpu_ioctl(CPUState *cpu, unsigned long type, ...)
>      arg = va_arg(ap, void *);
>      va_end(ap);
>  
> +    /* kvm_close() has been called: the vCPU fds are gone */
> +    if (cpu->kvm_fd == -1) {
> +        return 0;
> +    }
> +

[Severity: Medium]
Does the same issue apply to kvm_vcpu_ioctl()?

KVM_GET_STATS_FD on a closed vCPU returns 0, and kvm_init_vcpu() stores
that result in cpu->kvm_vcpu_stats_fd. The vCPU GET_* ioctls also return
success without writing any data. KVM_RUN returns without running.

If kvm_get_msrs() were reached through a stale CPUState, it would hit
assert(ret == nmsrs). cpu_remove_sync() takes the CPUs off CPU_FOREACH in
kvm_close(), so this is probably only reachable through a stale CPUState
pointer.

>      trace_kvm_vcpu_ioctl(cpu->cpu_index, type, arg);
>      accel_cpu_ioctl_begin(cpu);
>      ret = ioctl(cpu->kvm_fd, type, arg);

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=23

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 27/35] vfio/migration: don't assume the device is running on CPR incoming
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 27/35] vfio/migration: don't assume the device is running on CPR incoming Andrey Drobyshev
@ 2026-11-03 14:25   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:25 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core; +Cc: andrey.drobyshev

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] vfio/migration: CPR target caches STOP for a device the source left RUNNING
--

commit f6418f1061a535eaf9359082700c745eaca13dbc
Author: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

vfio/migration: don't assume the device is running on CPR incoming

This patch changes the CPR target so that vfio_migration_init() caches
STOP instead of RUNNING for the device. The VM state handler then
updates the cached state to RUNNING or STOP when the target VM starts
or stays paused.

> diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
> index b142c9f228911..04d0de7cef0ac 100644
> --- a/hw/vfio/migration.c
> +++ b/hw/vfio/migration.c

[ ... ]

> @@ -914,6 +914,14 @@ static void vfio_vmstate_change(void *opaque, bool running, RunState state)
>      Error *local_err = NULL;
>      int ret;
>  
> +    /* CPR target only: the device kept its state, note where the VM left it */
> +    if (cpr_is_incoming()) {
> +        vbasedev->migration->device_state = running ?
> +            VFIO_DEVICE_STATE_RUNNING : VFIO_DEVICE_STATE_STOP;
> +        return;
> +    }
> +
> +    /* CPR source only: the device keeps running across the exec */
>      if (cpr_is_in_progress()) {
>          return;
>      }

[ ... ]

> @@ -1039,7 +1047,9 @@ static int vfio_migration_init(VFIODevice *vbasedev)
>      vbasedev->migration = g_new0(VFIOMigration, 1);
>      migration = vbasedev->migration;
>      migration->vbasedev = vbasedev;
> -    migration->device_state = VFIO_DEVICE_STATE_RUNNING;
> +    /* A device inherited across CPR is in STOP unless the VM gets started */
> +    migration->device_state = cpr_is_incoming() ? VFIO_DEVICE_STATE_STOP :
> +                                                  VFIO_DEVICE_STATE_RUNNING;

[Severity: Medium]
Can this cache a wrong state when the CPR source VM was running?

On the source, migration_stop_vm() calls
vm_stop_force_state(RUN_STATE_FINISH_MIGRATE). Since
cpr_is_in_progress() is true, both vfio_vmstate_change() and
vfio_vmstate_change_prepare() return early, so the device stays
RUNNING in the kernel.

On the target, vfio_migration_init() runs while cpr_is_incoming() is
true and caches STOP unconditionally.

The cache is corrected only if vfio_vmstate_change() runs. That
requires vm_start() to be called while the incoming mode is still set.
In process_incoming_migration_bh() the paused path does not run VM
change-state handlers:

migration/migration.c:process_incoming_migration_bh() {
    ...
        } else {
            runstate_set(RUN_STATE_PAUSED);
        }
    ...
}

This path is taken with -S, with autostart off, or when
migration_block_activate() fails. In those cases the new branch in
vfio_vmstate_change() never runs. The cache stays STOP while the device
is RUNNING in the kernel.

Once migration_incoming_state_destroy() clears the incoming mode, later
transitions rely on that cache. In vfio_migration_set_state():

    if (new_state == migration->device_state) {
        return 0;
    }

Could a later request for STOP be skipped here, leaving a running
device that is believed to be stopped? The cached value is also read by
vfio_device_state_is_running() and by the switch in vfio_save_setup().

The commit message says the device is cached as "STOP if it stays
paused" and that "At CPR the device can only be in one of the two."
Is that accurate for a source that was running? The patch infers the
device state from the target's run state, not from the source's run
state.

Would it be possible to carry the real device state from the source, or
to query it, instead of inferring it from the target's run state?

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=27

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 26/35] vfio/migration: don't migrate device state during CPR
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 26/35] vfio/migration: don't migrate device state during CPR Andrey Drobyshev
@ 2026-11-03 14:26   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:26 UTC (permalink / raw)
  To: svt-core, Andrey Drobyshev; +Cc: andrey.drobyshev

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] vfio/migration: cpr_is_in_progress() is false on cpr-transfer source during the stream
- [Medium] vfio/migration: cpr_is_in_progress() true before migration starts skips stop/cont device state
--

commit 67f4499a74b760de74e49d3ef3311d53eec749fd
Author: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

vfio/migration: don't migrate device state during CPR

This patch gates the VFIO migration handlers on cpr_is_in_progress() so
that cpr-transfer and cpr-exec no longer save, load or change the state
of a preserved VFIO device. It also resets multifd_transfer in
vfio_multifd_cleanup().

> diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
> --- a/hw/vfio/migration.c
> +++ b/hw/vfio/migration.c

[ ... ]

> @@ -674,6 +674,16 @@ static void vfio_save_state(QEMUFile *f, void *opaque)
>      Error *local_err = NULL;
>      int ret;
>  
> +    /*
> +     * .is_active doesn't cover the non-iterable stage, so the section is
> +     * still sent: leave it empty, or new QEMU would load the config into
> +     * the preserved device and re-program its MSI-X.
> +     */
> +    if (cpr_is_in_progress()) {
> +        qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE);
> +        return;
> +    }
> +

[Severity: High]
Is cpr_is_in_progress() still true on a cpr-transfer source by the time
this runs?

On the source, cpr_is_in_progress() ends with:

    return cpr_state_file != NULL;

For cpr-transfer, qmp_migrate() calls cpr_state_save() and then
cpr_transfer_add_hup_watch(). When the target closes its CPR socket,
qmp_migrate_finish_cb() runs:

    qmp_migrate_finish(addr, NULL);

    cpr_state_close();

qmp_migrate_finish() only starts the asynchronous connect.
cpr_state_close() then does:

    qemu_fclose(cpr_state_file);
    cpr_state_file = NULL;

The migration thread is started later, from socket_outgoing_migration()
via migration_channel_connect(). So by the time save_setup, vm_stop,
vfio_save_state(), the switchover-ack check and save_complete run on the
source, cpr_state_file appears to be NULL. The source has no incoming
mode, so cpr_is_incoming() is false and cpr_is_in_progress() returns
false.

On the target, cpr_is_incoming() is true, so cpr_is_in_progress() returns
true. The two sides then disagree:

vfio_is_active() returns true on the source, so the source sends the full
config section, and vfio_vmstate_change() stops the device.

The target skips load_setup and vfio_vmstate_change(), but
vfio_load_state() has no is_active check, so it may still process the
config and buffer sections. That could re-program MSI-X on the preserved
device, use an uninitialised data_fd, or leave the device stopped and
never restarted.

For fd transport the connect is not asynchronous, so the migration thread
may race with cpr_state_close().

Can the source-side predicate be made to stay true for the whole outgoing
cpr-transfer stream? As far as I can tell, cpr-exec is not affected,
since its cpr_state_file stays open until migration_cleanup(). The
later patches in the series do not appear to change cpr_is_in_progress(),
cpr_state_close() timing or qmp_migrate_finish_cb().

[ ... ]

> @@ -857,6 +884,11 @@ static void vfio_vmstate_change_prepare(void *opaque, bool running,
>      Error *local_err = NULL;
>      int ret;
>  
> +    /* The device is preserved, not a plain stop/cont: keep its state */
> +    if (cpr_is_in_progress()) {
> +        return;
> +    }
> +
>  
> @@ -882,6 +914,10 @@ static void vfio_vmstate_change(void *opaque, bool running, RunState state)
>      Error *local_err = NULL;
>      int ret;
>  
> +    if (cpr_is_in_progress()) {
> +        return;
> +    }
> +

[Severity: Medium]
Can these early returns skip a plain stop/cont on the source before the
migration has started?

On the source, cpr_is_in_progress() becomes true as soon as
qmp_migrate() calls cpr_state_save() and sets cpr_state_file. For
cpr-transfer, qmp_migrate() then only adds the HUP watch and returns.
The real migration starts only in qmp_migrate_finish_cb(), after the
target closes the CPR socket. That window is not bounded.

During the window the migration state is still SETUP and no savevm
handlers have run. qmp_cont() still accepts stop and cont. Both
vfio_vmstate_change_prepare() and vfio_vmstate_change() return early.

For example, if the VM is paused, the device is in STOP. The user runs
migrate with cpr-transfer and then cont before the target connects.
vm_start() resumes the vCPUs, but vfio_vmstate_change() skips the
RUNNING transition, so the guest runs against a device left in STOP. If
the migration is then cancelled or fails, cpr_state_close() clears the
predicate, but nothing restores the device state. A stop inside the
window can similarly leave the device RUNNING.

The commit message says "the mode may merely be set, and then a plain
stop/cont must still change the device state". Does that hold for this
window, where cpr_state_file is already set but no migration is
running? The later patches in the series appear to add only a
cpr_is_incoming() branch for the target in vfio_vmstate_change(), so the
source keeps the same early return.

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=26

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 28/35] migration/ram: don't start dirty logging in CPR modes
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 28/35] migration/ram: don't start dirty logging in CPR modes Andrey Drobyshev
@ 2026-11-03 14:27   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:27 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core; +Cc: andrey.drobyshev

> In the CPR modes guest RAM is preserved in place, not copied:
> migrate_ram_is_ignored() is true for every block.  Yet ram_init_bitmaps()
> still starts the global dirty log, so every listener starts tracking
> pages nobody will send.  KVM write-protects all guest memory, and VFIO
> starts DMA logging on the devices; with cpr-exec that logging is never
> stopped before the exec, leaving e.g. the DMA page tracker of an NVIDIA
> vGPU running after the update.
> 
> Don't start the dirty log for a migration that migrates no RAM, as is
> already done for background snapshots.  ram_save_cleanup() only stops
> the log if it was started.
> 
> Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=28


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 29/35] VZ: vfio/cpr: don't close KVM on cpr-exec
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 29/35] VZ: vfio/cpr: don't close KVM on cpr-exec Andrey Drobyshev
@ 2026-11-03 14:29   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:29 UTC (permalink / raw)
  To: svt-core, Andrey Drobyshev; +Cc: andrey.drobyshev

> With VFIO devices, KVM is closed on the source when the migration
> completes, so that the new QEMU can create its own KVM VM with the same
> devices.  That is done from a migration notifier, which for cpr-exec
> runs before cpr_exec_cb().  But cpr_exec_cb() may still give up, when
> the memory can't be preserved or execve() fails, and resume the VM in
> the old QEMU.  A VM without KVM can't resume: kvm_close() removes the
> vCPU threads, and the first KVM ioctl of vm_start() aborts QEMU
> (KVM_SET_CLOCK in kvmclock_vm_state_change()).
> 
> cpr-exec doesn't need it anyway: the KVM fds are close-on-exec, so the
> kernel closes them during execve(), before the new QEMU runs, and
> kvm_vm_release() tears down the irqfds then.  Keep the notifier for
> cpr-transfer only, where the source process lives on.
> 
> Tested on the vfio-net rig: cpr-exec 5/5; a cpr-exec aborted in
> preserve_ram() and one with a bogus cpr-exec-command both left the VM
> running with no lost pings, and a later cpr-exec completed.
> 
> Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=29


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 30/35] VZ: migration/cpr-exec: abort cpr-exec if memory can't be preserved
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 30/35] VZ: migration/cpr-exec: abort cpr-exec if memory can't be preserved Andrey Drobyshev
@ 2026-11-03 14:29   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:29 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core; +Cc: andrey.drobyshev

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] migration: unpreserve_ram() uses raw MADV_DONTEXEC and ignores madvise() result
--

commit 4dac8e6fe698ac777e5eb0a332bee47fd286faab
Author: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

VZ: migration/cpr-exec: abort cpr-exec if memory can't be preserved

This patch makes cpr_exec_cb() treat a failed MADV_DOEXEC on any saved
ramblock as a failed cpr-exec. It jumps to a shared failure path instead
of calling execvp(). That path also clears MADV_DOEXEC from the saved
ramblocks again.

> diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
> --- a/migration/cpr-exec.c
> +++ b/migration/cpr-exec.c

[ ... ]

> @@ -163,6 +163,19 @@ static bool preserve_ram(cpr_ramblock_t ram, char *name)
>      return true;
>  }
>  
> +static bool unpreserve_ram(cpr_ramblock_t ram, char *name)
> +{
> +#ifdef MADV_DONTEXEC
> +    qemu_madvise((void *) ram.addr, ram.length, MADV_DONTEXEC);
> +#endif
> +    return true;
> +}
> +

[Severity: Low]
This isn't a bug, but unpreserve_ram() is asymmetric with preserve_ram().

preserve_ram() goes through the QEMU_MADV_DOEXEC wrapper and reports
failures. unpreserve_ram() instead guards on #ifdef MADV_DONTEXEC, passes
the raw constant to qemu_madvise(), and discards the return value.

include/qemu/madvise.h has no QEMU_MADV_DONTEXEC counterpart. Would it be
cleaner to add one?

Separately, suppose cpr_exec_cb() reaches the fail: label after a
preserve_ram() or execvp() failure, and the madvise() clear in
unpreserve_ram() fails. The old QEMU would then keep stale DOEXEC marks
with no diagnostic. Those marks could carry over into a later exec from
the same process, for example a retried cpr-exec.

Should a failure here at least be reported with error_report() or similar?

In the posix_madvise fallback build, QEMU_MADV_DOEXEC is
QEMU_MADV_INVALID, so preserve_ram() fails first. unpreserve_ram() then
passes MADV_DONTEXEC to posix_madvise(), which returns EINVAL. That is
harmless because nothing was marked.

> +static void cpr_exec_unpreserve_ram(void)
> +{
> +    cpr_walk_ramblock(unpreserve_ram);
> +}
> +

[ ... ]

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=30

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 32/35] VZ: migration/cpr-exec: don't exec if a vhost-net device can't be released
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 32/35] VZ: migration/cpr-exec: don't exec if a vhost-net device can't be released Andrey Drobyshev
@ 2026-11-03 14:30   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:30 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core; +Cc: andrey.drobyshev

> vhost_net_cpr_reset_owners() only warns when VHOST_RESET_OWNER fails for
> a device, and the exec goes on.  But the new QEMU can't take such a
> device: it is still owned by the old mm, so VHOST_SET_OWNER fails with
> EBUSY, and the guest is left without network after the update.
> 
> Stop at the first device that can't be released and abort the cpr-exec
> instead.  The VM goes on in the old QEMU, with the devices released so
> far taken back.  vhost-vsock already does that in its pre_save.
> 
> Fixes: 1278e4830052 ("VZ: migration/cpr-exec: vhost-net: send RESET_OWNER before cpr-exec")
> Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=32


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 31/35] VZ: migration/cpr-exec: take the vhost-net devices back if exec fails
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 31/35] VZ: migration/cpr-exec: take the vhost-net devices back if exec fails Andrey Drobyshev
@ 2026-11-03 14:32   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:32 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core; +Cc: andrey.drobyshev

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Low] migration: missing vhost_net-stub.c stubs for vhost_net_cpr_set_owners()
- [Low] vhost-net: cpr-exec failure path does not restore per-vring busyloop timeout
--

commit e24cbe54c34c6fafdb0c02981bf567ef0cd723b5
Author: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

VZ: migration/cpr-exec: take the vhost-net devices back if exec fails

This patch makes cpr_exec_cb() call a new function,
vhost_net_cpr_set_owners(), when execve() fails. The function takes back
the kernel vhost-net devices that vhost_net_cpr_reset_owners() released. It
does this with VHOST_SET_OWNER, and it sets the vring call and err fds
again.

> diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
> --- a/hw/net/vhost_net.c
> +++ b/hw/net/vhost_net.c

[ ... ]

> +void vhost_net_cpr_set_owners(void)
> +{
> +    NetClientState *nc;
> +
> +    QTAILQ_FOREACH(nc, &net_clients, next) {
> +        VHostNetState *net = get_vhost_net(nc);
> +
> +        if (net && net->cpr_owner_reset) {

[ ... ]

> +            /* RESET_OWNER also dropped the vring fds set by vhost_dev_init() */
> +            for (i = 0; i < dev->nvqs; i++) {
> +                const VhostOps *ops = dev->vhost_ops;
> +                struct vhost_virtqueue *vq = &dev->vqs[i];
> +                struct vhost_vring_file file = {
> +                    .index = ops->vhost_get_vq_index(dev, dev->vq_index + i),
> +                    .fd = event_notifier_get_wfd(&vq->masked_notifier),
> +                };
> +
> +                r = ops->vhost_set_vring_call(dev, &file);
> +                if (r == 0 && ops->vhost_set_vring_err) {
> +                    file.fd = event_notifier_get_fd(&vq->error_notifier);
> +                    r = ops->vhost_set_vring_err(dev, &file);
> +                }

[Severity: Low]
VHOST_RESET_OWNER also clears the per-vring busyloop timeout in the kernel.
This loop restores only the call and err fds. Can the busyloop timeout be
lost after a failed execve()?

vhost_dev_init() is the only place that applies it:

hw/virtio/vhost.c:vhost_dev_init() {
    ...
    if (busyloop_timeout) {
        for (i = 0; i < hdev->nvqs; ++i) {
            r = vhost_virtqueue_set_busyloop_timeout(hdev, hdev->vq_index + i,
                                                     busyloop_timeout);
    ...
}

The value is not saved in vhost_dev or vhost_net. Neither vhost_net_start()
nor vhost_dev_start() applies it again. After vhost_net_cpr_set_owners(),
the VM would therefore keep running without the configured busyloop_timeout.
Should this loop re-apply it?

> diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
> --- a/migration/cpr-exec.c
> +++ b/migration/cpr-exec.c

[ ... ]

> @@ -259,6 +259,8 @@ fail:
>      if (cpr_ram_rdonly) {
>          cpr_ram_restore_rw();
>      }
> +    /* Still the same mm, take back the vhost-net devices we released */
> +    vhost_net_cpr_set_owners();

[Severity: Low]
Will this link in builds that use hw/net/vhost_net-stub.c?

migration/cpr-exec.c is always compiled, and cpr_exec_cb() calls
vhost_net_cpr_set_owners() without any #ifdef guard.

hw/net/meson.build compiles vhost_net.c only when have_vhost_net is set and
CONFIG_VIRTIO_NET is enabled. Otherwise it compiles vhost_net-stub.c. That
covers --disable-vhost-net builds, non-Linux hosts, and builds without
CONFIG_VIRTIO_NET. The stub file has no stub for this function.

This problem wasn't introduced by this patch for vhost_net_cpr_reset_owners(),
which has the same missing stub. The patch adds a second unresolved symbol
and does not add either stub. Could stubs for both functions be added to
vhost_net-stub.c?

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=31

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 34/35] VZ: migration/cpr-exec: allow cpr-exec with VFIO devices #VSTOR-103860
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 34/35] VZ: migration/cpr-exec: allow cpr-exec with VFIO devices #VSTOR-103860 Andrey Drobyshev
@ 2026-11-03 14:33   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:33 UTC (permalink / raw)
  To: svt-core, Andrey Drobyshev; +Cc: andrey.drobyshev

> Commit bb8da93ddfc4 ("VZ: migration/cpr-exec: enable checks prohibiting
> cpr-exec #VSTOR-108564") introduced restrictions for cpr-exec migration
> mode.  Among them was prohibition to do such migration in case of
> presence of any VFIO devices.  Now we want to allow migration with such
> devices in scope of VSTOR-103860.  Therefore, let's lift that restiction.
> 
> Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=34


^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 35/35] VZ: vfio/migration: don't wait for stop-copy-only device state #VSTOR-141821
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 35/35] VZ: vfio/migration: don't wait for stop-copy-only device state #VSTOR-141821 Andrey Drobyshev
@ 2026-11-03 14:35   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:35 UTC (permalink / raw)
  To: svt-core, Andrey Drobyshev; +Cc: andrey.drobyshev

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] vfio/migration: stop-copy size dropped from pending, so downtime-limit is bypassed
- [Low] vfio: comment in vfio_state_pending_exact() misdescribes error fallback
--

commit 4741edc5da3eca96f6669a6d5a3ce9d95caac4ae
Author: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

VZ: vfio/migration: don't wait for stop-copy-only device state #VSTOR-141821

This patch changes vfio_state_pending_exact() so that the VFIO stop-copy size is added to *must_precopy only when a query failed or the device still has pre-copy data. Devices without pre-copy support, and pre-copy devices that have drained, no longer have their stop-copy state counted toward the switchover decision.

> diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
> --- a/hw/vfio/migration.c
> +++ b/hw/vfio/migration.c

[ ... ]

> @@ -569,16 +569,24 @@ static void vfio_state_pending_exact(void *opaque, uint64_t *must_precopy,
>      VFIODevice *vbasedev = opaque;
>      VFIOMigration *migration = vbasedev->migration;
>      uint64_t stop_copy_size = VFIO_MIG_STOP_COPY_SIZE;
> +    int ret;
>  
>      /*
> -     * If getting pending migration size fails, VFIO_MIG_STOP_COPY_SIZE is
> -     * reported so downtime limit won't be violated.
> +     * If getting the pending sizes fails, VFIO_MIG_STOP_COPY_SIZE is
> +     * reported, unconditionally, so the switchover isn't taken blindly.
>       */
> -    vfio_query_stop_copy_size(vbasedev, &stop_copy_size);
> -    *must_precopy += stop_copy_size;
> +    ret = vfio_query_stop_copy_size(vbasedev, &stop_copy_size);
>  
>      if (vfio_device_state_is_precopy(vbasedev)) {
> -        vfio_query_precopy_size(migration);
> +        ret |= vfio_query_precopy_size(migration);
> +    }

[Severity: Low]
This isn't a bug, but is the new comment accurate?

It says that if getting the pending sizes fails, VFIO_MIG_STOP_COPY_SIZE is reported unconditionally. That only holds when vfio_query_stop_copy_size() itself fails, because only then does stop_copy_size keep its initial value.

If vfio_query_stop_copy_size() succeeds and vfio_query_precopy_size() then fails, ret is non-zero. The real stop_copy_length value is added to *must_precopy, not VFIO_MIG_STOP_COPY_SIZE.

vfio_query_precopy_size() also zeroes precopy_init_size and precopy_dirty_size in that case, so the trace event shows 0 for both. Could the comment be reworded to match this?

> +
> +    /*
> +     * Once there's nothing left to pre-copy, the stop-copy state can't
> +     * shrink by iterating: don't let it hold up the switchover.
> +     */
> +    if (ret || migration->precopy_init_size || migration->precopy_dirty_size) {
> +        *must_precopy += stop_copy_size;
>      }

[Severity: Medium]
Does this gate let the switchover happen while a large stop-copy state still has to be sent with the guest paused?

The switchover decision in migration_iteration_run() depends only on the reported pending size:

    complete_ready = can_switchover && (pending_size <= s->threshold_size);

With stop_copy_size left out of *must_precopy, this can become true even when the device state is much larger than bandwidth x downtime-limit. The commit message acknowledges that "the downtime limit no longer bounds the time that state takes to send".

Two cases make the gate false:

1) A device without VFIO_MIGRATION_PRE_COPY never reaches vfio_query_precopy_size(), because vfio_device_state_is_precopy() is false. precopy_init_size and precopy_dirty_size stay 0, so its stop-copy state is never counted, however large it is. vfio_state_pending_estimate() also returns early for such a device:

    if (!vfio_device_state_is_precopy(vbasedev)) {
        return;
    }

so no other handler accounts for that state.

2) A pre-copy device whose query returns 0/0 at a single sampling instant is treated as drained, although the guest is still running. Can stop_copy_length grow after that sample? The comment "can't shrink by iterating" says nothing about whether the size is final once 0/0 is read.

There is no per-device or per-driver opt-in here, so every VFIO device is affected, not only the vGPU case described in the commit message. Would it make sense to limit this behaviour to devices known to need it, or to make it configurable?

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=35

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 33/35] VZ: vfio/region: preserve BAR mappings across cpr-exec #VSTOR-103860
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 33/35] VZ: vfio/region: preserve BAR mappings across cpr-exec #VSTOR-103860 Andrey Drobyshev
@ 2026-11-03 14:38   ` no-reply
  0 siblings, 0 replies; 72+ messages in thread
From: no-reply @ 2026-11-03 14:38 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core; +Cc: andrey.drobyshev

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Medium] vfio: cpr-transfer blocked for every VFIO device, not only those with saved BAR mmaps
- [Medium] vfio: cpr-transfer blocker skipped for vfio-user, stale BAR mappings reused
- [Low] vfio/region: BAR mappings enter CPR ramblock list walked by cpr-ram-rdonly mprotect
- [Low] vfio: unclaimed BAR mappings preserved by cpr-exec are never cleaned up
--

commit 8715bbf752f1d56051fe39f5336635bb75b99caa
Author: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>

VZ: vfio/region: preserve BAR mappings across cpr-exec #VSTOR-103860

This patch saves each VFIO BAR mapping in the CPR ramblock list so that
cpr-exec preserves it with MADV_DOEXEC, and the new QEMU reuses it instead
of calling mmap() again. It also blocks cpr-transfer for all VFIO devices
and updates CPR.rst to say so.

> diff --git a/docs/devel/migration/CPR.rst b/docs/devel/migration/CPR.rst
> --- a/docs/devel/migration/CPR.rst
> +++ b/docs/devel/migration/CPR.rst

[ ... ]

> -RAM in place, albeit with new virtual addresses in new QEMU.  Devices
> -and their pinned memory pages are also preserved for VFIO and IOMMUFD.
> +RAM in place, albeit with new virtual addresses in new QEMU.  VFIO and
> +IOMMUFD devices are not supported in this mode: use cpr-exec for them.

This documents the new policy, but only this paragraph was changed. Are
the other cpr-transfer and VFIO descriptions in CPR.rst still accurate?

> diff --git a/hw/vfio/device.c b/hw/vfio/device.c
> --- a/hw/vfio/device.c
> +++ b/hw/vfio/device.c

[ ... ]

> @@ -464,12 +464,32 @@ bool vfio_device_attach(char *name, VFIODevice *vbasedev,
> +    error_setg(&vbasedev->cpr.mmap_blocker,
> +               "cpr-transfer is not supported with VFIO devices; "
> +               "use the cpr-exec mode");
> +    if (migrate_add_blocker_modes(&vbasedev->cpr.mmap_blocker, errp,
> +                                  MIG_MODE_CPR_TRANSFER, -1)) {
> +        return false;
> +    }

[Severity: Medium]
Is it intended that this blocker is installed in vfio_device_attach() for
every VFIO device?

The blocker is added before any backend-specific work and without checking
device type, no_mmap, or whether any region will ever be saved through
vfio_region_cpr_save(). Devices such as vfio-ap, vfio-ccw, x-no-mmap
devices, devices with no mmap-able BARs, and PCI devices whose drivers
allow re-mapping would also lose cpr-transfer.

The commit message gives the NVIDIA vGPU re-map restriction as the
rationale. Would it be narrower to block only when
vfio_region_cpr_save() actually saves a mapping, or only on the PCI path?

The series does not appear to narrow this blocker later. The final state
still has the same unconditional mmap_blocker in vfio_device_attach().

Also, the cpr-transfer specific VFIO code now looks unreachable for
cpr-transfer, but it is left in place: the id_blocker with
MIG_MODE_CPR_TRANSFER in device.c, the blockers in cpr-legacy.c and
cpr-iommufd.c, and the ones in cpr.c. Should that code be cleaned up?

[Severity: Medium]
Does this blocker cover vfio-user devices? vfio_user_pci_realize() in
hw/vfio-user/pci.c calls vfio_device_attach_by_iommu_type() directly:

    if (!vfio_device_attach_by_iommu_type(TYPE_VFIO_IOMMU_USER,
                                          vbasedev->name, vbasedev,
                                          as, errp)) {

That bypasses vfio_device_attach(), so vbasedev->cpr.mmap_blocker is never
installed for vfio-user. hw/vfio-user/ has no other CPR blocker.

vfio-user regions can be mmapped through server-supplied region fds, and
vfio_bar_register() in hw/vfio/pci.c calls vfio_region_mmap(). That now
reaches vfio_region_cpr_save(), which puts the mapping in
cpr_state.ramblocks. That list is serialized for both cpr-exec and
cpr-transfer.

On a cpr-transfer target, vfio_region_cpr_find() looks up
"<vbasedev->name> region N mmaps[i]". For vfio-user, vbasedev->name is
"vfio-user:<sock>", so the name matches the saved entry.

> +static void *vfio_region_cpr_find(VFIORegion *region, int index)
> +{
> +    g_autofree char *name = vfio_region_cpr_name(region, index);
> +    cpr_ramblock_t ram = cpr_find_ramblock(name);
> +
> +    if (ram.addr && ram.length != region->mmaps[index].size) {
[ ... ]
> +    return (void *)(uintptr_t)ram.addr;
> +}

Nothing checks that the incoming mode is cpr-exec here. Can a cpr-transfer
target take the old process's address from this function and pass it to
memory_region_init_ram_device_ptr() in vfio_region_mmap() as if it were a
local mapping? If so, the first guest access to that BAR would touch an
invalid or unrelated address.

Should vfio_region_cpr_find() be gated on cpr_exec_is_incoming(), or should
the vfio-user attach path also install the blocker?

> +static void vfio_region_cpr_save(VFIORegion *region, int index)
> +{
[ ... ]
> +    cpr_save_ramblock(name, (cpr_ramblock_t) { (uintptr_t)addr, size });
> +}

[Severity: Low]
Is it intended that BAR mappings, including read-only ones, now go into the
same ramblock list that cpr_exec_cb() walks for the -cpr-ram-rdonly debug
option?

With that option set, cpr_exec_cb() calls
cpr_walk_ramblock(mark_ram_rdonly), and mark_ram_rw() later calls
qemu_mprotect_rw() on each entry and abort()s on failure:

    if (qemu_mprotect_rw((void *) ram.addr, ram.length)) {
        error_report("qemu_mprotect_rw() failed on memory region %s:"
        ...
        abort();

A BAR that vfio_region_mmap() mapped read-only (no
VFIO_REGION_INFO_FLAG_WRITE) would be turned into PROT_READ|PROT_WRITE by
cpr_ram_restore_rw(). If a driver rejects mprotect on these device PFN
mappings, QEMU would abort.

I did not confirm that any driver refuses mprotect. Should these entries be
excluded from the mprotect walks?

[Severity: Low]
Can a preserved BAR mapping that new QEMU never claims be left behind?

The only place a saved mapping is claimed is vfio_region_mmap():

    region->mmaps[i].mmap = vfio_region_cpr_find(region, i);
    if (region->mmaps[i].mmap) {
        goto mapped;
    }

The only cleanup is cpr_delete_ramblock() in vfio_subregion_unmap() and
vfio_region_finalize(), and both run only for regions that were set up on
the new side. If new QEMU never reaches vfio_region_mmap() for a region
(for example a different x-no-mmap setting, a changed region layout, or a
failed realize), the mapping stays mapped with its MADV_DOEXEC mark and its
cpr_state.ramblocks entry.

A later cpr-exec would then apply MADV_DOEXEC to it again through
cpr_walk_ramblock(preserve_ram). Because it is never unmapped, a driver
such as the NVIDIA vGPU driver would still see the BAR as mapped.

Is there a sweep of unclaimed region entries planned? None of the later
patches in the series appear to add one.

-- 
Sashiko AI review · https://sashiko.vzint.dev/#/patchset/20261103-vfio_v1-v7-0-4467fdccbbe6@virtuozzo.com?part=33

^ permalink raw reply	[flat|nested] 72+ messages in thread

* Re: [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860
  2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
                   ` (34 preceding siblings ...)
  2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 35/35] VZ: vfio/migration: don't wait for stop-copy-only device state #VSTOR-141821 Andrey Drobyshev
@ 2026-11-04 13:11 ` Denis V. Lunev
  35 siblings, 0 replies; 72+ messages in thread
From: Denis V. Lunev @ 2026-11-04 13:11 UTC (permalink / raw)
  To: Andrey Drobyshev, svt-core

On 11/3/26 14:57, Andrey Drobyshev wrote:
> v6 -> v7:
>
> Fixes of the issues found by Sashiko AI review.
>
> - Patch 4 ("migration/cpr-exec: require a file or exec migration URI"):
>   the message says the check covers the incoming URI too
>
> - Patch 6 ("migration/cpr-exec: close the persisted state if the
>   channel fails to open"): the fd is parsed before its variable is
>   unset
>
> - Patch 7 (NEW) ("migration/cpr-exec: don't leak the dup'd memfd if
>   g_setenv() fails"): the dup was leaked on that path and a failed
>   dup() went unchecked; the patches after it are renumbered
>
> - Patch 32 -> 33 ("VZ: vfio/region: preserve BAR mappings across
>   cpr-exec"): CPR.rst no longer says cpr-transfer preserves VFIO
>   devices
>
> - Patch 34 -> 35 ("VZ: vfio/migration: don't wait for stop-copy-only
>   device state"): the message covers devices without pre-copy, which
>   the rule applies to as well
>
> v6: http://lore.virtuozzo.com/20261102-vfio_v1-v6-0-9bc7cabc850d@virtuozzo.com
> ---
> Akihiko Odaki (1):
>       vfio/pci: Grow buffer in vfio_pci_host_match()
>
> Andrey Drobyshev (21):
>       VZ: migration/cpr-exec: fix Error leak in preserve_ram()
>       migration/cpr-exec: restore RAM write access if exec fails
>       vfio/region: create BAR dma-bufs with O_CLOEXEC
>       migration/cpr-exec: require a file or exec migration URI
>       migration: close the CPR state if the outgoing channel fails to open
>       migration/cpr-exec: close the persisted state if the channel fails to open
>       migration/cpr-exec: don't leak the dup'd memfd if g_setenv() fails
>       migration: don't refuse a blocker over a migration of another mode
>       kvm: tolerate ioctls after kvm_close()
>       kvm: fix coalesced MMIO flush racing with kvm_close()
>       migration/cpr: add cpr_is_in_progress()
>       vfio/migration: don't migrate device state during CPR
>       vfio/migration: don't assume the device is running on CPR incoming
>       migration/ram: don't start dirty logging in CPR modes
>       VZ: vfio/cpr: don't close KVM on cpr-exec
>       VZ: migration/cpr-exec: abort cpr-exec if memory can't be preserved
>       VZ: migration/cpr-exec: take the vhost-net devices back if exec fails
>       VZ: migration/cpr-exec: don't exec if a vhost-net device can't be released
>       VZ: vfio/region: preserve BAR mappings across cpr-exec #VSTOR-103860
>       VZ: migration/cpr-exec: allow cpr-exec with VFIO devices #VSTOR-103860
>       VZ: vfio/migration: don't wait for stop-copy-only device state #VSTOR-141821
>
> Arun Menon (1):
>       migration: Fix regression of passing error_fatal into vmstate_load_state()
>
> Cédric Le Goater (2):
>       vfio/pci: Reject invalid MSI-X Table and PBA BIR values
>       vfio/pci: Fix information leak in vfio_rom_read()
>
> Fabiano Rosas (3):
>       migration: yank: Move register instance earlier
>       migration: Free cpr-transfer MigrationAddress along with gsource
>       migration: Move CPR HUP watch to cpr-transfer.c
>
> Igor Mammedov (1):
>       q35: Fix migration of SMRAM state
>
> Mario Casquero (1):
>       vfio/pci: Initialize rom_read_failed in vfio_pci_load_rom()
>
> Markus Armbruster (1):
>       migration: Plug memory leaks after migrate_set_error()
>
> Peter Xu (1):
>       migration: Fix crash on second migration when cancel early
>
> Tycho Andersen (AMD) (1):
>       hw/vfio: Fix liveness check in vfio_connect_kvm_msi_virq()
>
> Vladimir Sementsov-Ogievskiy (1):
>       migration: vmstate_save_state_v: fix double error_setg
>
> Yanfei Xu (1):
>       migration: ensure APIC is loaded prior to VFIO PCI devices
>
>  accel/kvm/kvm-all.c           |  24 +++++++-
>  docs/devel/migration/CPR.rst  |  12 ++--
>  hw/display/virtio-gpu.c       |  22 +++++---
>  hw/intc/apic_common.c         |   1 +
>  hw/net/vhost_net.c            |  62 ++++++++++++++++++++-
>  hw/pci-host/q35.c             |  25 ++++-----
>  hw/pci/pci.c                  |  15 ++++-
>  hw/s390x/virtio-ccw.c         |  17 +++++-
>  hw/scsi/spapr_vscsi.c         |  10 +++-
>  hw/vfio/cpr.c                 |  11 ++--
>  hw/vfio/device.c              |  29 +++++++---
>  hw/vfio/migration-multifd.c   |   2 +
>  hw/vfio/migration.c           |  68 ++++++++++++++++++++---
>  hw/vfio/pci.c                 |  25 ++++++---
>  hw/vfio/region.c              |  51 ++++++++++++++++-
>  hw/virtio/virtio-mmio.c       |  15 ++++-
>  hw/virtio/virtio-pci.c        |  15 ++++-
>  hw/virtio/virtio.c            |  10 +++-
>  include/hw/vfio/vfio-cpr.h    |   1 +
>  include/hw/vfio/vfio-device.h |   2 -
>  include/hw/virtio/vhost.h     |   2 +
>  include/migration/cpr.h       |   7 +++
>  include/migration/vmstate.h   |   1 +
>  include/net/vhost_net.h       |   3 +-
>  migration/cpr-exec.c          |  54 +++++++++++++++---
>  migration/cpr-transfer.c      |  33 +++++++++++
>  migration/cpr.c               |  22 ++++++++
>  migration/migration.c         | 126 ++++++++++++++++++++++++------------------
>  migration/migration.h         |   5 ++
>  migration/multifd.c           |   2 +
>  migration/ram.c               |   7 ++-
>  migration/vmstate.c           |   7 ++-
>  tests/qtest/q35-test.c        |   6 --
>  33 files changed, 546 insertions(+), 146 deletions(-)
> ---
> base-commit: 1c84596876fb77b3e25d56cd4a4fa0c32f1faa32
> change-id: 20261009-vfio_v1-a176cddd6b64
>
> Best regards,
> --  
> Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
Ack

^ permalink raw reply	[flat|nested] 72+ messages in thread

end of thread, other threads:[~2026-11-04 13:11 UTC | newest]

Thread overview: 72+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-03 13:57 [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Andrey Drobyshev
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 01/35] VZ: migration/cpr-exec: fix Error leak in preserve_ram() Andrey Drobyshev
2026-11-03 14:00   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 02/35] migration/cpr-exec: restore RAM write access if exec fails Andrey Drobyshev
2026-11-03 14:01   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 03/35] vfio/region: create BAR dma-bufs with O_CLOEXEC Andrey Drobyshev
2026-11-03 14:02   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 04/35] migration/cpr-exec: require a file or exec migration URI Andrey Drobyshev
2026-11-03 14:02   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 05/35] migration: close the CPR state if the outgoing channel fails to open Andrey Drobyshev
2026-11-03 14:03   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 06/35] migration/cpr-exec: close the persisted state if the " Andrey Drobyshev
2026-11-03 14:04   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 07/35] migration/cpr-exec: don't leak the dup'd memfd if g_setenv() fails Andrey Drobyshev
2026-11-03 14:04   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 08/35] migration: don't refuse a blocker over a migration of another mode Andrey Drobyshev
2026-11-03 14:05   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 09/35] migration: Plug memory leaks after migrate_set_error() Andrey Drobyshev
2026-11-03 14:05   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 10/35] hw/vfio: Fix liveness check in vfio_connect_kvm_msi_virq() Andrey Drobyshev
2026-11-03 14:05   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 11/35] vfio/pci: Reject invalid MSI-X Table and PBA BIR values Andrey Drobyshev
2026-11-03 14:05   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 12/35] vfio/pci: Fix information leak in vfio_rom_read() Andrey Drobyshev
2026-11-03 14:06   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 13/35] vfio/pci: Initialize rom_read_failed in vfio_pci_load_rom() Andrey Drobyshev
2026-11-03 14:06   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 14/35] vfio/pci: Grow buffer in vfio_pci_host_match() Andrey Drobyshev
2026-11-03 14:06   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 15/35] q35: Fix migration of SMRAM state Andrey Drobyshev
2026-11-03 14:08   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 16/35] migration: ensure APIC is loaded prior to VFIO PCI devices Andrey Drobyshev
2026-11-03 14:08   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 17/35] migration: Fix regression of passing error_fatal into vmstate_load_state() Andrey Drobyshev
2026-11-03 14:11   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 18/35] migration: vmstate_save_state_v: fix double error_setg Andrey Drobyshev
2026-11-03 14:11   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 19/35] migration: yank: Move register instance earlier Andrey Drobyshev
2026-11-03 14:16   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 20/35] migration: Free cpr-transfer MigrationAddress along with gsource Andrey Drobyshev
2026-11-03 14:14   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 21/35] migration: Move CPR HUP watch to cpr-transfer.c Andrey Drobyshev
2026-11-03 14:14   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 22/35] migration: Fix crash on second migration when cancel early Andrey Drobyshev
2026-11-03 14:16   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 23/35] kvm: tolerate ioctls after kvm_close() Andrey Drobyshev
2026-11-03 14:21   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 24/35] kvm: fix coalesced MMIO flush racing with kvm_close() Andrey Drobyshev
2026-11-03 14:19   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 25/35] migration/cpr: add cpr_is_in_progress() Andrey Drobyshev
2026-11-03 14:21   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 26/35] vfio/migration: don't migrate device state during CPR Andrey Drobyshev
2026-11-03 14:26   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 27/35] vfio/migration: don't assume the device is running on CPR incoming Andrey Drobyshev
2026-11-03 14:25   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 28/35] migration/ram: don't start dirty logging in CPR modes Andrey Drobyshev
2026-11-03 14:27   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 29/35] VZ: vfio/cpr: don't close KVM on cpr-exec Andrey Drobyshev
2026-11-03 14:29   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 30/35] VZ: migration/cpr-exec: abort cpr-exec if memory can't be preserved Andrey Drobyshev
2026-11-03 14:29   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 31/35] VZ: migration/cpr-exec: take the vhost-net devices back if exec fails Andrey Drobyshev
2026-11-03 14:32   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 32/35] VZ: migration/cpr-exec: don't exec if a vhost-net device can't be released Andrey Drobyshev
2026-11-03 14:30   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 33/35] VZ: vfio/region: preserve BAR mappings across cpr-exec #VSTOR-103860 Andrey Drobyshev
2026-11-03 14:38   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 34/35] VZ: migration/cpr-exec: allow cpr-exec with VFIO devices #VSTOR-103860 Andrey Drobyshev
2026-11-03 14:33   ` no-reply
2026-11-03 13:57 ` [PATCH QEMU HCI-8.1 v7 35/35] VZ: vfio/migration: don't wait for stop-copy-only device state #VSTOR-141821 Andrey Drobyshev
2026-11-03 14:35   ` no-reply
2026-11-04 13:11 ` [PATCH QEMU HCI-8.1 v7 00/35] vfio: support qemu-update with passed through GPU's and vGPU's #VSTOR-103860 Denis V. Lunev

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.