qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v9 00/27] migration: propagate vTPM errors using Error objects
@ 2025-08-05 18:25 Arun Menon
  2025-08-05 18:25 ` [PATCH v9 01/27] migration: push Error **errp into vmstate_subsection_load() Arun Menon
                   ` (26 more replies)
  0 siblings, 27 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon,
	Daniel P. Berrangé, Stefan Berger

Hello,

Currently, when a migration of a VM with an encrypted vTPM
fails on the destination host (e.g., due to a mismatch in secret values),
the error message displayed on the source host is generic and unhelpful.

For example, a typical error looks like this:
"operation failed: job 'migration out' failed: Sibling indicated error 1.
operation failed: job 'migration in' failed: load of migration failed:
Input/output error"

This message does not provide any specific indication of a vTPM failure.
Such generic errors are logged using error_report(), which prints to
the console/monitor but does not make the detailed error accessible via
the QMP query-migrate command.

This series addresses the issue, by ensuring that specific TPM error
messages are propagated via the QEMU Error object.
To make this possible,
- A set of functions in the call stack is changed
  to incorporate an Error object as an additional parameter.
- Also, the TPM backend makes use of a new hook called post_load_errp()
  that explicitly passes an Error object.

It is organized as follows,
 - Patches 1-21 focuses on pushing Error object into the functions
   that are important in the call stack where TPM errors are observed.
   We still need to make changes in rest of the functions in savevm.c
   such that they also incorporate the errp object for propagating errors.
 - Patch 22 introduces the new variants of the hooks in VMStateDescription
   structure. These hooks should be used in future implementations.
 - Patch 23 focuses on changing the TPM backend such that the errors are
   set in the Error object.

While this series focuses specifically on TPM error reporting during
live migration, it lays the groundwork for broader improvements.
A lot of methods in savevm.c that previously returned an integer now capture
errors in the Error object, enabling other modules to adopt the
post_load_errp hook in the future.

One such change previously attempted:
https://lists.gnu.org/archive/html/qemu-devel/2021-02/msg01727.html

Resolves: https://issues.redhat.com/browse/RHEL-82826

Signed-off-by: Arun Menon <armenon@redhat.com>
---
Changes in v9:
- Re ordering patches such that error is reported in each one of them.
- format specifier enclosed in '' changed i.e. '%d' changed to %d
- Reporting errors where they were missed before. Setting errp to NULL
  in case of retry.
- Link to v8: https://lore.kernel.org/qemu-devel/20250731-propagate_tpm_error-v8-0-28fd82fdfdb2@redhat.com

Changes in v8:
- 3 new patches added:
  - patch 23:
	- Changes the error propagation by returning the most recent error
	  to the caller when both save device state and post_save fails.
  - patch 24:
	- Refactors the vmstate_save_state_v() function by adding wrapper
	  functions to separate concerns.
  - patch 25:
	- Removes the error variant of the vmstate_save_state()
	  function introduced in commit 969298f9d7.
- Use ERRP_GUARD() where there is an errp dereference or an error_prepend call.
- Pass &error_warn in place of NULL, in vmstate_load_state() calls so
  that the caller knows about the error.
- Remove unnecessary null check before setting errp. Dereferencing it is not required.
- Documentation for the new variants of post/pre save/load hooks added.
- Some patches, although they received a 'Reviewed-by' tag, have undergone few minor changes,
	Patch 1 : removed extra space
	Patch 2 : Commit message changed, refactoring the function to
		always set errp and return.
	Patch 8 : Commit message changed.
	Patch 9 : use error_setg_errno instead of error_setg.
	Patch 27 : use error_setg_errno instead of error_setg.
- Link to v7: https://lore.kernel.org/qemu-devel/20250725-propagate_tpm_error-v7-0-d52704443975@redhat.com

Changes in v7:
- Fix propagating errors in post_save_errp. The latest error encountered is
  propagated.
- user-strings in error_prepend() calls now end with a ': ' so that the print is pretty.
- Change the order of one of the patches.
- Link to v6: https://lore.kernel.org/qemu-devel/20250721-propagate_tpm_error-v6-0-fef740e15e17@redhat.com

Changes in v6:
- Incorporated review comments from Daniel and Akihiko, related to few
  semantic errors and improve error logging.
- Add one more patch that removes NULL checks after calling
  qemu_file_get_return_path() because it does not fail.
- Link to v5: https://lore.kernel.org/qemu-devel/20250717-propagate_tpm_error-v5-0-1f406f88ee65@redhat.com

Changes in v5:
- Solve a bug that set errp even though it was not NULL, pointed out by Fabiano in v4.
- Link to v4: https://lore.kernel.org/qemu-devel/20250716-propagate_tpm_error-v4-0-7141902077c0@redhat.com

Changes in v4:
- Split the patches into smaller ones based on functions. Pass NULL in the
  caller until errp is made available. Every function that has an
  Error **errp object passed to it, ensures that it sets the errp object
  in case of failure.
- A few more functions within loadvm_process_command() now handle errors using
  the errp object. I've converted these for consistency, taking Daniel's
  patches (link above) as a reference.
- Along with the post_load_errp() hook, other duplicate hooks are also introduced.
  This will enable us to migrate to the newer versions eventually.
- Fix some semantic errors, like using error_propagate_prepend() in places where
  we need to preserve existing behaviour of accumulating the error in local_err
  and then propagating it to errp. This can be refactored in a later commit.
- Add more information in commit messages explaining the changes.
- Link to v3: https://lore.kernel.org/qemu-devel/20250702-propagate_tpm_error-v3-0-986d94540528@redhat.com

Changes in v3:
- Split the 2nd patch into 2. Introducing post_load_with_error() hook
  has been separated from using it in the backends TPM module. This is
  so that it can be acknowledged.
- Link to v2: https://lore.kernel.org/qemu-devel/20250627-propagate_tpm_error-v2-0-85990c89da29@redhat.com

Changes in v2:
- Combine the first two changes into one, focusing on passing the
  Error object (errp) consistently through functions involved in
  loading the VM's state. Other functions are not yet changed.
- As suggested in the review comment, add null checks for errp
  before adding error messages, preventing crashes.
  We also now correctly set errors when post-copy migration fails.
- In process_incoming_migration_co(), switch to error_prepend
  instead of error_setg. This means we now null-check local_err in
  the "fail" section before using it, preventing dereferencing issues.
- Link to v1: https://lore.kernel.org/qemu-devel/20250624-propagate_tpm_error-v1-0-2171487a593d@redhat.com

---
Arun Menon (27):
      migration: push Error **errp into vmstate_subsection_load()
      migration: push Error **errp into vmstate_load_state()
      migration: push Error **errp into qemu_loadvm_state_header()
      migration: push Error **errp into vmstate_load()
      migration: push Error **errp into loadvm_process_command()
      migration: push Error **errp into loadvm_handle_cmd_packaged()
      migration: push Error **errp into qemu_loadvm_state()
      migration: push Error **errp into qemu_load_device_state()
      migration: push Error **errp into qemu_loadvm_state_main()
      migration: push Error **errp into qemu_loadvm_section_start_full()
      migration: push Error **errp into qemu_loadvm_section_part_end()
      migration: Update qemu_file_get_return_path() docs and remove dead checks
      migration: make loadvm_postcopy_handle_resume() void
      migration: push Error **errp into ram_postcopy_incoming_init()
      migration: push Error **errp into loadvm_postcopy_handle_advise()
      migration: push Error **errp into loadvm_postcopy_handle_listen()
      migration: push Error **errp into loadvm_postcopy_handle_run()
      migration: push Error **errp into loadvm_postcopy_ram_handle_discard()
      migration: push Error **errp into loadvm_handle_recv_bitmap()
      migration: push Error **errp into loadvm_process_enable_colo()
      migration: push Error **errp into loadvm_postcopy_handle_switchover_start()
      migration: Capture error in postcopy_ram_listen_thread()
      migration: Refactor vmstate_save_state_v() function
      migration: Propagate last encountered error in vmstate_save_state_v() function
      migration: Remove error variant of vmstate_save_state() function
      migration: Add error-parameterized function variants in VMSD struct
      backends/tpm: Propagate vTPM error on migration failure

 backends/tpm/tpm_emulator.c   |  40 ++---
 docs/devel/migration/main.rst |  24 +++
 hw/display/virtio-gpu.c       |   5 +-
 hw/pci/pci.c                  |   5 +-
 hw/s390x/virtio-ccw.c         |   4 +-
 hw/scsi/spapr_vscsi.c         |   4 +-
 hw/vfio/pci.c                 |   6 +-
 hw/virtio/virtio-mmio.c       |   5 +-
 hw/virtio/virtio-pci.c        |   4 +-
 hw/virtio/virtio.c            |   8 +-
 include/migration/colo.h      |   2 +-
 include/migration/vmstate.h   |  19 ++-
 migration/colo.c              |  13 +-
 migration/cpr.c               |  10 +-
 migration/migration.c         |  31 ++--
 migration/postcopy-ram.c      |   9 +-
 migration/postcopy-ram.h      |   2 +-
 migration/qemu-file.c         |   1 -
 migration/ram.c               |  12 +-
 migration/ram.h               |   4 +-
 migration/savevm.c            | 330 ++++++++++++++++++++++++------------------
 migration/savevm.h            |   7 +-
 migration/vmstate-types.c     |  23 +--
 migration/vmstate.c           | 191 ++++++++++++++++++------
 tests/unit/test-vmstate.c     |  28 ++--
 ui/vdagent.c                  |   5 +-
 26 files changed, 497 insertions(+), 295 deletions(-)
---
base-commit: 4e06566dbd1b1251c2788af26a30bd148d4eb6c1
change-id: 20250624-propagate_tpm_error-bf4ae6c23d30

Best regards,
-- 
Arun Menon <armenon@redhat.com>



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

* [PATCH v9 01/27] migration: push Error **errp into vmstate_subsection_load()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-05 18:25 ` [PATCH v9 02/27] migration: push Error **errp into vmstate_load_state() Arun Menon
                   ` (25 subsequent siblings)
  26 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.
It is ensured that vmstate_subsection_load() must report an error
in errp, in case of failure.

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/vmstate.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/migration/vmstate.c b/migration/vmstate.c
index 5feaa3244d259874f03048326b2497e7db32e47c..24451b054c11dfca2d76e24b053d604bb7184e1c 100644
--- a/migration/vmstate.c
+++ b/migration/vmstate.c
@@ -25,7 +25,7 @@ static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
                                    void *opaque, JSONWriter *vmdesc,
                                    Error **errp);
 static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
-                                   void *opaque);
+                                   void *opaque, Error **errp);
 
 /* Whether this field should exist for either save or load the VM? */
 static bool
@@ -225,7 +225,7 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
         field++;
     }
     assert(field->flags == VMS_END);
-    ret = vmstate_subsection_load(f, vmsd, opaque);
+    ret = vmstate_subsection_load(f, vmsd, opaque, NULL);
     if (ret != 0) {
         qemu_file_set_error(f, ret);
         return ret;
@@ -566,7 +566,7 @@ vmstate_get_subsection(const VMStateDescription * const *sub,
 }
 
 static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
-                                   void *opaque)
+                                   void *opaque, Error **errp)
 {
     trace_vmstate_subsection_load(vmsd->name);
 
@@ -598,6 +598,8 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
         sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr);
         if (sub_vmsd == NULL) {
             trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(lookup)");
+            error_setg(errp, "VM subsection '%s' in '%s' does not exist",
+                       idstr, vmsd->name);
             return -ENOENT;
         }
         qemu_file_skip(f, 1); /* subsection */
@@ -608,6 +610,9 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
         ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
         if (ret) {
             trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(child)");
+            error_setg(errp,
+                       "Loading VM subsection '%s' in '%s' failed: %d",
+                       idstr, vmsd->name, ret);
             return ret;
         }
     }

-- 
2.50.1



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

* [PATCH v9 02/27] migration: push Error **errp into vmstate_load_state()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
  2025-08-05 18:25 ` [PATCH v9 01/27] migration: push Error **errp into vmstate_subsection_load() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-06  8:31   ` Marc-André Lureau
  2025-08-10  4:47   ` Akihiko Odaki
  2025-08-05 18:25 ` [PATCH v9 03/27] migration: push Error **errp into qemu_loadvm_state_header() Arun Menon
                   ` (24 subsequent siblings)
  26 siblings, 2 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.
It is ensured that vmstate_load_state() must report an error
in errp, in case of failure.

In cases where we do not want to essentially set the error
in errp object and also not abort/exit, the caller only
passes &error_warn, to warn on error.

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 hw/display/virtio-gpu.c     |  2 +-
 hw/pci/pci.c                |  3 ++-
 hw/s390x/virtio-ccw.c       |  2 +-
 hw/scsi/spapr_vscsi.c       |  2 +-
 hw/vfio/pci.c               |  2 +-
 hw/virtio/virtio-mmio.c     |  3 ++-
 hw/virtio/virtio-pci.c      |  2 +-
 hw/virtio/virtio.c          |  4 ++--
 include/migration/vmstate.h |  2 +-
 migration/cpr.c             |  5 ++--
 migration/savevm.c          |  6 +++--
 migration/vmstate-types.c   | 11 +++++----
 migration/vmstate.c         | 56 ++++++++++++++++++++++++++++++---------------
 tests/unit/test-vmstate.c   | 20 ++++++++--------
 ui/vdagent.c                |  2 +-
 15 files changed, 75 insertions(+), 47 deletions(-)

diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 0a1a625b0ea6cf26cb0d799171a57ed3d3ab2442..5ff9f5e6dc0b6b112e36170318c900f893a73c5e 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -1343,7 +1343,7 @@ 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);
+    vmstate_load_state(f, &vmstate_virtio_gpu_scanouts, g, 1, &error_warn);
 
     return 0;
 }
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index c70b5ceebaf1f2b10768bd030526cbb518da2b8d..80545189980f176ca6a3dc9abce7043c8bc2708c 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -934,7 +934,8 @@ void pci_device_save(PCIDevice *s, QEMUFile *f)
 int pci_device_load(PCIDevice *s, QEMUFile *f)
 {
     int ret;
-    ret = vmstate_load_state(f, &vmstate_pci_device, s, s->version_id);
+    ret = vmstate_load_state(f, &vmstate_pci_device, s, s->version_id,
+                             &error_warn);
     /* 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 d2f85b39f30f7fc82e0c600144c0a958e1269b2c..bafcb9b76d81749925e31b5a0a1320b3332ad2cf 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -1136,7 +1136,7 @@ static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f)
 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);
+    return vmstate_load_state(f, &vmstate_virtio_ccw_dev, dev, 1, &error_warn);
 }
 
 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 20f70fb2729de78b9636a6b8c869695dab4f8902..a4812afd0cc7c495080ef03a531c7d279af29b33 100644
--- a/hw/scsi/spapr_vscsi.c
+++ b/hw/scsi/spapr_vscsi.c
@@ -648,7 +648,7 @@ static void *vscsi_load_request(QEMUFile *f, SCSIRequest *sreq)
     assert(!req->active);
 
     memset(req, 0, sizeof(*req));
-    rc = vmstate_load_state(f, &vmstate_spapr_vscsi_req, req, 1);
+    rc = vmstate_load_state(f, &vmstate_spapr_vscsi_req, req, 1, &error_warn);
     if (rc) {
         fprintf(stderr, "VSCSI: failed loading request tag#%u\n", sreq->tag);
         return NULL;
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 4fa692c1a32bcfa4e4939e5fcb64f2bf19905b3b..04d385d7674f444844beeee7364ee0424f762758 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -2795,7 +2795,7 @@ static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f)
         old_addr[bar] = pdev->io_regions[bar].addr;
     }
 
-    ret = vmstate_load_state(f, &vmstate_vfio_pci_config, vdev, 1);
+    ret = vmstate_load_state(f, &vmstate_vfio_pci_config, vdev, 1, &error_warn);
     if (ret) {
         return ret;
     }
diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c
index 532c67107ba1d2978a76cf49f9cdc1de1dea3e11..f14f0487fec3ed30d2cc3aec96950c2121265716 100644
--- a/hw/virtio/virtio-mmio.c
+++ b/hw/virtio/virtio-mmio.c
@@ -34,6 +34,7 @@
 #include "qemu/error-report.h"
 #include "qemu/log.h"
 #include "trace.h"
+#include "qapi/error.h"
 
 static bool virtio_mmio_ioeventfd_enabled(DeviceState *d)
 {
@@ -619,7 +620,7 @@ static int virtio_mmio_load_extra_state(DeviceState *opaque, QEMUFile *f)
 {
     VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
 
-    return vmstate_load_state(f, &vmstate_virtio_mmio, proxy, 1);
+    return vmstate_load_state(f, &vmstate_virtio_mmio, proxy, 1, &error_warn);
 }
 
 static bool virtio_mmio_has_extra_state(DeviceState *opaque)
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 767216d795998708f5716a23ae16c79cd90ff489..07f144d6d1bfbb561b28015de71762601080dc23 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -161,7 +161,7 @@ static int virtio_pci_load_extra_state(DeviceState *d, QEMUFile *f)
 {
     VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
 
-    return vmstate_load_state(f, &vmstate_virtio_pci, proxy, 1);
+    return vmstate_load_state(f, &vmstate_virtio_pci, proxy, 1, &error_warn);
 }
 
 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 2ab1d20769495ea39445b87e3673b076ad172510..aac317a93c3cd55ece9b0fd858c7b2459f8242f9 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -3317,14 +3317,14 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
     }
 
     if (vdc->vmsd) {
-        ret = vmstate_load_state(f, vdc->vmsd, vdev, version_id);
+        ret = vmstate_load_state(f, vdc->vmsd, vdev, version_id, &error_warn);
         if (ret) {
             return ret;
         }
     }
 
     /* Subsections */
-    ret = vmstate_load_state(f, &vmstate_virtio, vdev, 1);
+    ret = vmstate_load_state(f, &vmstate_virtio, vdev, 1, &error_warn);
     if (ret) {
         return ret;
     }
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 1ff7bd9ac425ba67cd5ca7ad97bcf570f9e19abe..056781b1c21e737583f081594d9f88b32adfd674 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -1196,7 +1196,7 @@ extern const VMStateInfo vmstate_info_qlist;
     }
 
 int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
-                       void *opaque, int version_id);
+                       void *opaque, int version_id, Error **errp);
 int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
                        void *opaque, JSONWriter *vmdesc);
 int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription *vmsd,
diff --git a/migration/cpr.c b/migration/cpr.c
index 42ad0b0d500e5de57faf0c6517e216b2d1c0cacf..bdb24736f44e91ba59b6e622a315597c97e7f64d 100644
--- a/migration/cpr.c
+++ b/migration/cpr.c
@@ -202,6 +202,7 @@ int cpr_state_save(MigrationChannel *channel, Error **errp)
 
 int cpr_state_load(MigrationChannel *channel, Error **errp)
 {
+    ERRP_GUARD();
     int ret;
     uint32_t v;
     QEMUFile *f;
@@ -233,9 +234,9 @@ int cpr_state_load(MigrationChannel *channel, Error **errp)
         return -ENOTSUP;
     }
 
-    ret = vmstate_load_state(f, &vmstate_cpr_state, &cpr_state, 1);
+    ret = vmstate_load_state(f, &vmstate_cpr_state, &cpr_state, 1, errp);
     if (ret) {
-        error_setg(errp, "vmstate_load_state error %d", ret);
+        error_prepend(errp, "vmstate_load_state error %d: ", ret);
         qemu_fclose(f);
         return ret;
     }
diff --git a/migration/savevm.c b/migration/savevm.c
index fabbeb296ae987d0c06ba6dafda63720205fecfd..ab947620f724874f325fb9fb59bef50b7c16fb51 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -969,7 +969,8 @@ static int vmstate_load(QEMUFile *f, SaveStateEntry *se)
     if (!se->vmsd) {         /* Old style */
         return se->ops->load_state(f, se->opaque, se->load_version_id);
     }
-    return vmstate_load_state(f, se->vmsd, se->opaque, se->load_version_id);
+    return vmstate_load_state(f, se->vmsd, se->opaque, se->load_version_id,
+                              NULL);
 }
 
 static void vmstate_save_old_style(QEMUFile *f, SaveStateEntry *se,
@@ -2839,7 +2840,8 @@ static int qemu_loadvm_state_header(QEMUFile *f)
             error_report("Configuration section missing");
             return -EINVAL;
         }
-        ret = vmstate_load_state(f, &vmstate_configuration, &savevm_state, 0);
+        ret = vmstate_load_state(f, &vmstate_configuration, &savevm_state, 0,
+                                 NULL);
 
         if (ret) {
             return ret;
diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c
index 741a588b7e18c6d37724b08a0101edc8bc74a0a5..e2020a733d0921d748b6f832a193e5de8d302d5f 100644
--- a/migration/vmstate-types.c
+++ b/migration/vmstate-types.c
@@ -19,6 +19,7 @@
 #include "qemu/error-report.h"
 #include "qemu/queue.h"
 #include "trace.h"
+#include "qapi/error.h"
 
 /* bool */
 
@@ -549,7 +550,7 @@ static int get_tmp(QEMUFile *f, void *pv, size_t size,
 
     /* Writes the parent field which is at the start of the tmp */
     *(void **)tmp = pv;
-    ret = vmstate_load_state(f, vmsd, tmp, version_id);
+    ret = vmstate_load_state(f, vmsd, tmp, version_id, &error_warn);
     g_free(tmp);
     return ret;
 }
@@ -649,7 +650,7 @@ static int get_qtailq(QEMUFile *f, void *pv, size_t unused_size,
 
     while (qemu_get_byte(f)) {
         elm = g_malloc(size);
-        ret = vmstate_load_state(f, vmsd, elm, version_id);
+        ret = vmstate_load_state(f, vmsd, elm, version_id, &error_warn);
         if (ret) {
             return ret;
         }
@@ -803,7 +804,7 @@ static int get_gtree(QEMUFile *f, void *pv, size_t unused_size,
             key = (void *)(uintptr_t)qemu_get_be64(f);
         } else {
             key = g_malloc0(key_size);
-            ret = vmstate_load_state(f, key_vmsd, key, version_id);
+            ret = vmstate_load_state(f, key_vmsd, key, version_id, &error_warn);
             if (ret) {
                 error_report("%s : failed to load %s (%d)",
                              field->name, key_vmsd->name, ret);
@@ -811,7 +812,7 @@ static int get_gtree(QEMUFile *f, void *pv, size_t unused_size,
             }
         }
         val = g_malloc0(val_size);
-        ret = vmstate_load_state(f, val_vmsd, val, version_id);
+        ret = vmstate_load_state(f, val_vmsd, val, version_id, &error_warn);
         if (ret) {
             error_report("%s : failed to load %s (%d)",
                          field->name, val_vmsd->name, ret);
@@ -892,7 +893,7 @@ static int get_qlist(QEMUFile *f, void *pv, size_t unused_size,
 
     while (qemu_get_byte(f)) {
         elm = g_malloc(size);
-        ret = vmstate_load_state(f, vmsd, elm, version_id);
+        ret = vmstate_load_state(f, vmsd, elm, version_id, &error_warn);
         if (ret) {
             error_report("%s: failed to load %s (%d)", field->name,
                          vmsd->name, ret);
diff --git a/migration/vmstate.c b/migration/vmstate.c
index 24451b054c11dfca2d76e24b053d604bb7184e1c..60ff38858cf54277992fa5eddeadb6f3d70edec3 100644
--- a/migration/vmstate.c
+++ b/migration/vmstate.c
@@ -132,29 +132,33 @@ static void vmstate_handle_alloc(void *ptr, const VMStateField *field,
 }
 
 int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
-                       void *opaque, int version_id)
+                       void *opaque, int version_id, Error **errp)
 {
     const VMStateField *field = vmsd->fields;
     int ret = 0;
 
     trace_vmstate_load_state(vmsd->name, version_id);
     if (version_id > vmsd->version_id) {
-        error_report("%s: incoming version_id %d is too new "
-                     "for local version_id %d",
-                     vmsd->name, version_id, vmsd->version_id);
+        error_setg(errp, "%s: incoming version_id %d is too new "
+                   "for local version_id %d",
+                   vmsd->name, version_id, vmsd->version_id);
         trace_vmstate_load_state_end(vmsd->name, "too new", -EINVAL);
         return -EINVAL;
     }
     if  (version_id < vmsd->minimum_version_id) {
-        error_report("%s: incoming version_id %d is too old "
-                     "for local minimum version_id  %d",
-                     vmsd->name, version_id, vmsd->minimum_version_id);
+        error_setg(errp, "%s: incoming version_id %d is too old "
+                   "for local minimum version_id %d",
+                   vmsd->name, version_id, vmsd->minimum_version_id);
         trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL);
         return -EINVAL;
     }
     if (vmsd->pre_load) {
         ret = vmsd->pre_load(opaque);
         if (ret) {
+            error_setg(errp, "VM pre load failed for: '%s', "
+                       "version_id: %d, minimum version_id: %d, ret: %d",
+                       vmsd->name, vmsd->version_id, vmsd->minimum_version_id,
+                       ret);
             return ret;
         }
     }
@@ -192,10 +196,12 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
 
                 if (inner_field->flags & VMS_STRUCT) {
                     ret = vmstate_load_state(f, inner_field->vmsd, curr_elem,
-                                             inner_field->vmsd->version_id);
+                                             inner_field->vmsd->version_id,
+                                             errp);
                 } else if (inner_field->flags & VMS_VSTRUCT) {
                     ret = vmstate_load_state(f, inner_field->vmsd, curr_elem,
-                                             inner_field->struct_version_id);
+                                             inner_field->struct_version_id,
+                                             errp);
                 } else {
                     ret = inner_field->info->get(f, curr_elem, size,
                                                  inner_field);
@@ -208,30 +214,43 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
 
                 if (ret >= 0) {
                     ret = qemu_file_get_error(f);
+                    if (ret < 0) {
+                        error_setg(errp, "Failed to load %s state: %d",
+                                   vmsd->name, ret);
+                        return ret;
+                    }
                 }
                 if (ret < 0) {
                     qemu_file_set_error(f, ret);
-                    error_report("Failed to load %s:%s", vmsd->name,
-                                 field->name);
+                    error_setg(errp,
+                               "Failed to load %s:%s version_id: %d: ",
+                               vmsd->name, field->name, vmsd->version_id);
                     trace_vmstate_load_field_error(field->name, ret);
                     return ret;
                 }
             }
         } else if (field->flags & VMS_MUST_EXIST) {
-            error_report("Input validation failed: %s/%s",
-                         vmsd->name, field->name);
+            error_setg(errp, "Input validation failed: %s/%s version_id: %d",
+                       vmsd->name, field->name, vmsd->version_id);
             return -1;
         }
         field++;
     }
     assert(field->flags == VMS_END);
-    ret = vmstate_subsection_load(f, vmsd, opaque, NULL);
+    ret = vmstate_subsection_load(f, vmsd, opaque, errp);
     if (ret != 0) {
         qemu_file_set_error(f, ret);
         return ret;
     }
     if (vmsd->post_load) {
         ret = vmsd->post_load(opaque, version_id);
+        if (ret < 0) {
+            error_setg(errp,
+                       "VM Post load failed for: %s, version_id: %d, "
+                       "minimum_version: %d, ret: %d",
+                       vmsd->name, vmsd->version_id, vmsd->minimum_version_id,
+                       ret);
+        }
     }
     trace_vmstate_load_state_end(vmsd->name, "end", ret);
     return ret;
@@ -568,6 +587,7 @@ vmstate_get_subsection(const VMStateDescription * const *sub,
 static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
                                    void *opaque, Error **errp)
 {
+    ERRP_GUARD();
     trace_vmstate_subsection_load(vmsd->name);
 
     while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) {
@@ -607,12 +627,12 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
         qemu_file_skip(f, len); /* idstr */
         version_id = qemu_get_be32(f);
 
-        ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
+        ret = vmstate_load_state(f, sub_vmsd, opaque, version_id, errp);
         if (ret) {
             trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(child)");
-            error_setg(errp,
-                       "Loading VM subsection '%s' in '%s' failed: %d",
-                       idstr, vmsd->name, ret);
+            error_prepend(errp,
+                          "Loading VM subsection '%s' in '%s' failed: %d: ",
+                          idstr, vmsd->name, ret);
             return ret;
         }
     }
diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c
index 63f28f26f45691a70936d33e7341d16477a3471f..807deb3531f3079864ac99567d4dece7122571dd 100644
--- a/tests/unit/test-vmstate.c
+++ b/tests/unit/test-vmstate.c
@@ -30,6 +30,7 @@
 #include "../migration/savevm.h"
 #include "qemu/module.h"
 #include "io/channel-file.h"
+#include "qapi/error.h"
 
 static int temp_fd;
 
@@ -114,7 +115,7 @@ static int load_vmstate_one(const VMStateDescription *desc, void *obj,
     qemu_fclose(f);
 
     f = open_test_file(false);
-    ret = vmstate_load_state(f, desc, obj, version);
+    ret = vmstate_load_state(f, desc, obj, version, &error_warn);
     if (ret) {
         g_assert(qemu_file_get_error(f));
     } else{
@@ -365,7 +366,7 @@ static void test_load_v1(void)
 
     QEMUFile *loading = open_test_file(false);
     TestStruct obj = { .b = 200, .e = 500, .f = 600 };
-    vmstate_load_state(loading, &vmstate_versioned, &obj, 1);
+    vmstate_load_state(loading, &vmstate_versioned, &obj, 1, &error_warn);
     g_assert(!qemu_file_get_error(loading));
     g_assert_cmpint(obj.a, ==, 10);
     g_assert_cmpint(obj.b, ==, 200);
@@ -391,7 +392,7 @@ static void test_load_v2(void)
 
     QEMUFile *loading = open_test_file(false);
     TestStruct obj;
-    vmstate_load_state(loading, &vmstate_versioned, &obj, 2);
+    vmstate_load_state(loading, &vmstate_versioned, &obj, 2, &error_warn);
     g_assert_cmpint(obj.a, ==, 10);
     g_assert_cmpint(obj.b, ==, 20);
     g_assert_cmpint(obj.c, ==, 30);
@@ -480,7 +481,7 @@ static void test_load_noskip(void)
 
     QEMUFile *loading = open_test_file(false);
     TestStruct obj = { .skip_c_e = false };
-    vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
+    vmstate_load_state(loading, &vmstate_skipping, &obj, 2, &error_warn);
     g_assert(!qemu_file_get_error(loading));
     g_assert_cmpint(obj.a, ==, 10);
     g_assert_cmpint(obj.b, ==, 20);
@@ -504,7 +505,7 @@ static void test_load_skip(void)
 
     QEMUFile *loading = open_test_file(false);
     TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 };
-    vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
+    vmstate_load_state(loading, &vmstate_skipping, &obj, 2, &error_warn);
     g_assert(!qemu_file_get_error(loading));
     g_assert_cmpint(obj.a, ==, 10);
     g_assert_cmpint(obj.b, ==, 20);
@@ -773,7 +774,7 @@ static void test_load_q(void)
     TestQtailq tgt;
 
     QTAILQ_INIT(&tgt.q);
-    vmstate_load_state(fload, &vmstate_q, &tgt, 1);
+    vmstate_load_state(fload, &vmstate_q, &tgt, 1, &error_warn);
     char eof = qemu_get_byte(fload);
     g_assert(!qemu_file_get_error(fload));
     g_assert_cmpint(tgt.i16, ==, obj_q.i16);
@@ -1127,7 +1128,7 @@ static void test_gtree_load_domain(void)
 
     fload = open_test_file(false);
 
-    vmstate_load_state(fload, &vmstate_domain, dest_domain, 1);
+    vmstate_load_state(fload, &vmstate_domain, dest_domain, 1, &error_warn);
     eof = qemu_get_byte(fload);
     g_assert(!qemu_file_get_error(fload));
     g_assert_cmpint(orig_domain->id, ==, dest_domain->id);
@@ -1241,7 +1242,7 @@ static void test_gtree_load_iommu(void)
     qemu_fclose(fsave);
 
     fload = open_test_file(false);
-    vmstate_load_state(fload, &vmstate_iommu, dest_iommu, 1);
+    vmstate_load_state(fload, &vmstate_iommu, dest_iommu, 1, &error_warn);
     eof = qemu_get_byte(fload);
     g_assert(!qemu_file_get_error(fload));
     g_assert_cmpint(orig_iommu->id, ==, dest_iommu->id);
@@ -1376,7 +1377,8 @@ static void test_load_qlist(void)
     qemu_fclose(fsave);
 
     fload = open_test_file(false);
-    vmstate_load_state(fload, &vmstate_container, dest_container, 1);
+    vmstate_load_state(fload, &vmstate_container, dest_container, 1,
+                       &error_warn);
     eof = qemu_get_byte(fload);
     g_assert(!qemu_file_get_error(fload));
     g_assert_cmpint(eof, ==, QEMU_VM_EOF);
diff --git a/ui/vdagent.c b/ui/vdagent.c
index c0746fe5b168fdc7aeb4866de2ba0c3387566649..b9a22a689d9acfeafb862ae73308db6fbd52331e 100644
--- a/ui/vdagent.c
+++ b/ui/vdagent.c
@@ -1008,7 +1008,7 @@ static int get_cbinfo(QEMUFile *f, void *pv, size_t size,
 
     vdagent_clipboard_peer_register(vd);
 
-    ret = vmstate_load_state(f, &vmstate_cbinfo_array, &cbinfo, 0);
+    ret = vmstate_load_state(f, &vmstate_cbinfo_array, &cbinfo, 0, &error_warn);
     if (ret) {
         return ret;
     }

-- 
2.50.1



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

* [PATCH v9 03/27] migration: push Error **errp into qemu_loadvm_state_header()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
  2025-08-05 18:25 ` [PATCH v9 01/27] migration: push Error **errp into vmstate_subsection_load() Arun Menon
  2025-08-05 18:25 ` [PATCH v9 02/27] migration: push Error **errp into vmstate_load_state() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-05 18:25 ` [PATCH v9 04/27] migration: push Error **errp into vmstate_load() Arun Menon
                   ` (23 subsequent siblings)
  26 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.
It is ensured that qemu_loadvm_state_header() must report an error
in errp, in case of failure.

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/savevm.c | 26 +++++++++++++++++---------
 1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/migration/savevm.c b/migration/savevm.c
index ab947620f724874f325fb9fb59bef50b7c16fb51..b517e3bec651cb5b4050564f7dcbbf34c80a5035 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2814,35 +2814,42 @@ qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type)
     return 0;
 }
 
-static int qemu_loadvm_state_header(QEMUFile *f)
+static int qemu_loadvm_state_header(QEMUFile *f, Error **errp)
 {
     unsigned int v;
     int ret;
 
     v = qemu_get_be32(f);
     if (v != QEMU_VM_FILE_MAGIC) {
-        error_report("Not a migration stream");
+        error_setg(errp, "Not a migration stream, magic: %x != %x",
+                   v, QEMU_VM_FILE_MAGIC);
         return -EINVAL;
     }
 
     v = qemu_get_be32(f);
     if (v == QEMU_VM_FILE_VERSION_COMPAT) {
-        error_report("SaveVM v2 format is obsolete and don't work anymore");
+        error_setg(errp,
+                   "SaveVM v2 format is obsolete and no longer supported");
+
         return -ENOTSUP;
     }
     if (v != QEMU_VM_FILE_VERSION) {
-        error_report("Unsupported migration stream version");
+        error_setg(errp, "Unsupported migration stream version, "
+                   "file version %x != %x",
+                   v, QEMU_VM_FILE_VERSION);
         return -ENOTSUP;
     }
 
     if (migrate_get_current()->send_configuration) {
-        if (qemu_get_byte(f) != QEMU_VM_CONFIGURATION) {
-            error_report("Configuration section missing");
+        v = qemu_get_byte(f);
+        if (v != QEMU_VM_CONFIGURATION) {
+            error_setg(errp, "Configuration section missing, %x != %x",
+                       v, QEMU_VM_CONFIGURATION);
             return -EINVAL;
         }
-        ret = vmstate_load_state(f, &vmstate_configuration, &savevm_state, 0,
-                                 NULL);
 
+        ret = vmstate_load_state(f, &vmstate_configuration, &savevm_state, 0,
+                                 errp);
         if (ret) {
             return ret;
         }
@@ -3119,8 +3126,9 @@ int qemu_loadvm_state(QEMUFile *f)
 
     qemu_loadvm_thread_pool_create(mis);
 
-    ret = qemu_loadvm_state_header(f);
+    ret = qemu_loadvm_state_header(f, &local_err);
     if (ret) {
+        error_report_err(local_err);
         return ret;
     }
 

-- 
2.50.1



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

* [PATCH v9 04/27] migration: push Error **errp into vmstate_load()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (2 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 03/27] migration: push Error **errp into qemu_loadvm_state_header() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-05 18:25 ` [PATCH v9 05/27] migration: push Error **errp into loadvm_process_command() Arun Menon
                   ` (22 subsequent siblings)
  26 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.
It is ensured that vmstate_load() must report an error
in errp, in case of failure.

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/savevm.c | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/migration/savevm.c b/migration/savevm.c
index b517e3bec651cb5b4050564f7dcbbf34c80a5035..f37c4455dcf839d46f026fc7c7ff02e2dfffe7b4 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -963,14 +963,20 @@ void vmstate_unregister(VMStateIf *obj, const VMStateDescription *vmsd,
     }
 }
 
-static int vmstate_load(QEMUFile *f, SaveStateEntry *se)
+static int vmstate_load(QEMUFile *f, SaveStateEntry *se, Error **errp)
 {
+    int ret;
     trace_vmstate_load(se->idstr, se->vmsd ? se->vmsd->name : "(old)");
     if (!se->vmsd) {         /* Old style */
-        return se->ops->load_state(f, se->opaque, se->load_version_id);
+        ret = se->ops->load_state(f, se->opaque, se->load_version_id);
+        if (ret < 0) {
+            error_setg(errp, "Failed to load VM version_id: %d, ret: %d",
+                       se->load_version_id, ret);
+        }
+        return ret;
     }
     return vmstate_load_state(f, se->vmsd, se->opaque, se->load_version_id,
-                              NULL);
+                              errp);
 }
 
 static void vmstate_save_old_style(QEMUFile *f, SaveStateEntry *se,
@@ -2741,7 +2747,7 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type)
         start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
     }
 
-    ret = vmstate_load(f, se);
+    ret = vmstate_load(f, se, NULL);
     if (ret < 0) {
         error_report("error while loading state for instance 0x%"PRIx32" of"
                      " device '%s'", instance_id, idstr);
@@ -2794,7 +2800,7 @@ qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type)
         start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
     }
 
-    ret = vmstate_load(f, se);
+    ret = vmstate_load(f, se, NULL);
     if (ret < 0) {
         error_report("error while loading state section id %d(%s)",
                      section_id, se->idstr);

-- 
2.50.1



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

* [PATCH v9 05/27] migration: push Error **errp into loadvm_process_command()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (3 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 04/27] migration: push Error **errp into vmstate_load() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-06  8:31   ` Marc-André Lureau
  2025-08-05 18:25 ` [PATCH v9 06/27] migration: push Error **errp into loadvm_handle_cmd_packaged() Arun Menon
                   ` (21 subsequent siblings)
  26 siblings, 1 reply; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.
It is ensured that loadvm_process_command() must report an error
in errp, in case of failure.

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/savevm.c | 72 +++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 52 insertions(+), 20 deletions(-)

diff --git a/migration/savevm.c b/migration/savevm.c
index f37c4455dcf839d46f026fc7c7ff02e2dfffe7b4..cb673f43b174249ff1525dba41284de2e5a70735 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2546,12 +2546,13 @@ static int loadvm_postcopy_handle_switchover_start(void)
  * LOADVM_QUIT All good, but exit the loop
  * <0          Error
  */
-static int loadvm_process_command(QEMUFile *f)
+static int loadvm_process_command(QEMUFile *f, Error **errp)
 {
     MigrationIncomingState *mis = migration_incoming_get_current();
     uint16_t cmd;
     uint16_t len;
     uint32_t tmp32;
+    int ret;
 
     cmd = qemu_get_be16(f);
     len = qemu_get_be16(f);
@@ -2562,16 +2563,16 @@ static int loadvm_process_command(QEMUFile *f)
     }
 
     if (cmd >= MIG_CMD_MAX || cmd == MIG_CMD_INVALID) {
-        error_report("MIG_CMD 0x%x unknown (len 0x%x)", cmd, len);
+        error_setg(errp, "MIG_CMD 0x%x unknown (len 0x%x)", cmd, len);
         return -EINVAL;
     }
 
     trace_loadvm_process_command(mig_cmd_args[cmd].name, len);
 
     if (mig_cmd_args[cmd].len != -1 && mig_cmd_args[cmd].len != len) {
-        error_report("%s received with bad length - expecting %zu, got %d",
-                     mig_cmd_args[cmd].name,
-                     (size_t)mig_cmd_args[cmd].len, len);
+        error_setg(errp, "%s received with bad length - expecting %zu, got %d",
+                   mig_cmd_args[cmd].name,
+                   (size_t)mig_cmd_args[cmd].len, len);
         return -ERANGE;
     }
 
@@ -2594,11 +2595,10 @@ static int loadvm_process_command(QEMUFile *f)
          * been created.
          */
         if (migrate_switchover_ack() && !mis->switchover_ack_pending_num) {
-            int ret = migrate_send_rp_switchover_ack(mis);
+            ret = migrate_send_rp_switchover_ack(mis);
             if (ret) {
-                error_report(
-                    "Could not send switchover ack RP MSG, err %d (%s)", ret,
-                    strerror(-ret));
+                error_setg_errno(errp, -ret,
+                                 "Could not send switchover ack RP MSG");
                 return ret;
             }
         }
@@ -2608,39 +2608,71 @@ static int loadvm_process_command(QEMUFile *f)
         tmp32 = qemu_get_be32(f);
         trace_loadvm_process_command_ping(tmp32);
         if (!mis->to_src_file) {
-            error_report("CMD_PING (0x%x) received with no return path",
-                         tmp32);
+            error_setg(errp, "CMD_PING (0x%x) received with no return path",
+                       tmp32);
             return -1;
         }
         migrate_send_rp_pong(mis, tmp32);
         break;
 
     case MIG_CMD_PACKAGED:
-        return loadvm_handle_cmd_packaged(mis);
+        ret = loadvm_handle_cmd_packaged(mis);
+        if (ret < 0) {
+            error_setg(errp, "Failed to load device state command: %d", ret);
+        }
+        return ret;
 
     case MIG_CMD_POSTCOPY_ADVISE:
-        return loadvm_postcopy_handle_advise(mis, len);
+        ret = loadvm_postcopy_handle_advise(mis, len);
+        if (ret < 0) {
+            error_setg(errp, "Failed to load device state command: %d", ret);
+        }
+        return ret;
 
     case MIG_CMD_POSTCOPY_LISTEN:
-        return loadvm_postcopy_handle_listen(mis);
+        ret = loadvm_postcopy_handle_listen(mis);
+        if (ret < 0) {
+            error_setg(errp, "Failed to load device state command: %d", ret);
+        }
+        return ret;
 
     case MIG_CMD_POSTCOPY_RUN:
-        return loadvm_postcopy_handle_run(mis);
+        ret = loadvm_postcopy_handle_run(mis);
+        if (ret < 0) {
+            error_setg(errp, "Failed to load device state command: %d", ret);
+        }
+        return ret;
 
     case MIG_CMD_POSTCOPY_RAM_DISCARD:
-        return loadvm_postcopy_ram_handle_discard(mis, len);
+        ret = loadvm_postcopy_ram_handle_discard(mis, len);
+        if (ret < 0) {
+            error_setg(errp, "Failed to load device state command: %d", ret);
+        }
+        return ret;
 
     case MIG_CMD_POSTCOPY_RESUME:
         return loadvm_postcopy_handle_resume(mis);
 
     case MIG_CMD_RECV_BITMAP:
-        return loadvm_handle_recv_bitmap(mis, len);
+        ret = loadvm_handle_recv_bitmap(mis, len);
+        if (ret < 0) {
+            error_setg(errp, "Failed to load device state command: %d", ret);
+        }
+        return ret;
 
     case MIG_CMD_ENABLE_COLO:
-        return loadvm_process_enable_colo(mis);
+        ret = loadvm_process_enable_colo(mis);
+        if (ret < 0) {
+            error_setg(errp, "Failed to load device state command: %d", ret);
+        }
+        return ret;
 
     case MIG_CMD_SWITCHOVER_START:
-        return loadvm_postcopy_handle_switchover_start();
+        ret = loadvm_postcopy_handle_switchover_start();
+        if (ret < 0) {
+            error_setg(errp, "Failed to load device state command: %d", ret);
+        }
+        return ret;
     }
 
     return 0;
@@ -3074,7 +3106,7 @@ retry:
             }
             break;
         case QEMU_VM_COMMAND:
-            ret = loadvm_process_command(f);
+            ret = loadvm_process_command(f, NULL);
             trace_qemu_loadvm_state_section_command(ret);
             if ((ret < 0) || (ret == LOADVM_QUIT)) {
                 goto out;

-- 
2.50.1



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

* [PATCH v9 06/27] migration: push Error **errp into loadvm_handle_cmd_packaged()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (4 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 05/27] migration: push Error **errp into loadvm_process_command() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-05 18:25 ` [PATCH v9 07/27] migration: push Error **errp into qemu_loadvm_state() Arun Menon
                   ` (20 subsequent siblings)
  26 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon,
	Daniel P. Berrangé

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.
It is ensured that loadvm_handle_cmd_packaged() must report an error
in errp, in case of failure.

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/savevm.c | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/migration/savevm.c b/migration/savevm.c
index cb673f43b174249ff1525dba41284de2e5a70735..1bd27efe437d4d911728d776e995490d0a45dcf5 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2405,7 +2405,7 @@ static int loadvm_postcopy_handle_resume(MigrationIncomingState *mis)
  * Returns: Negative values on error
  *
  */
-static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis)
+static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis, Error **errp)
 {
     int ret;
     size_t length;
@@ -2415,7 +2415,7 @@ static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis)
     trace_loadvm_handle_cmd_packaged(length);
 
     if (length > MAX_VM_CMD_PACKAGED_SIZE) {
-        error_report("Unreasonably large packaged state: %zu", length);
+        error_setg(errp, "Unreasonably large packaged state: %zu", length);
         return -1;
     }
 
@@ -2426,8 +2426,8 @@ static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis)
                           length);
     if (ret != length) {
         object_unref(OBJECT(bioc));
-        error_report("CMD_PACKAGED: Buffer receive fail ret=%d length=%zu",
-                     ret, length);
+        error_setg(errp, "CMD_PACKAGED: Buffer receive fail ret=%d length=%zu",
+                   ret, length);
         return (ret < 0) ? ret : -EAGAIN;
     }
     bioc->usage += length;
@@ -2457,6 +2457,9 @@ static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis)
     } while (1);
 
     ret = qemu_loadvm_state_main(packf, mis);
+    if (ret < 0) {
+        error_setg(errp, "VM state load failed: %d", ret);
+    }
     trace_loadvm_handle_cmd_packaged_main(ret);
     qemu_fclose(packf);
     object_unref(OBJECT(bioc));
@@ -2616,11 +2619,7 @@ static int loadvm_process_command(QEMUFile *f, Error **errp)
         break;
 
     case MIG_CMD_PACKAGED:
-        ret = loadvm_handle_cmd_packaged(mis);
-        if (ret < 0) {
-            error_setg(errp, "Failed to load device state command: %d", ret);
-        }
-        return ret;
+        return loadvm_handle_cmd_packaged(mis, errp);
 
     case MIG_CMD_POSTCOPY_ADVISE:
         ret = loadvm_postcopy_handle_advise(mis, len);

-- 
2.50.1



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

* [PATCH v9 07/27] migration: push Error **errp into qemu_loadvm_state()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (5 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 06/27] migration: push Error **errp into loadvm_handle_cmd_packaged() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-06  5:17   ` Akihiko Odaki
  2025-08-06  7:24   ` Marc-André Lureau
  2025-08-05 18:25 ` [PATCH v9 08/27] migration: push Error **errp into qemu_load_device_state() Arun Menon
                   ` (19 subsequent siblings)
  26 siblings, 2 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.
It is ensured that qemu_loadvm_state() must report an error
in errp, in case of failure.

When postcopy live migration runs, the device states are loaded by
both the qemu coroutine process_incoming_migration_co() and the
postcopy_ram_listen_thread(). Therefore, it is important that the
coroutine also reports the error in case of failure, with
error_report_err(). Otherwise, the source qemu will not display
any errors before going into the postcopy pause state.

Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/migration.c |  7 ++++---
 migration/savevm.c    | 31 +++++++++++++++++++------------
 migration/savevm.h    |  2 +-
 3 files changed, 24 insertions(+), 16 deletions(-)

diff --git a/migration/migration.c b/migration/migration.c
index 10c216d25dec01f206eacad2edd24d21f00e614c..bb7d5e1dee52692cbea1d2c8fdca541e6a75bedb 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -881,7 +881,7 @@ process_incoming_migration_co(void *opaque)
                       MIGRATION_STATUS_ACTIVE);
 
     mis->loadvm_co = qemu_coroutine_self();
-    ret = qemu_loadvm_state(mis->from_src_file);
+    ret = qemu_loadvm_state(mis->from_src_file, &local_err);
     mis->loadvm_co = NULL;
 
     trace_vmstate_downtime_checkpoint("dst-precopy-loadvm-completed");
@@ -908,7 +908,8 @@ process_incoming_migration_co(void *opaque)
     }
 
     if (ret < 0) {
-        error_setg(&local_err, "load of migration failed: %s", strerror(-ret));
+        error_prepend(&local_err, "load of migration failed: %s: ",
+                      strerror(-ret));
         goto fail;
     }
 
@@ -924,7 +925,7 @@ fail:
     migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
                       MIGRATION_STATUS_FAILED);
     migrate_set_error(s, local_err);
-    error_free(local_err);
+    error_report_err(local_err);
 
     migration_incoming_state_destroy();
 
diff --git a/migration/savevm.c b/migration/savevm.c
index 1bd27efe437d4d911728d776e995490d0a45dcf5..ca166ebd397ad80836ed2f9cb20a92f704fd4ed5 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -3149,28 +3149,24 @@ out:
     return ret;
 }
 
-int qemu_loadvm_state(QEMUFile *f)
+int qemu_loadvm_state(QEMUFile *f, Error **errp)
 {
     MigrationState *s = migrate_get_current();
     MigrationIncomingState *mis = migration_incoming_get_current();
-    Error *local_err = NULL;
     int ret;
 
-    if (qemu_savevm_state_blocked(&local_err)) {
-        error_report_err(local_err);
+    if (qemu_savevm_state_blocked(errp)) {
         return -EINVAL;
     }
 
     qemu_loadvm_thread_pool_create(mis);
 
-    ret = qemu_loadvm_state_header(f, &local_err);
+    ret = qemu_loadvm_state_header(f, errp);
     if (ret) {
-        error_report_err(local_err);
         return ret;
     }
 
-    if (qemu_loadvm_state_setup(f, &local_err) != 0) {
-        error_report_err(local_err);
+    if (qemu_loadvm_state_setup(f, errp) != 0) {
         return -EINVAL;
     }
 
@@ -3181,6 +3177,9 @@ int qemu_loadvm_state(QEMUFile *f)
     cpu_synchronize_all_pre_loadvm();
 
     ret = qemu_loadvm_state_main(f, mis);
+    if (ret < 0) {
+        error_setg(errp, "Load VM state failed: %d", ret);
+    }
     qemu_event_set(&mis->main_thread_load_event);
 
     trace_qemu_loadvm_state_post_main(ret);
@@ -3198,8 +3197,14 @@ int qemu_loadvm_state(QEMUFile *f)
         if (migrate_has_error(migrate_get_current()) ||
             !qemu_loadvm_thread_pool_wait(s, mis)) {
             ret = -EINVAL;
+            error_setg(errp,
+                       "Error while loading VM state: "
+                       "Migration stream has error");
         } else {
             ret = qemu_file_get_error(f);
+            if (ret < 0) {
+                error_setg(errp, "Error while loading vmstate : %d", ret);
+            }
         }
     }
     /*
@@ -3464,6 +3469,7 @@ void qmp_xen_save_devices_state(const char *filename, bool has_live, bool live,
 
 void qmp_xen_load_devices_state(const char *filename, Error **errp)
 {
+    ERRP_GUARD();
     QEMUFile *f;
     QIOChannelFile *ioc;
     int ret;
@@ -3485,10 +3491,10 @@ void qmp_xen_load_devices_state(const char *filename, Error **errp)
     f = qemu_file_new_input(QIO_CHANNEL(ioc));
     object_unref(OBJECT(ioc));
 
-    ret = qemu_loadvm_state(f);
+    ret = qemu_loadvm_state(f, errp);
     qemu_fclose(f);
     if (ret < 0) {
-        error_setg(errp, "loading Xen device state failed");
+        error_prepend(errp, "loading Xen device state failed: ");
     }
     migration_incoming_state_destroy();
 }
@@ -3496,6 +3502,7 @@ void qmp_xen_load_devices_state(const char *filename, Error **errp)
 bool load_snapshot(const char *name, const char *vmstate,
                    bool has_devices, strList *devices, Error **errp)
 {
+    ERRP_GUARD();
     BlockDriverState *bs_vm_state;
     QEMUSnapshotInfo sn;
     QEMUFile *f;
@@ -3559,13 +3566,13 @@ bool load_snapshot(const char *name, const char *vmstate,
         ret = -EINVAL;
         goto err_drain;
     }
-    ret = qemu_loadvm_state(f);
+    ret = qemu_loadvm_state(f, errp);
     migration_incoming_state_destroy();
 
     bdrv_drain_all_end();
 
     if (ret < 0) {
-        error_setg(errp, "Error %d while loading VM state", ret);
+        error_prepend(errp, "Error %d while loading VM state: ", ret);
         return false;
     }
 
diff --git a/migration/savevm.h b/migration/savevm.h
index 2d5e9c716686f06720325e82fe90c75335ced1de..b80770b7461a60e2ad6ba5e24a7baeae73d90955 100644
--- a/migration/savevm.h
+++ b/migration/savevm.h
@@ -64,7 +64,7 @@ void qemu_savevm_send_colo_enable(QEMUFile *f);
 void qemu_savevm_live_state(QEMUFile *f);
 int qemu_save_device_state(QEMUFile *f);
 
-int qemu_loadvm_state(QEMUFile *f);
+int qemu_loadvm_state(QEMUFile *f, Error **errp);
 void qemu_loadvm_state_cleanup(MigrationIncomingState *mis);
 int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis);
 int qemu_load_device_state(QEMUFile *f);

-- 
2.50.1



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

* [PATCH v9 08/27] migration: push Error **errp into qemu_load_device_state()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (6 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 07/27] migration: push Error **errp into qemu_loadvm_state() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-06  7:27   ` Marc-André Lureau
  2025-08-05 18:25 ` [PATCH v9 09/27] migration: push Error **errp into qemu_loadvm_state_main() Arun Menon
                   ` (18 subsequent siblings)
  26 siblings, 1 reply; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.
It is ensured that qemu_load_device_state() must report an error
in errp, in case of failure.

Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/colo.c   | 4 ++--
 migration/savevm.c | 5 +++--
 migration/savevm.h | 2 +-
 3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/migration/colo.c b/migration/colo.c
index e0f713c837f5da25d67afbd02ceb6c54024ca3af..0ba22ee76a13e313793f653f43a728e3c433bbc1 100644
--- a/migration/colo.c
+++ b/migration/colo.c
@@ -729,9 +729,9 @@ static void colo_incoming_process_checkpoint(MigrationIncomingState *mis,
     bql_lock();
     vmstate_loading = true;
     colo_flush_ram_cache();
-    ret = qemu_load_device_state(fb);
+    ret = qemu_load_device_state(fb, errp);
     if (ret < 0) {
-        error_setg(errp, "COLO: load device state failed");
+        error_prepend(errp, "COLO: load device state failed: ");
         vmstate_loading = false;
         bql_unlock();
         return;
diff --git a/migration/savevm.c b/migration/savevm.c
index ca166ebd397ad80836ed2f9cb20a92f704fd4ed5..f3b91c8ae0eee6078406081f0bd7f686fed28601 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -3253,15 +3253,16 @@ int qemu_loadvm_state(QEMUFile *f, Error **errp)
     return ret;
 }
 
-int qemu_load_device_state(QEMUFile *f)
+int qemu_load_device_state(QEMUFile *f, Error **errp)
 {
+    ERRP_GUARD();
     MigrationIncomingState *mis = migration_incoming_get_current();
     int ret;
 
     /* Load QEMU_VM_SECTION_FULL section */
     ret = qemu_loadvm_state_main(f, mis);
     if (ret < 0) {
-        error_report("Failed to load device state: %d", ret);
+        error_setg(errp, "Failed to load device state: %d", ret);
         return ret;
     }
 
diff --git a/migration/savevm.h b/migration/savevm.h
index b80770b7461a60e2ad6ba5e24a7baeae73d90955..b12681839f0b1afa3255e45215d99c13a224b19f 100644
--- a/migration/savevm.h
+++ b/migration/savevm.h
@@ -67,7 +67,7 @@ int qemu_save_device_state(QEMUFile *f);
 int qemu_loadvm_state(QEMUFile *f, Error **errp);
 void qemu_loadvm_state_cleanup(MigrationIncomingState *mis);
 int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis);
-int qemu_load_device_state(QEMUFile *f);
+int qemu_load_device_state(QEMUFile *f, Error **errp);
 int qemu_loadvm_approve_switchover(void);
 int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
         bool in_postcopy);

-- 
2.50.1



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

* [PATCH v9 09/27] migration: push Error **errp into qemu_loadvm_state_main()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (7 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 08/27] migration: push Error **errp into qemu_load_device_state() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-06  5:14   ` Akihiko Odaki
  2025-08-06  7:34   ` Marc-André Lureau
  2025-08-05 18:25 ` [PATCH v9 10/27] migration: push Error **errp into qemu_loadvm_section_start_full() Arun Menon
                   ` (17 subsequent siblings)
  26 siblings, 2 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon,
	Daniel P. Berrangé

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.
It is ensured that qemu_loadvm_state_main() must report an error
in errp, in case of failure.
loadvm_process_command also sets the errp object explicitly.

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/colo.c   |  5 +++--
 migration/savevm.c | 33 ++++++++++++++++++++-------------
 migration/savevm.h |  3 ++-
 3 files changed, 25 insertions(+), 16 deletions(-)

diff --git a/migration/colo.c b/migration/colo.c
index 0ba22ee76a13e313793f653f43a728e3c433bbc1..a96e4dba15516b71d1b315c736c3b4879ff04e58 100644
--- a/migration/colo.c
+++ b/migration/colo.c
@@ -659,6 +659,7 @@ void migrate_start_colo_process(MigrationState *s)
 static void colo_incoming_process_checkpoint(MigrationIncomingState *mis,
                       QEMUFile *fb, QIOChannelBuffer *bioc, Error **errp)
 {
+    ERRP_GUARD();
     uint64_t total_size;
     uint64_t value;
     Error *local_err = NULL;
@@ -686,11 +687,11 @@ static void colo_incoming_process_checkpoint(MigrationIncomingState *mis,
 
     bql_lock();
     cpu_synchronize_all_states();
-    ret = qemu_loadvm_state_main(mis->from_src_file, mis);
+    ret = qemu_loadvm_state_main(mis->from_src_file, mis, errp);
     bql_unlock();
 
     if (ret < 0) {
-        error_setg(errp, "Load VM's live state (ram) error");
+        error_prepend(errp, "Load VM's live state (ram) error: ");
         return;
     }
 
diff --git a/migration/savevm.c b/migration/savevm.c
index f3b91c8ae0eee6078406081f0bd7f686fed28601..ad3dd9b172afc541f104d2187a79bafa8e380466 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2105,7 +2105,7 @@ static void *postcopy_ram_listen_thread(void *opaque)
     qemu_file_set_blocking(f, true);
 
     /* TODO: sanity check that only postcopiable data will be loaded here */
-    load_res = qemu_loadvm_state_main(f, mis);
+    load_res = qemu_loadvm_state_main(f, mis, NULL);
 
     /*
      * This is tricky, but, mis->from_src_file can change after it
@@ -2407,6 +2407,7 @@ static int loadvm_postcopy_handle_resume(MigrationIncomingState *mis)
  */
 static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis, Error **errp)
 {
+    ERRP_GUARD();
     int ret;
     size_t length;
     QIOChannelBuffer *bioc;
@@ -2456,9 +2457,9 @@ static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis, Error **errp)
         qemu_coroutine_yield();
     } while (1);
 
-    ret = qemu_loadvm_state_main(packf, mis);
+    ret = qemu_loadvm_state_main(packf, mis, errp);
     if (ret < 0) {
-        error_setg(errp, "VM state load failed: %d", ret);
+        error_prepend(errp, "Loading VM state failed: %d: ", ret);
     }
     trace_loadvm_handle_cmd_packaged_main(ret);
     qemu_fclose(packf);
@@ -3074,8 +3075,10 @@ static bool postcopy_pause_incoming(MigrationIncomingState *mis)
     return true;
 }
 
-int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis)
+int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis,
+                           Error **errp)
 {
+    ERRP_GUARD();
     uint8_t section_type;
     int ret = 0;
 
@@ -3083,8 +3086,10 @@ retry:
     while (true) {
         section_type = qemu_get_byte(f);
 
-        ret = qemu_file_get_error_obj_any(f, mis->postcopy_qemufile_dst, NULL);
+        ret = qemu_file_get_error_obj_any(f, mis->postcopy_qemufile_dst, errp);
         if (ret) {
+            error_prepend(errp, "Failed to load device state section ID: %d: ",
+                          ret);
             break;
         }
 
@@ -3105,7 +3110,7 @@ retry:
             }
             break;
         case QEMU_VM_COMMAND:
-            ret = loadvm_process_command(f, NULL);
+            ret = loadvm_process_command(f, errp);
             trace_qemu_loadvm_state_section_command(ret);
             if ((ret < 0) || (ret == LOADVM_QUIT)) {
                 goto out;
@@ -3115,7 +3120,7 @@ retry:
             /* This is the end of migration */
             goto out;
         default:
-            error_report("Unknown savevm section type %d", section_type);
+            error_setg(errp, "Unknown savevm section type %d", section_type);
             ret = -EINVAL;
             goto out;
         }
@@ -3123,6 +3128,9 @@ retry:
 
 out:
     if (ret < 0) {
+        if (*errp == NULL) {
+            error_setg(errp, "Loading VM state failed: %d", ret);
+        }
         qemu_file_set_error(f, ret);
 
         /* Cancel bitmaps incoming regardless of recovery */
@@ -3143,6 +3151,8 @@ out:
             migrate_postcopy_ram() && postcopy_pause_incoming(mis)) {
             /* Reset f to point to the newly created channel */
             f = mis->from_src_file;
+            error_free(*errp);
+            *errp = NULL;
             goto retry;
         }
     }
@@ -3176,10 +3186,7 @@ int qemu_loadvm_state(QEMUFile *f, Error **errp)
 
     cpu_synchronize_all_pre_loadvm();
 
-    ret = qemu_loadvm_state_main(f, mis);
-    if (ret < 0) {
-        error_setg(errp, "Load VM state failed: %d", ret);
-    }
+    ret = qemu_loadvm_state_main(f, mis, errp);
     qemu_event_set(&mis->main_thread_load_event);
 
     trace_qemu_loadvm_state_post_main(ret);
@@ -3260,9 +3267,9 @@ int qemu_load_device_state(QEMUFile *f, Error **errp)
     int ret;
 
     /* Load QEMU_VM_SECTION_FULL section */
-    ret = qemu_loadvm_state_main(f, mis);
+    ret = qemu_loadvm_state_main(f, mis, errp);
     if (ret < 0) {
-        error_setg(errp, "Failed to load device state: %d", ret);
+        error_prepend(errp, "Failed to load device state: %d: ", ret);
         return ret;
     }
 
diff --git a/migration/savevm.h b/migration/savevm.h
index b12681839f0b1afa3255e45215d99c13a224b19f..c337e3e3d111a7f28a57b90f61e8f70b71803d4e 100644
--- a/migration/savevm.h
+++ b/migration/savevm.h
@@ -66,7 +66,8 @@ int qemu_save_device_state(QEMUFile *f);
 
 int qemu_loadvm_state(QEMUFile *f, Error **errp);
 void qemu_loadvm_state_cleanup(MigrationIncomingState *mis);
-int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis);
+int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis,
+                           Error **errp);
 int qemu_load_device_state(QEMUFile *f, Error **errp);
 int qemu_loadvm_approve_switchover(void);
 int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,

-- 
2.50.1



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

* [PATCH v9 10/27] migration: push Error **errp into qemu_loadvm_section_start_full()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (8 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 09/27] migration: push Error **errp into qemu_loadvm_state_main() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-06  7:37   ` Marc-André Lureau
  2025-08-05 18:25 ` [PATCH v9 11/27] migration: push Error **errp into qemu_loadvm_section_part_end() Arun Menon
                   ` (16 subsequent siblings)
  26 siblings, 1 reply; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.
It is ensured that qemu_loadvm_section_start_full() must report an error
in errp, in case of failure.

Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/savevm.c | 36 ++++++++++++++++++++----------------
 1 file changed, 20 insertions(+), 16 deletions(-)

diff --git a/migration/savevm.c b/migration/savevm.c
index ad3dd9b172afc541f104d2187a79bafa8e380466..e337e3c61e7627f09b853bf5e9b845c38cb5f082 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2722,8 +2722,9 @@ static bool check_section_footer(QEMUFile *f, SaveStateEntry *se)
 }
 
 static int
-qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type)
+qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type, Error **errp)
 {
+    ERRP_GUARD();
     bool trace_downtime = (type == QEMU_VM_SECTION_FULL);
     uint32_t instance_id, version_id, section_id;
     int64_t start_ts, end_ts;
@@ -2734,8 +2735,8 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type)
     /* Read section start */
     section_id = qemu_get_be32(f);
     if (!qemu_get_counted_string(f, idstr)) {
-        error_report("Unable to read ID string for section %u",
-                     section_id);
+        error_setg(errp, "Unable to read ID string for section %u",
+                   section_id);
         return -EINVAL;
     }
     instance_id = qemu_get_be32(f);
@@ -2743,8 +2744,7 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type)
 
     ret = qemu_file_get_error(f);
     if (ret) {
-        error_report("%s: Failed to read instance/version ID: %d",
-                     __func__, ret);
+        error_setg(errp, "Failed to read instance/version ID: %d", ret);
         return ret;
     }
 
@@ -2753,17 +2753,17 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type)
     /* Find savevm section */
     se = find_se(idstr, instance_id);
     if (se == NULL) {
-        error_report("Unknown savevm section or instance '%s' %"PRIu32". "
-                     "Make sure that your current VM setup matches your "
-                     "saved VM setup, including any hotplugged devices",
-                     idstr, instance_id);
+        error_setg(errp, "Unknown savevm section or instance '%s' %"PRIu32". "
+                   "Make sure that your current VM setup matches your "
+                   "saved VM setup, including any hotplugged devices",
+                   idstr, instance_id);
         return -EINVAL;
     }
 
     /* Validate version */
     if (version_id > se->version_id) {
-        error_report("savevm: unsupported version %d for '%s' v%d",
-                     version_id, idstr, se->version_id);
+        error_setg(errp, "savevm: unsupported version %d for '%s' v%d",
+                   version_id, idstr, se->version_id);
         return -EINVAL;
     }
     se->load_version_id = version_id;
@@ -2771,7 +2771,7 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type)
 
     /* Validate if it is a device's state */
     if (xen_enabled() && se->is_ram) {
-        error_report("loadvm: %s RAM loading not allowed on Xen", idstr);
+        error_setg(errp, "loadvm: %s RAM loading not allowed on Xen", idstr);
         return -EINVAL;
     }
 
@@ -2779,10 +2779,11 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type)
         start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
     }
 
-    ret = vmstate_load(f, se, NULL);
+    ret = vmstate_load(f, se, errp);
     if (ret < 0) {
-        error_report("error while loading state for instance 0x%"PRIx32" of"
-                     " device '%s'", instance_id, idstr);
+        error_prepend(errp,
+                      "error while loading state for instance 0x%"PRIx32" of"
+                      " device '%s': ", instance_id, idstr);
         return ret;
     }
 
@@ -2793,6 +2794,9 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type)
     }
 
     if (!check_section_footer(f, se)) {
+        error_setg(errp, "Reading footer section of instance "
+                   "0x%"PRIx32" of device '%s' for version_id: %d failed",
+                   instance_id, idstr, version_id);
         return -EINVAL;
     }
 
@@ -3097,7 +3101,7 @@ retry:
         switch (section_type) {
         case QEMU_VM_SECTION_START:
         case QEMU_VM_SECTION_FULL:
-            ret = qemu_loadvm_section_start_full(f, section_type);
+            ret = qemu_loadvm_section_start_full(f, section_type, errp);
             if (ret < 0) {
                 goto out;
             }

-- 
2.50.1



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

* [PATCH v9 11/27] migration: push Error **errp into qemu_loadvm_section_part_end()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (9 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 10/27] migration: push Error **errp into qemu_loadvm_section_start_full() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-06  7:46   ` Marc-André Lureau
  2025-08-05 18:25 ` [PATCH v9 12/27] migration: Update qemu_file_get_return_path() docs and remove dead checks Arun Menon
                   ` (15 subsequent siblings)
  26 siblings, 1 reply; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.
It is ensured that qemu_loadvm_section_part_end() must report an error
in errp, in case of failure.

Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/savevm.c | 21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/migration/savevm.c b/migration/savevm.c
index e337e3c61e7627f09b853bf5e9b845c38cb5f082..1b93df7894dd332aea25da91e24d02fa16e73363 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2804,8 +2804,9 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type, Error **errp)
 }
 
 static int
-qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type)
+qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type, Error **errp)
 {
+    ERRP_GUARD();
     bool trace_downtime = (type == QEMU_VM_SECTION_END);
     int64_t start_ts, end_ts;
     uint32_t section_id;
@@ -2816,8 +2817,7 @@ qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type)
 
     ret = qemu_file_get_error(f);
     if (ret) {
-        error_report("%s: Failed to read section ID: %d",
-                     __func__, ret);
+        error_setg(errp, "Failed to read section ID: %d", ret);
         return ret;
     }
 
@@ -2828,7 +2828,7 @@ qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type)
         }
     }
     if (se == NULL) {
-        error_report("Unknown savevm section %d", section_id);
+        error_setg(errp, "Unknown savevm section %d", section_id);
         return -EINVAL;
     }
 
@@ -2836,10 +2836,10 @@ qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type)
         start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
     }
 
-    ret = vmstate_load(f, se, NULL);
+    ret = vmstate_load(f, se, errp);
     if (ret < 0) {
-        error_report("error while loading state section id %d(%s)",
-                     section_id, se->idstr);
+        error_prepend(errp, "error while loading state section id %d(%s): ",
+                      section_id, se->idstr);
         return ret;
     }
 
@@ -2850,6 +2850,8 @@ qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type)
     }
 
     if (!check_section_footer(f, se)) {
+        error_setg(errp, "Check section footer error, section_id: %d",
+                   section_id);
         return -EINVAL;
     }
 
@@ -3108,7 +3110,7 @@ retry:
             break;
         case QEMU_VM_SECTION_PART:
         case QEMU_VM_SECTION_END:
-            ret = qemu_loadvm_section_part_end(f, section_type);
+            ret = qemu_loadvm_section_part_end(f, section_type, errp);
             if (ret < 0) {
                 goto out;
             }
@@ -3132,9 +3134,6 @@ retry:
 
 out:
     if (ret < 0) {
-        if (*errp == NULL) {
-            error_setg(errp, "Loading VM state failed: %d", ret);
-        }
         qemu_file_set_error(f, ret);
 
         /* Cancel bitmaps incoming regardless of recovery */

-- 
2.50.1



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

* [PATCH v9 12/27] migration: Update qemu_file_get_return_path() docs and remove dead checks
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (10 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 11/27] migration: push Error **errp into qemu_loadvm_section_part_end() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-08 13:07   ` Fabiano Rosas
  2025-08-05 18:25 ` [PATCH v9 13/27] migration: make loadvm_postcopy_handle_resume() void Arun Menon
                   ` (14 subsequent siblings)
  26 siblings, 1 reply; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon,
	Daniel P. Berrangé

The documentation of qemu_file_get_return_path() states that it can
return NULL on failure. However, a review of the current implementation
reveals that it is guaranteed that it will always succeed and will never
return NULL.

As a result, the NULL checks post calling the function become redundant.
This commit updates the documentation for the function and removes all
NULL checks throughout the migration code.

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/colo.c      |  4 ----
 migration/migration.c | 12 ++----------
 migration/qemu-file.c |  1 -
 migration/savevm.c    |  4 ----
 4 files changed, 2 insertions(+), 19 deletions(-)

diff --git a/migration/colo.c b/migration/colo.c
index a96e4dba15516b71d1b315c736c3b4879ff04e58..71842c798a1dbbea6a7ccf8cb64e2c836c0f574c 100644
--- a/migration/colo.c
+++ b/migration/colo.c
@@ -850,10 +850,6 @@ static void *colo_process_incoming_thread(void *opaque)
     failover_init_state();
 
     mis->to_src_file = qemu_file_get_return_path(mis->from_src_file);
-    if (!mis->to_src_file) {
-        error_report("COLO incoming thread: Open QEMUFile to_src_file failed");
-        goto out;
-    }
     /*
      * Note: the communication between Primary side and Secondary side
      * should be sequential, we set the fd to unblocked in migration incoming
diff --git a/migration/migration.c b/migration/migration.c
index bb7d5e1dee52692cbea1d2c8fdca541e6a75bedb..6962dc7d7f3e0121d28994c98f12f9f2258343d7 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -2647,12 +2647,9 @@ out:
     return NULL;
 }
 
-static int open_return_path_on_source(MigrationState *ms)
+static void open_return_path_on_source(MigrationState *ms)
 {
     ms->rp_state.from_dst_file = qemu_file_get_return_path(ms->to_dst_file);
-    if (!ms->rp_state.from_dst_file) {
-        return -1;
-    }
 
     trace_open_return_path_on_source();
 
@@ -2661,8 +2658,6 @@ static int open_return_path_on_source(MigrationState *ms)
     ms->rp_state.rp_thread_created = true;
 
     trace_open_return_path_on_source_continue();
-
-    return 0;
 }
 
 /* Return true if error detected, or false otherwise */
@@ -4011,10 +4006,7 @@ void migration_connect(MigrationState *s, Error *error_in)
      * QEMU uses the return path.
      */
     if (migrate_postcopy_ram() || migrate_return_path()) {
-        if (open_return_path_on_source(s)) {
-            error_setg(&local_err, "Unable to open return-path for postcopy");
-            goto fail;
-        }
+        open_return_path_on_source(s);
     }
 
     /*
diff --git a/migration/qemu-file.c b/migration/qemu-file.c
index b6ac190034f777dbde0da1598483a892089d7538..f9ccee9a1091ecbd37e6b7d2081a4446442b544d 100644
--- a/migration/qemu-file.c
+++ b/migration/qemu-file.c
@@ -125,7 +125,6 @@ static QEMUFile *qemu_file_new_impl(QIOChannel *ioc, bool is_writable)
 
 /*
  * Result: QEMUFile* for a 'return path' for comms in the opposite direction
- *         NULL if not available
  */
 QEMUFile *qemu_file_get_return_path(QEMUFile *f)
 {
diff --git a/migration/savevm.c b/migration/savevm.c
index 1b93df7894dd332aea25da91e24d02fa16e73363..b6b3f3fae742ae9d72d3af6e68440e14ffd30b0c 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2588,10 +2588,6 @@ static int loadvm_process_command(QEMUFile *f, Error **errp)
             return 0;
         }
         mis->to_src_file = qemu_file_get_return_path(f);
-        if (!mis->to_src_file) {
-            error_report("CMD_OPEN_RETURN_PATH failed");
-            return -1;
-        }
 
         /*
          * Switchover ack is enabled but no device uses it, so send an ACK to

-- 
2.50.1



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

* [PATCH v9 13/27] migration: make loadvm_postcopy_handle_resume() void
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (11 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 12/27] migration: Update qemu_file_get_return_path() docs and remove dead checks Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-08 13:08   ` Fabiano Rosas
  2025-08-05 18:25 ` [PATCH v9 14/27] migration: push Error **errp into ram_postcopy_incoming_init() Arun Menon
                   ` (13 subsequent siblings)
  26 siblings, 1 reply; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon,
	Daniel P. Berrangé

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.

Use warn_report() instead of error_report(); it ensures that
a resume command received while the migration is not
in postcopy recover state is not fatal. It only informs that
the command received is unusual, and therefore we should not set
errp with the error string.

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/savevm.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/migration/savevm.c b/migration/savevm.c
index b6b3f3fae742ae9d72d3af6e68440e14ffd30b0c..3517609ac661af618cf4d97e7f8fa1ef2b64bd4d 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2334,12 +2334,12 @@ static void migrate_send_rp_req_pages_pending(MigrationIncomingState *mis)
     }
 }
 
-static int loadvm_postcopy_handle_resume(MigrationIncomingState *mis)
+static void loadvm_postcopy_handle_resume(MigrationIncomingState *mis)
 {
     if (mis->state != MIGRATION_STATUS_POSTCOPY_RECOVER) {
-        error_report("%s: illegal resume received", __func__);
+        warn_report("%s: illegal resume received", __func__);
         /* Don't fail the load, only for this. */
-        return 0;
+        return;
     }
 
     /*
@@ -2391,8 +2391,6 @@ static int loadvm_postcopy_handle_resume(MigrationIncomingState *mis)
         /* Kick the fast ram load thread too */
         qemu_sem_post(&mis->postcopy_pause_sem_fast_load);
     }
-
-    return 0;
 }
 
 /**
@@ -2647,7 +2645,8 @@ static int loadvm_process_command(QEMUFile *f, Error **errp)
         return ret;
 
     case MIG_CMD_POSTCOPY_RESUME:
-        return loadvm_postcopy_handle_resume(mis);
+        loadvm_postcopy_handle_resume(mis);
+        return 0;
 
     case MIG_CMD_RECV_BITMAP:
         ret = loadvm_handle_recv_bitmap(mis, len);

-- 
2.50.1



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

* [PATCH v9 14/27] migration: push Error **errp into ram_postcopy_incoming_init()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (12 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 13/27] migration: make loadvm_postcopy_handle_resume() void Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-05 18:25 ` [PATCH v9 15/27] migration: push Error **errp into loadvm_postcopy_handle_advise() Arun Menon
                   ` (12 subsequent siblings)
  26 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.
It is ensured that ram_postcopy_incoming_init() must report an error
in errp, in case of failure.

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/postcopy-ram.c | 9 ++++++---
 migration/postcopy-ram.h | 2 +-
 migration/ram.c          | 4 ++--
 migration/ram.h          | 2 +-
 migration/savevm.c       | 2 +-
 5 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
index 45af9a361e8eacaad0fb217a5da2c5004416c1da..05617e5fbcad62226a54fe17d9f7d9a316baf1e4 100644
--- a/migration/postcopy-ram.c
+++ b/migration/postcopy-ram.c
@@ -681,6 +681,7 @@ out:
  */
 static int init_range(RAMBlock *rb, void *opaque)
 {
+    Error **errp = opaque;
     const char *block_name = qemu_ram_get_idstr(rb);
     void *host_addr = qemu_ram_get_host_addr(rb);
     ram_addr_t offset = qemu_ram_get_offset(rb);
@@ -701,6 +702,8 @@ static int init_range(RAMBlock *rb, void *opaque)
      * (Precopy will just overwrite this data, so doesn't need the discard)
      */
     if (ram_discard_range(block_name, 0, length)) {
+        error_setg(errp, "failed to discard RAM block %s len=%zu",
+                   block_name, length);
         return -1;
     }
 
@@ -749,9 +752,9 @@ static int cleanup_range(RAMBlock *rb, void *opaque)
  * postcopy later; must be called prior to any precopy.
  * called from arch_init's similarly named ram_postcopy_incoming_init
  */
-int postcopy_ram_incoming_init(MigrationIncomingState *mis)
+int postcopy_ram_incoming_init(MigrationIncomingState *mis, Error **errp)
 {
-    if (foreach_not_ignored_block(init_range, NULL)) {
+    if (foreach_not_ignored_block(init_range, errp)) {
         return -1;
     }
 
@@ -1703,7 +1706,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis, Error **errp)
     return false;
 }
 
-int postcopy_ram_incoming_init(MigrationIncomingState *mis)
+int postcopy_ram_incoming_init(MigrationIncomingState *mis, Error **errp)
 {
     error_report("postcopy_ram_incoming_init: No OS support");
     return -1;
diff --git a/migration/postcopy-ram.h b/migration/postcopy-ram.h
index 3852141d7e37ab18bada4b46c137fef0969d0070..ca19433b246893fa5105bcebffb442c58a9a4f48 100644
--- a/migration/postcopy-ram.h
+++ b/migration/postcopy-ram.h
@@ -30,7 +30,7 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis);
  * postcopy later; must be called prior to any precopy.
  * called from ram.c's similarly named ram_postcopy_incoming_init
  */
-int postcopy_ram_incoming_init(MigrationIncomingState *mis);
+int postcopy_ram_incoming_init(MigrationIncomingState *mis, Error **errp);
 
 /*
  * At the end of a migration where postcopy_ram_incoming_init was called.
diff --git a/migration/ram.c b/migration/ram.c
index 7208bc114fb5c366740db380ee6956a91b3871a0..6a0dcc04f436524a37672c41c38f201f06773374 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -3716,9 +3716,9 @@ static int ram_load_cleanup(void *opaque)
  * postcopy-ram. postcopy-ram's similarly names
  * postcopy_ram_incoming_init does the work.
  */
-int ram_postcopy_incoming_init(MigrationIncomingState *mis)
+int ram_postcopy_incoming_init(MigrationIncomingState *mis, Error **errp)
 {
-    return postcopy_ram_incoming_init(mis);
+    return postcopy_ram_incoming_init(mis, errp);
 }
 
 /**
diff --git a/migration/ram.h b/migration/ram.h
index 921c39a2c5c45bc2344be80854c46e4c10c09aeb..275709a99187f9429ccb4111e05281ec268ba0db 100644
--- a/migration/ram.h
+++ b/migration/ram.h
@@ -86,7 +86,7 @@ void ram_postcopy_migrated_memory_release(MigrationState *ms);
 void ram_postcopy_send_discard_bitmap(MigrationState *ms);
 /* For incoming postcopy discard */
 int ram_discard_range(const char *block_name, uint64_t start, size_t length);
-int ram_postcopy_incoming_init(MigrationIncomingState *mis);
+int ram_postcopy_incoming_init(MigrationIncomingState *mis, Error **errp);
 int ram_load_postcopy(QEMUFile *f, int channel);
 
 void ram_handle_zero(void *host, uint64_t size);
diff --git a/migration/savevm.c b/migration/savevm.c
index 3517609ac661af618cf4d97e7f8fa1ef2b64bd4d..bed03ad69008ef742f595f368896866d13713d16 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -1989,7 +1989,7 @@ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis,
         return -1;
     }
 
-    if (ram_postcopy_incoming_init(mis)) {
+    if (ram_postcopy_incoming_init(mis, NULL) < 0) {
         return -1;
     }
 

-- 
2.50.1



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

* [PATCH v9 15/27] migration: push Error **errp into loadvm_postcopy_handle_advise()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (13 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 14/27] migration: push Error **errp into ram_postcopy_incoming_init() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-05 18:25 ` [PATCH v9 16/27] migration: push Error **errp into loadvm_postcopy_handle_listen() Arun Menon
                   ` (11 subsequent siblings)
  26 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon,
	Daniel P. Berrangé

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.
It is ensured that loadvm_postcopy_handle_advise() must report an error
in errp, in case of failure.

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/savevm.c | 40 +++++++++++++++++++---------------------
 1 file changed, 19 insertions(+), 21 deletions(-)

diff --git a/migration/savevm.c b/migration/savevm.c
index bed03ad69008ef742f595f368896866d13713d16..09b9da6c58dda6e062a9a22bafd3487d38295f22 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -1912,39 +1912,39 @@ enum LoadVMExitCodes {
  * quickly.
  */
 static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis,
-                                         uint16_t len)
+                                         uint16_t len, Error **errp)
 {
     PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_ADVISE);
     uint64_t remote_pagesize_summary, local_pagesize_summary, remote_tps;
     size_t page_size = qemu_target_page_size();
-    Error *local_err = NULL;
 
     trace_loadvm_postcopy_handle_advise();
     if (ps != POSTCOPY_INCOMING_NONE) {
-        error_report("CMD_POSTCOPY_ADVISE in wrong postcopy state (%d)", ps);
+        error_setg(errp, "CMD_POSTCOPY_ADVISE in wrong postcopy state (%d)",
+                   ps);
         return -1;
     }
 
     switch (len) {
     case 0:
         if (migrate_postcopy_ram()) {
-            error_report("RAM postcopy is enabled but have 0 byte advise");
+            error_setg(errp, "RAM postcopy is enabled but have 0 byte advise");
             return -EINVAL;
         }
         return 0;
     case 8 + 8:
         if (!migrate_postcopy_ram()) {
-            error_report("RAM postcopy is disabled but have 16 byte advise");
+            error_setg(errp,
+                       "RAM postcopy is disabled but have 16 byte advise");
             return -EINVAL;
         }
         break;
     default:
-        error_report("CMD_POSTCOPY_ADVISE invalid length (%d)", len);
+        error_setg(errp, "CMD_POSTCOPY_ADVISE invalid length (%d)", len);
         return -EINVAL;
     }
 
-    if (!postcopy_ram_supported_by_host(mis, &local_err)) {
-        error_report_err(local_err);
+    if (!postcopy_ram_supported_by_host(mis, errp)) {
         postcopy_state_set(POSTCOPY_INCOMING_NONE);
         return -1;
     }
@@ -1967,9 +1967,10 @@ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis,
          *      also fails when passed to an older qemu that doesn't
          *      do huge pages.
          */
-        error_report("Postcopy needs matching RAM page sizes (s=%" PRIx64
-                                                             " d=%" PRIx64 ")",
-                     remote_pagesize_summary, local_pagesize_summary);
+        error_setg(errp,
+                   "Postcopy needs matching RAM page sizes "
+                   "(s=%" PRIx64 " d=%" PRIx64 ")",
+                   remote_pagesize_summary, local_pagesize_summary);
         return -1;
     }
 
@@ -1979,17 +1980,18 @@ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis,
          * Again, some differences could be dealt with, but for now keep it
          * simple.
          */
-        error_report("Postcopy needs matching target page sizes (s=%d d=%zd)",
-                     (int)remote_tps, page_size);
+        error_setg(errp,
+                   "Postcopy needs matching target page sizes (s=%d d=%zd)",
+                   (int)remote_tps, page_size);
         return -1;
     }
 
-    if (postcopy_notify(POSTCOPY_NOTIFY_INBOUND_ADVISE, &local_err)) {
-        error_report_err(local_err);
+    if (postcopy_notify(POSTCOPY_NOTIFY_INBOUND_ADVISE, errp)) {
         return -1;
     }
 
-    if (ram_postcopy_incoming_init(mis, NULL) < 0) {
+    if (ram_postcopy_incoming_init(mis, errp) < 0) {
+        error_prepend(errp, "Postcopy RAM incoming init failed: ");
         return -1;
     }
 
@@ -2617,11 +2619,7 @@ static int loadvm_process_command(QEMUFile *f, Error **errp)
         return loadvm_handle_cmd_packaged(mis, errp);
 
     case MIG_CMD_POSTCOPY_ADVISE:
-        ret = loadvm_postcopy_handle_advise(mis, len);
-        if (ret < 0) {
-            error_setg(errp, "Failed to load device state command: %d", ret);
-        }
-        return ret;
+        return loadvm_postcopy_handle_advise(mis, len, errp);
 
     case MIG_CMD_POSTCOPY_LISTEN:
         ret = loadvm_postcopy_handle_listen(mis);

-- 
2.50.1



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

* [PATCH v9 16/27] migration: push Error **errp into loadvm_postcopy_handle_listen()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (14 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 15/27] migration: push Error **errp into loadvm_postcopy_handle_advise() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-05 18:25 ` [PATCH v9 17/27] migration: push Error **errp into loadvm_postcopy_handle_run() Arun Menon
                   ` (10 subsequent siblings)
  26 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon,
	Daniel P. Berrangé

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.
It is ensured that loadvm_postcopy_handle_listen() must report an error
in errp, in case of failure.

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/savevm.c | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/migration/savevm.c b/migration/savevm.c
index 09b9da6c58dda6e062a9a22bafd3487d38295f22..be77da66d813392625c6d701b24d1bcffe26cdce 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2181,15 +2181,16 @@ static void *postcopy_ram_listen_thread(void *opaque)
 }
 
 /* After this message we must be able to immediately receive postcopy data */
-static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
+static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis,
+                                         Error **errp)
 {
     PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_LISTENING);
-    Error *local_err = NULL;
 
     trace_loadvm_postcopy_handle_listen("enter");
 
     if (ps != POSTCOPY_INCOMING_ADVISE && ps != POSTCOPY_INCOMING_DISCARD) {
-        error_report("CMD_POSTCOPY_LISTEN in wrong postcopy state (%d)", ps);
+        error_setg(errp,
+                   "CMD_POSTCOPY_LISTEN in wrong postcopy state (%d)", ps);
         return -1;
     }
     if (ps == POSTCOPY_INCOMING_ADVISE) {
@@ -2212,14 +2213,14 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
     if (migrate_postcopy_ram()) {
         if (postcopy_ram_incoming_setup(mis)) {
             postcopy_ram_incoming_cleanup(mis);
+            error_setg(errp, "Failed to setup incoming postcopy RAM blocks");
             return -1;
         }
     }
 
     trace_loadvm_postcopy_handle_listen("after uffd");
 
-    if (postcopy_notify(POSTCOPY_NOTIFY_INBOUND_LISTEN, &local_err)) {
-        error_report_err(local_err);
+    if (postcopy_notify(POSTCOPY_NOTIFY_INBOUND_LISTEN, errp)) {
         return -1;
     }
 
@@ -2622,11 +2623,7 @@ static int loadvm_process_command(QEMUFile *f, Error **errp)
         return loadvm_postcopy_handle_advise(mis, len, errp);
 
     case MIG_CMD_POSTCOPY_LISTEN:
-        ret = loadvm_postcopy_handle_listen(mis);
-        if (ret < 0) {
-            error_setg(errp, "Failed to load device state command: %d", ret);
-        }
-        return ret;
+        return loadvm_postcopy_handle_listen(mis, errp);
 
     case MIG_CMD_POSTCOPY_RUN:
         ret = loadvm_postcopy_handle_run(mis);

-- 
2.50.1



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

* [PATCH v9 17/27] migration: push Error **errp into loadvm_postcopy_handle_run()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (15 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 16/27] migration: push Error **errp into loadvm_postcopy_handle_listen() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-05 18:25 ` [PATCH v9 18/27] migration: push Error **errp into loadvm_postcopy_ram_handle_discard() Arun Menon
                   ` (9 subsequent siblings)
  26 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon,
	Daniel P. Berrangé

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.
It is ensured that loadvm_postcopy_handle_run() must report an error
in errp, in case of failure.

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/savevm.c | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/migration/savevm.c b/migration/savevm.c
index be77da66d813392625c6d701b24d1bcffe26cdce..eb90873a750ded354b3db31cba40b44d1be79864 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2273,13 +2273,13 @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
 }
 
 /* After all discards we can start running and asking for pages */
-static int loadvm_postcopy_handle_run(MigrationIncomingState *mis)
+static int loadvm_postcopy_handle_run(MigrationIncomingState *mis, Error **errp)
 {
     PostcopyState ps = postcopy_state_get();
 
     trace_loadvm_postcopy_handle_run();
     if (ps != POSTCOPY_INCOMING_LISTENING) {
-        error_report("CMD_POSTCOPY_RUN in wrong postcopy state (%d)", ps);
+        error_setg(errp, "CMD_POSTCOPY_RUN in wrong postcopy state (%d)", ps);
         return -1;
     }
 
@@ -2626,11 +2626,7 @@ static int loadvm_process_command(QEMUFile *f, Error **errp)
         return loadvm_postcopy_handle_listen(mis, errp);
 
     case MIG_CMD_POSTCOPY_RUN:
-        ret = loadvm_postcopy_handle_run(mis);
-        if (ret < 0) {
-            error_setg(errp, "Failed to load device state command: %d", ret);
-        }
-        return ret;
+        return loadvm_postcopy_handle_run(mis, errp);
 
     case MIG_CMD_POSTCOPY_RAM_DISCARD:
         ret = loadvm_postcopy_ram_handle_discard(mis, len);

-- 
2.50.1



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

* [PATCH v9 18/27] migration: push Error **errp into loadvm_postcopy_ram_handle_discard()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (16 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 17/27] migration: push Error **errp into loadvm_postcopy_handle_run() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-06  7:54   ` Marc-André Lureau
  2025-08-05 18:25 ` [PATCH v9 19/27] migration: push Error **errp into loadvm_handle_recv_bitmap() Arun Menon
                   ` (8 subsequent siblings)
  26 siblings, 1 reply; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.
It is ensured that loadvm_postcopy_ram_handle_discard() must report an error
in errp, in case of failure.

Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/savevm.c | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/migration/savevm.c b/migration/savevm.c
index eb90873a750ded354b3db31cba40b44d1be79864..3abe4193e02aae9c813ff07fb388a7ee470c8a6a 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2004,7 +2004,7 @@ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis,
  * There can be 0..many of these messages, each encoding multiple pages.
  */
 static int loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis,
-                                              uint16_t len)
+                                              uint16_t len, Error **errp)
 {
     int tmp;
     char ramid[256];
@@ -2017,6 +2017,7 @@ static int loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis,
         /* 1st discard */
         tmp = postcopy_ram_prepare_discard(mis);
         if (tmp) {
+            error_setg(errp, "Failed to prepare for RAM discard: %d", tmp);
             return tmp;
         }
         break;
@@ -2026,8 +2027,9 @@ static int loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis,
         break;
 
     default:
-        error_report("CMD_POSTCOPY_RAM_DISCARD in wrong postcopy state (%d)",
-                     ps);
+        error_setg(errp,
+                   "CMD_POSTCOPY_RAM_DISCARD in wrong postcopy state (%d)",
+                   ps);
         return -1;
     }
     /* We're expecting a
@@ -2036,29 +2038,30 @@ static int loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis,
      *    then at least 1 16 byte chunk
     */
     if (len < (1 + 1 + 1 + 1 + 2 * 8)) {
-        error_report("CMD_POSTCOPY_RAM_DISCARD invalid length (%d)", len);
+        error_setg(errp, "CMD_POSTCOPY_RAM_DISCARD invalid length (%d)", len);
         return -1;
     }
 
     tmp = qemu_get_byte(mis->from_src_file);
     if (tmp != postcopy_ram_discard_version) {
-        error_report("CMD_POSTCOPY_RAM_DISCARD invalid version (%d)", tmp);
+        error_setg(errp, "CMD_POSTCOPY_RAM_DISCARD invalid version (%d)", tmp);
         return -1;
     }
 
     if (!qemu_get_counted_string(mis->from_src_file, ramid)) {
-        error_report("CMD_POSTCOPY_RAM_DISCARD Failed to read RAMBlock ID");
+        error_setg(errp,
+                   "CMD_POSTCOPY_RAM_DISCARD Failed to read RAMBlock ID");
         return -1;
     }
     tmp = qemu_get_byte(mis->from_src_file);
     if (tmp != 0) {
-        error_report("CMD_POSTCOPY_RAM_DISCARD missing nil (%d)", tmp);
+        error_setg(errp, "CMD_POSTCOPY_RAM_DISCARD missing nil (%d)", tmp);
         return -1;
     }
 
     len -= 3 + strlen(ramid);
     if (len % 16) {
-        error_report("CMD_POSTCOPY_RAM_DISCARD invalid length (%d)", len);
+        error_setg(errp, "CMD_POSTCOPY_RAM_DISCARD invalid length (%d)", len);
         return -1;
     }
     trace_loadvm_postcopy_ram_handle_discard_header(ramid, len);
@@ -2070,6 +2073,7 @@ static int loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis,
         len -= 16;
         int ret = ram_discard_range(ramid, start_addr, block_length);
         if (ret) {
+            error_setg(errp, "Failed to discard RAM range %s: %d", ramid, ret);
             return ret;
         }
     }
@@ -2629,11 +2633,7 @@ static int loadvm_process_command(QEMUFile *f, Error **errp)
         return loadvm_postcopy_handle_run(mis, errp);
 
     case MIG_CMD_POSTCOPY_RAM_DISCARD:
-        ret = loadvm_postcopy_ram_handle_discard(mis, len);
-        if (ret < 0) {
-            error_setg(errp, "Failed to load device state command: %d", ret);
-        }
-        return ret;
+        return loadvm_postcopy_ram_handle_discard(mis, len, errp);
 
     case MIG_CMD_POSTCOPY_RESUME:
         loadvm_postcopy_handle_resume(mis);

-- 
2.50.1



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

* [PATCH v9 19/27] migration: push Error **errp into loadvm_handle_recv_bitmap()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (17 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 18/27] migration: push Error **errp into loadvm_postcopy_ram_handle_discard() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-05 18:25 ` [PATCH v9 20/27] migration: push Error **errp into loadvm_process_enable_colo() Arun Menon
                   ` (7 subsequent siblings)
  26 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon,
	Daniel P. Berrangé

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.
It is ensured that loadvm_handle_recv_bitmap() must report an error
in errp, in case of failure.

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/savevm.c | 21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/migration/savevm.c b/migration/savevm.c
index 3abe4193e02aae9c813ff07fb388a7ee470c8a6a..938adb20270adbf9546b0884d0877c25c3f0f816 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2480,32 +2480,35 @@ static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis, Error **errp)
  * len (1 byte) + ramblock_name (<255 bytes)
  */
 static int loadvm_handle_recv_bitmap(MigrationIncomingState *mis,
-                                     uint16_t len)
+                                     uint16_t len, Error **errp)
 {
     QEMUFile *file = mis->from_src_file;
     RAMBlock *rb;
     char block_name[256];
     size_t cnt;
+    int ret;
 
     cnt = qemu_get_counted_string(file, block_name);
     if (!cnt) {
-        error_report("%s: failed to read block name", __func__);
+        error_setg(errp, "failed to read block name: %s", block_name);
         return -EINVAL;
     }
 
     /* Validate before using the data */
-    if (qemu_file_get_error(file)) {
-        return qemu_file_get_error(file);
+    ret = qemu_file_get_error(file);
+    if (ret < 0) {
+        error_setg(errp, "migration stream has error: %d", ret);
+        return ret;
     }
 
     if (len != cnt + 1) {
-        error_report("%s: invalid payload length (%d)", __func__, len);
+        error_setg(errp, "invalid payload length (%d)", len);
         return -EINVAL;
     }
 
     rb = qemu_ram_block_by_name(block_name);
     if (!rb) {
-        error_report("%s: block '%s' not found", __func__, block_name);
+        error_setg(errp, "block '%s' not found", block_name);
         return -EINVAL;
     }
 
@@ -2640,11 +2643,7 @@ static int loadvm_process_command(QEMUFile *f, Error **errp)
         return 0;
 
     case MIG_CMD_RECV_BITMAP:
-        ret = loadvm_handle_recv_bitmap(mis, len);
-        if (ret < 0) {
-            error_setg(errp, "Failed to load device state command: %d", ret);
-        }
-        return ret;
+        return loadvm_handle_recv_bitmap(mis, len, errp);
 
     case MIG_CMD_ENABLE_COLO:
         ret = loadvm_process_enable_colo(mis);

-- 
2.50.1



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

* [PATCH v9 20/27] migration: push Error **errp into loadvm_process_enable_colo()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (18 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 19/27] migration: push Error **errp into loadvm_handle_recv_bitmap() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-06  8:07   ` Marc-André Lureau
  2025-08-05 18:25 ` [PATCH v9 21/27] migration: push Error **errp into loadvm_postcopy_handle_switchover_start() Arun Menon
                   ` (6 subsequent siblings)
  26 siblings, 1 reply; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.
It is ensured that loadvm_process_enable_colo() must report an error
in errp, in case of failure.

Signed-off-by: Arun Menon <armenon@redhat.com>
---
 include/migration/colo.h |  2 +-
 migration/migration.c    | 12 ++++++------
 migration/ram.c          |  8 ++++----
 migration/ram.h          |  2 +-
 migration/savevm.c       | 26 ++++++++++++++------------
 5 files changed, 26 insertions(+), 24 deletions(-)

diff --git a/include/migration/colo.h b/include/migration/colo.h
index 43222ef5ae6adc3f7d8aa6a48bef79af33d09208..d4fe422e4d335d3bef4f860f56400fcd73287a0e 100644
--- a/include/migration/colo.h
+++ b/include/migration/colo.h
@@ -25,7 +25,7 @@ void migrate_start_colo_process(MigrationState *s);
 bool migration_in_colo_state(void);
 
 /* loadvm */
-int migration_incoming_enable_colo(void);
+int migration_incoming_enable_colo(Error **errp);
 void migration_incoming_disable_colo(void);
 bool migration_incoming_colo_enabled(void);
 bool migration_incoming_in_colo_state(void);
diff --git a/migration/migration.c b/migration/migration.c
index 6962dc7d7f3e0121d28994c98f12f9f2258343d7..4a76d7ed730589bae87115368b0bf4819f8b161e 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -623,22 +623,22 @@ void migration_incoming_disable_colo(void)
     migration_colo_enabled = false;
 }
 
-int migration_incoming_enable_colo(void)
+int migration_incoming_enable_colo(Error **errp)
 {
 #ifndef CONFIG_REPLICATION
-    error_report("ENABLE_COLO command come in migration stream, but the "
-                 "replication module is not built in");
+    error_setg(errp, "ENABLE_COLO command come in migration stream, but the "
+               "replication module is not built in");
     return -ENOTSUP;
 #endif
 
     if (!migrate_colo()) {
-        error_report("ENABLE_COLO command come in migration stream, but x-colo "
-                     "capability is not set");
+        error_setg(errp, "ENABLE_COLO command come in migration stream"
+                   ", but x-colo capability is not set");
         return -EINVAL;
     }
 
     if (ram_block_discard_disable(true)) {
-        error_report("COLO: cannot disable RAM discard");
+        error_setg(errp, "COLO: cannot disable RAM discard");
         return -EBUSY;
     }
     migration_colo_enabled = true;
diff --git a/migration/ram.c b/migration/ram.c
index 6a0dcc04f436524a37672c41c38f201f06773374..995431c9e320f443c385c29d664d62e18c1afd90 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -3576,7 +3576,7 @@ static void colo_init_ram_state(void)
  * memory of the secondary VM, it is need to hold the global lock
  * to call this helper.
  */
-int colo_init_ram_cache(void)
+int colo_init_ram_cache(Error **errp)
 {
     RAMBlock *block;
 
@@ -3585,9 +3585,9 @@ int colo_init_ram_cache(void)
             block->colo_cache = qemu_anon_ram_alloc(block->used_length,
                                                     NULL, false, false);
             if (!block->colo_cache) {
-                error_report("%s: Can't alloc memory for COLO cache of block %s,"
-                             "size 0x" RAM_ADDR_FMT, __func__, block->idstr,
-                             block->used_length);
+                error_setg(errp, "Can't alloc memory for COLO cache of "
+                           "block %s, size 0x" RAM_ADDR_FMT,
+                           block->idstr, block->used_length);
                 RAMBLOCK_FOREACH_NOT_IGNORED(block) {
                     if (block->colo_cache) {
                         qemu_anon_ram_free(block->colo_cache, block->used_length);
diff --git a/migration/ram.h b/migration/ram.h
index 275709a99187f9429ccb4111e05281ec268ba0db..24cd0bf585762cfa1e86834dc03c6baeea2f0627 100644
--- a/migration/ram.h
+++ b/migration/ram.h
@@ -109,7 +109,7 @@ void ramblock_set_file_bmap_atomic(RAMBlock *block, ram_addr_t offset,
                                    bool set);
 
 /* ram cache */
-int colo_init_ram_cache(void);
+int colo_init_ram_cache(Error **errp);
 void colo_flush_ram_cache(void);
 void colo_release_ram_cache(void);
 void colo_incoming_start_dirty_log(void);
diff --git a/migration/savevm.c b/migration/savevm.c
index 938adb20270adbf9546b0884d0877c25c3f0f816..a6b71a958aeda31e89043f8103bfe2fc89542fb5 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2519,15 +2519,21 @@ static int loadvm_handle_recv_bitmap(MigrationIncomingState *mis,
     return 0;
 }
 
-static int loadvm_process_enable_colo(MigrationIncomingState *mis)
+static int loadvm_process_enable_colo(MigrationIncomingState *mis,
+                                      Error **errp)
 {
-    int ret = migration_incoming_enable_colo();
+    ERRP_GUARD();
+    int ret;
 
-    if (!ret) {
-        ret = colo_init_ram_cache();
-        if (ret) {
-            migration_incoming_disable_colo();
-        }
+    ret = migration_incoming_enable_colo(errp);
+    if (ret < 0) {
+        return ret;
+    }
+
+    ret = colo_init_ram_cache(errp);
+    if (ret) {
+        error_prepend(errp, "failed to init colo RAM cache: %d: ", ret);
+        migration_incoming_disable_colo();
     }
     return ret;
 }
@@ -2646,11 +2652,7 @@ static int loadvm_process_command(QEMUFile *f, Error **errp)
         return loadvm_handle_recv_bitmap(mis, len, errp);
 
     case MIG_CMD_ENABLE_COLO:
-        ret = loadvm_process_enable_colo(mis);
-        if (ret < 0) {
-            error_setg(errp, "Failed to load device state command: %d", ret);
-        }
-        return ret;
+        return loadvm_process_enable_colo(mis, errp);
 
     case MIG_CMD_SWITCHOVER_START:
         ret = loadvm_postcopy_handle_switchover_start();

-- 
2.50.1



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

* [PATCH v9 21/27] migration: push Error **errp into loadvm_postcopy_handle_switchover_start()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (19 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 20/27] migration: push Error **errp into loadvm_process_enable_colo() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-05 18:25 ` [PATCH v9 22/27] migration: Capture error in postcopy_ram_listen_thread() Arun Menon
                   ` (5 subsequent siblings)
  26 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon,
	Daniel P. Berrangé

This is an incremental step in converting vmstate loading code to report
error via Error objects instead of directly printing it to console/monitor.
It is ensured that loadvm_postcopy_handle_switchover_start() must report
an error in errp, in case of failure.

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/savevm.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/migration/savevm.c b/migration/savevm.c
index a6b71a958aeda31e89043f8103bfe2fc89542fb5..c89fa59161fd1c4f20d0c3f8f14e86f014223dd6 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2538,7 +2538,7 @@ static int loadvm_process_enable_colo(MigrationIncomingState *mis,
     return ret;
 }
 
-static int loadvm_postcopy_handle_switchover_start(void)
+static int loadvm_postcopy_handle_switchover_start(Error **errp)
 {
     SaveStateEntry *se;
 
@@ -2551,6 +2551,7 @@ static int loadvm_postcopy_handle_switchover_start(void)
 
         ret = se->ops->switchover_start(se->opaque);
         if (ret < 0) {
+            error_setg(errp, "Switchover start failed: %d", ret);
             return ret;
         }
     }
@@ -2655,11 +2656,7 @@ static int loadvm_process_command(QEMUFile *f, Error **errp)
         return loadvm_process_enable_colo(mis, errp);
 
     case MIG_CMD_SWITCHOVER_START:
-        ret = loadvm_postcopy_handle_switchover_start();
-        if (ret < 0) {
-            error_setg(errp, "Failed to load device state command: %d", ret);
-        }
-        return ret;
+        return loadvm_postcopy_handle_switchover_start(errp);
     }
 
     return 0;

-- 
2.50.1



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

* [PATCH v9 22/27] migration: Capture error in postcopy_ram_listen_thread()
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (20 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 21/27] migration: push Error **errp into loadvm_postcopy_handle_switchover_start() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-08 13:27   ` Fabiano Rosas
  2025-08-05 18:25 ` [PATCH v9 23/27] migration: Refactor vmstate_save_state_v() function Arun Menon
                   ` (4 subsequent siblings)
  26 siblings, 1 reply; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon

This is an incremental step in converting vmstate loading
code to report error via Error objects instead of directly
printing it to console/monitor.
postcopy_ram_listen_thread() calls qemu_loadvm_state_main()
to load the vm, and in case of a failure, it should set the error
in the migration object.

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/savevm.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/migration/savevm.c b/migration/savevm.c
index c89fa59161fd1c4f20d0c3f8f14e86f014223dd6..4df50fb5c674e56c0173c357c1e23367373e2114 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2095,6 +2095,7 @@ static void *postcopy_ram_listen_thread(void *opaque)
     QEMUFile *f = mis->from_src_file;
     int load_res;
     MigrationState *migr = migrate_get_current();
+    Error *local_err = NULL;
 
     object_ref(OBJECT(migr));
 
@@ -2111,7 +2112,7 @@ static void *postcopy_ram_listen_thread(void *opaque)
     qemu_file_set_blocking(f, true);
 
     /* TODO: sanity check that only postcopiable data will be loaded here */
-    load_res = qemu_loadvm_state_main(f, mis, NULL);
+    load_res = qemu_loadvm_state_main(f, mis, &local_err);
 
     /*
      * This is tricky, but, mis->from_src_file can change after it
@@ -2137,7 +2138,12 @@ static void *postcopy_ram_listen_thread(void *opaque)
                          __func__, load_res);
             load_res = 0; /* prevent further exit() */
         } else {
-            error_report("%s: loadvm failed: %d", __func__, load_res);
+            if (local_err != NULL) {
+                error_prepend(&local_err,
+                              "loadvm failed during postcopy: %d: ", load_res);
+                migrate_set_error(migr, local_err);
+                error_report_err(local_err);
+            }
             migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
                                            MIGRATION_STATUS_FAILED);
         }

-- 
2.50.1



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

* [PATCH v9 23/27] migration: Refactor vmstate_save_state_v() function
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (21 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 22/27] migration: Capture error in postcopy_ram_listen_thread() Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-06  5:10   ` Akihiko Odaki
  2025-08-06  8:19   ` Marc-André Lureau
  2025-08-05 18:25 ` [PATCH v9 24/27] migration: Propagate last encountered error in " Arun Menon
                   ` (3 subsequent siblings)
  26 siblings, 2 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon

The original vmstate_save_state_v() function combined multiple
responsibilities like calling pre-save hooks, saving the state of
the device, handling subsection saves and invoking post-save hooks.
This led to a lengthy and less readable function.

To address this, introduce wrapper functions for pre-save,
post-save and the device-state save functionalities.

Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/vmstate.c | 78 ++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 60 insertions(+), 18 deletions(-)

diff --git a/migration/vmstate.c b/migration/vmstate.c
index 60ff38858cf54277992fa5eddeadb6f3d70edec3..fbc59caadbbcc75fe6de27b459aa9aa25e76aa0a 100644
--- a/migration/vmstate.c
+++ b/migration/vmstate.c
@@ -414,22 +414,43 @@ int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription *vmsd,
     return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id, vmsd->version_id, errp);
 }
 
-int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
-                         void *opaque, JSONWriter *vmdesc, int version_id, Error **errp)
+static int pre_save_dispatch(const VMStateDescription *vmsd, void *opaque,
+                             Error **errp)
 {
     int ret = 0;
-    const VMStateField *field = vmsd->fields;
-
-    trace_vmstate_save_state_top(vmsd->name);
-
     if (vmsd->pre_save) {
         ret = vmsd->pre_save(opaque);
         trace_vmstate_save_state_pre_save_res(vmsd->name, ret);
         if (ret) {
-            error_setg(errp, "pre-save failed: %s", vmsd->name);
-            return ret;
+            error_setg(errp, "pre-save for %s failed, ret: %d",
+                       vmsd->name, ret);
         }
     }
+    return ret;
+}
+
+static int post_save_dispatch(const VMStateDescription *vmsd, void *opaque,
+                              Error **errp)
+{
+    int ret = 0;
+    if (vmsd->post_save) {
+        ret = vmsd->post_save(opaque);
+        error_setg(errp, "post-save for %s failed, ret: %d",
+                   vmsd->name, ret);
+    }
+    return ret;
+}
+
+static int vmstate_save_dispatch(QEMUFile *f,
+                                 const VMStateDescription *vmsd,
+                                 void *opaque, JSONWriter *vmdesc,
+                                 int version_id, Error **errp)
+{
+    ERRP_GUARD();
+    int ret = 0;
+    int ps_ret = 0;
+    Error *local_err = NULL;
+    const VMStateField *field = vmsd->fields;
 
     if (vmdesc) {
         json_writer_str(vmdesc, "vmsd_name", vmsd->name);
@@ -532,9 +553,7 @@ 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);
-                    if (vmsd->post_save) {
-                        vmsd->post_save(opaque);
-                    }
+                    ps_ret = post_save_dispatch(vmsd, opaque, &local_err);
                     return ret;
                 }
 
@@ -557,16 +576,39 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
     if (vmdesc) {
         json_writer_end_array(vmdesc);
     }
+    return ret;
+}
 
-    ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc, errp);
 
-    if (vmsd->post_save) {
-        int ps_ret = vmsd->post_save(opaque);
-        if (!ret && ps_ret) {
-            ret = ps_ret;
-            error_setg(errp, "post-save failed: %s", vmsd->name);
-        }
+int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
+                         void *opaque, JSONWriter *vmdesc, int version_id,
+                         Error **errp)
+{
+    ERRP_GUARD();
+    int ret = 0;
+    Error *local_err = NULL;
+    int ps_ret = 0;
+
+    trace_vmstate_save_state_top(vmsd->name);
+
+    ret = pre_save_dispatch(vmsd, opaque, errp);
+    if (ret) {
+        return ret;
+    }
+
+    ret = vmstate_save_dispatch(f, vmsd, opaque, vmdesc,
+                                version_id, errp);
+    if (ret) {
+        return ret;
     }
+
+    ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc, errp);
+    ps_ret = post_save_dispatch(vmsd, opaque, &local_err);
+    if (!ret && ps_ret) {
+        ret = ps_ret;
+        error_setg(errp, "post-save failed: %s", vmsd->name);
+    }
+
     return ret;
 }
 

-- 
2.50.1



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

* [PATCH v9 24/27] migration: Propagate last encountered error in vmstate_save_state_v() function
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (22 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 23/27] migration: Refactor vmstate_save_state_v() function Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-06  5:24   ` Akihiko Odaki
  2025-08-05 18:25 ` [PATCH v9 25/27] migration: Remove error variant of vmstate_save_state() function Arun Menon
                   ` (2 subsequent siblings)
  26 siblings, 1 reply; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon

We consider,
  - the saving of the device state (save or subsection save) and
  - running the cleanup after (post_save)
as an atomic operation. And that is why, post_save() is called regardless
of whether there is a preceeding error. This means that, it is possible
that we have 2 distict errors, one from the preceeding function and the
other from the post_save() function.

This commit changes the error handling behavior when two errors occur during
a save operation.

1) Preceding errors were propagated before, but they are now dismissed, if there
   is a new post_save() error.
    - A failure during the main save operation, means the system failed to
      reach a new desired state. A failure during the post-save cleanup,
      however, is a more critical issue as it can leave the system in an
      inconsistent or corrupted state. At present, all post_save() calls
      succeed. However, the introduction of error handling variants of these
      functions (post_save_errp) in the following commit, imply that we need
      to handle and propagate these errors. Therefore, we prioritize reporting
      the more severe error.

2) post_save() errors were dismissed before, but they are now propagated.
    - The original design implicitly assumed post-save hooks were infallible
      cleanup routines.
      This will not be the case after introducing the post/pre save/load errp
      variant functions. Dismissing these errors prevents users from
      identifying the specific operation that failed.
      The entire save-and-cleanup process is treated as a single
      logical operation, and all potential failures are communicated.

Signed-off-by: Arun Menon <armenon@redhat.com>
---
 migration/vmstate.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/migration/vmstate.c b/migration/vmstate.c
index fbc59caadbbcc75fe6de27b459aa9aa25e76aa0a..ef78a1e62ad92e9608de72d125da80ea496c8dd1 100644
--- a/migration/vmstate.c
+++ b/migration/vmstate.c
@@ -554,6 +554,12 @@ static int vmstate_save_dispatch(QEMUFile *f,
                     error_setg(errp, "Save of field %s/%s failed",
                                 vmsd->name, field->name);
                     ps_ret = post_save_dispatch(vmsd, opaque, &local_err);
+                    if (ps_ret) {
+                        ret = ps_ret;
+                        error_free_or_abort(errp);
+                        error_setg(errp, "post-save for %s failed, ret: %d",
+                                   vmsd->name, ret);
+                    }
                     return ret;
                 }
 
@@ -603,10 +609,14 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
     }
 
     ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc, errp);
+
     ps_ret = post_save_dispatch(vmsd, opaque, &local_err);
-    if (!ret && ps_ret) {
+    if (ps_ret) {
+        if (ret) {
+            error_free_or_abort(errp);
+        }
         ret = ps_ret;
-        error_setg(errp, "post-save failed: %s", vmsd->name);
+        error_propagate(errp, local_err);
     }
 
     return ret;

-- 
2.50.1



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

* [PATCH v9 25/27] migration: Remove error variant of vmstate_save_state() function
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (23 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 24/27] migration: Propagate last encountered error in " Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-06  8:28   ` Marc-André Lureau
  2025-08-05 18:25 ` [PATCH v9 26/27] migration: Add error-parameterized function variants in VMSD struct Arun Menon
  2025-08-05 18:25 ` [PATCH v9 27/27] backends/tpm: Propagate vTPM error on migration failure Arun Menon
  26 siblings, 1 reply; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon

This commit removes the redundant vmstate_save_state_with_err()
function.

Previously, commit 969298f9d7 introduced vmstate_save_state_with_err()
to handle error propagation, while vmstate_save_state() existed for
non-error scenarios.
This is because there were code paths where vmstate_save_state_v()
(called internally by vmstate_save_state) did not explicitly set
errors on failure.

This change unifies error handling by
 - updating vmstate_save_state() to accept an Error **errp argument.
 - vmstate_save_state_v() ensures errors are set directly within the errp
   object, eliminating the need for two separate functions. Specifically
   errors are handled by storing them in errp in vmstate_save_dispatch()

All calls to vmstate_save_state_with_err() are replaced with
vmstate_save_state(). This simplifies the API and improves code
maintainability.

Signed-off-by: Arun Menon <armenon@redhat.com>
---
 hw/display/virtio-gpu.c     |  3 ++-
 hw/pci/pci.c                |  2 +-
 hw/s390x/virtio-ccw.c       |  2 +-
 hw/scsi/spapr_vscsi.c       |  2 +-
 hw/vfio/pci.c               |  4 ++--
 hw/virtio/virtio-mmio.c     |  2 +-
 hw/virtio/virtio-pci.c      |  2 +-
 hw/virtio/virtio.c          |  4 ++--
 include/migration/vmstate.h |  2 --
 migration/cpr.c             |  5 +++--
 migration/savevm.c          |  7 ++++---
 migration/vmstate-types.c   | 12 +++++++-----
 migration/vmstate.c         | 18 ++++++++----------
 tests/unit/test-vmstate.c   |  8 +++++---
 ui/vdagent.c                |  3 ++-
 15 files changed, 40 insertions(+), 36 deletions(-)

diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 5ff9f5e6dc0b6b112e36170318c900f893a73c5e..bddb26fa21312e5d0b2b6412cdf2a6c28dd06eb3 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -1246,7 +1246,8 @@ 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);
+    return vmstate_save_state(f, &vmstate_virtio_gpu_scanouts, g, NULL,
+                              &error_warn);
 }
 
 static bool virtio_gpu_load_restore_mapping(VirtIOGPU *g,
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 80545189980f176ca6a3dc9abce7043c8bc2708c..f7fb3931ff190c25ffa665ba80abe7737dbefdb6 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -926,7 +926,7 @@ void pci_device_save(PCIDevice *s, QEMUFile *f)
      * 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);
+    vmstate_save_state(f, &vmstate_pci_device, s, NULL, &error_warn);
     /* Restore the interrupt status bit. */
     pci_update_irq_status(s);
 }
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
index bafcb9b76d81749925e31b5a0a1320b3332ad2cf..14304fd3e8901d28a7788266df2c7ef7334b691c 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -1130,7 +1130,7 @@ 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);
+    vmstate_save_state(f, &vmstate_virtio_ccw_dev, dev, NULL, &error_warn);
 }
 
 static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c
index a4812afd0cc7c495080ef03a531c7d279af29b33..2204beb40c7cdc827db2bf86fb8612d8c09999ed 100644
--- a/hw/scsi/spapr_vscsi.c
+++ b/hw/scsi/spapr_vscsi.c
@@ -630,7 +630,7 @@ static void vscsi_save_request(QEMUFile *f, SCSIRequest *sreq)
     vscsi_req *req = sreq->hba_private;
     assert(req->active);
 
-    vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL);
+    vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL, &error_warn);
 
     trace_spapr_vscsi_save_request(req->qtag, req->cur_desc_num,
                                    req->cur_desc_offset);
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 04d385d7674f444844beeee7364ee0424f762758..8e2b6d611de8efdf98235355226f5e5a6fd87b19 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -2780,8 +2780,8 @@ static int vfio_pci_save_config(VFIODevice *vbasedev, QEMUFile *f, Error **errp)
 {
     VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev);
 
-    return vmstate_save_state_with_err(f, &vmstate_vfio_pci_config, vdev, NULL,
-                                       errp);
+    return vmstate_save_state(f, &vmstate_vfio_pci_config, vdev, NULL,
+                              errp);
 }
 
 static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f)
diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c
index f14f0487fec3ed30d2cc3aec96950c2121265716..58957990743f061e78d0eafcfcb6cc5989252edc 100644
--- a/hw/virtio/virtio-mmio.c
+++ b/hw/virtio/virtio-mmio.c
@@ -613,7 +613,7 @@ static void virtio_mmio_save_extra_state(DeviceState *opaque, QEMUFile *f)
 {
     VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
 
-    vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL);
+    vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL, &error_warn);
 }
 
 static int virtio_mmio_load_extra_state(DeviceState *opaque, QEMUFile *f)
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 07f144d6d1bfbb561b28015de71762601080dc23..0826399cdfbf09b5b3f9acd9967de3417858c9d2 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -154,7 +154,7 @@ static void virtio_pci_save_extra_state(DeviceState *d, QEMUFile *f)
 {
     VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
 
-    vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL);
+    vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL, &error_warn);
 }
 
 static int virtio_pci_load_extra_state(DeviceState *d, QEMUFile *f)
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index aac317a93c3cd55ece9b0fd858c7b2459f8242f9..552e167529e9c22fb4cba2b445ffad85fef619f3 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -3025,14 +3025,14 @@ int virtio_save(VirtIODevice *vdev, QEMUFile *f)
     }
 
     if (vdc->vmsd) {
-        int ret = vmstate_save_state(f, vdc->vmsd, vdev, NULL);
+        int ret = vmstate_save_state(f, vdc->vmsd, vdev, NULL, &error_warn);
         if (ret) {
             return ret;
         }
     }
 
     /* Subsections */
-    return vmstate_save_state(f, &vmstate_virtio, vdev, NULL);
+    return vmstate_save_state(f, &vmstate_virtio, vdev, NULL, &error_warn);
 }
 
 /* A wrapper for use as a VMState .put function */
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 056781b1c21e737583f081594d9f88b32adfd674..5fe9bbf39058d0cf97c1adab54cc516dbe8dc32a 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -1198,8 +1198,6 @@ extern const VMStateInfo vmstate_info_qlist;
 int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
                        void *opaque, int version_id, Error **errp);
 int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
-                       void *opaque, JSONWriter *vmdesc);
-int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription *vmsd,
                        void *opaque, JSONWriter *vmdesc, Error **errp);
 int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
                          void *opaque, JSONWriter *vmdesc,
diff --git a/migration/cpr.c b/migration/cpr.c
index bdb24736f44e91ba59b6e622a315597c97e7f64d..d48f013dac21b531e41a26f4bb33eefe32097757 100644
--- a/migration/cpr.c
+++ b/migration/cpr.c
@@ -163,6 +163,7 @@ bool cpr_is_incoming(void)
 
 int cpr_state_save(MigrationChannel *channel, Error **errp)
 {
+    ERRP_GUARD();
     int ret;
     QEMUFile *f;
     MigMode mode = migrate_mode();
@@ -182,9 +183,9 @@ int cpr_state_save(MigrationChannel *channel, Error **errp)
     qemu_put_be32(f, QEMU_CPR_FILE_MAGIC);
     qemu_put_be32(f, QEMU_CPR_FILE_VERSION);
 
-    ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0);
+    ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0, errp);
     if (ret) {
-        error_setg(errp, "vmstate_save_state error %d", ret);
+        error_prepend(errp, "vmstate_save_state error %d: ", ret);
         qemu_fclose(f);
         return ret;
     }
diff --git a/migration/savevm.c b/migration/savevm.c
index 4df50fb5c674e56c0173c357c1e23367373e2114..3cb9c34efb22fdcb2b05cf1fefcb6fbd80cadca5 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -1056,8 +1056,8 @@ static int vmstate_save(QEMUFile *f, SaveStateEntry *se, JSONWriter *vmdesc,
     if (!se->vmsd) {
         vmstate_save_old_style(f, se, vmdesc);
     } else {
-        ret = vmstate_save_state_with_err(f, se->vmsd, se->opaque, vmdesc,
-                                          errp);
+        ret = vmstate_save_state(f, se->vmsd, se->opaque, vmdesc,
+                                 errp);
         if (ret) {
             return ret;
         }
@@ -1303,7 +1303,8 @@ void qemu_savevm_state_header(QEMUFile *f)
             json_writer_start_object(vmdesc, "configuration");
         }
 
-        vmstate_save_state(f, &vmstate_configuration, &savevm_state, vmdesc);
+        vmstate_save_state(f, &vmstate_configuration, &savevm_state, vmdesc,
+                           &error_warn);
 
         if (vmdesc) {
             json_writer_end_object(vmdesc);
diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c
index e2020a733d0921d748b6f832a193e5de8d302d5f..2b88dc145ab32072f09845cee95b79aad7ac3cb2 100644
--- a/migration/vmstate-types.c
+++ b/migration/vmstate-types.c
@@ -564,7 +564,7 @@ static int put_tmp(QEMUFile *f, void *pv, size_t size,
 
     /* Writes the parent field which is at the start of the tmp */
     *(void **)tmp = pv;
-    ret = vmstate_save_state(f, vmsd, tmp, vmdesc);
+    ret = vmstate_save_state(f, vmsd, tmp, vmdesc, &error_warn);
     g_free(tmp);
 
     return ret;
@@ -675,7 +675,7 @@ static int put_qtailq(QEMUFile *f, void *pv, size_t unused_size,
 
     QTAILQ_RAW_FOREACH(elm, pv, entry_offset) {
         qemu_put_byte(f, true);
-        ret = vmstate_save_state(f, vmsd, elm, vmdesc);
+        ret = vmstate_save_state(f, vmsd, elm, vmdesc, &error_warn);
         if (ret) {
             return ret;
         }
@@ -712,7 +712,8 @@ static gboolean put_gtree_elem(gpointer key, gpointer value, gpointer data)
     if (!capsule->key_vmsd) {
         qemu_put_be64(f, (uint64_t)(uintptr_t)(key)); /* direct key */
     } else {
-        ret = vmstate_save_state(f, capsule->key_vmsd, key, capsule->vmdesc);
+        ret = vmstate_save_state(f, capsule->key_vmsd, key, capsule->vmdesc,
+                                 &error_warn);
         if (ret) {
             capsule->ret = ret;
             return true;
@@ -720,7 +721,8 @@ static gboolean put_gtree_elem(gpointer key, gpointer value, gpointer data)
     }
 
     /* put the data */
-    ret = vmstate_save_state(f, capsule->val_vmsd, value, capsule->vmdesc);
+    ret = vmstate_save_state(f, capsule->val_vmsd, value, capsule->vmdesc,
+                             &error_warn);
     if (ret) {
         capsule->ret = ret;
         return true;
@@ -856,7 +858,7 @@ static int put_qlist(QEMUFile *f, void *pv, size_t unused_size,
     trace_put_qlist(field->name, vmsd->name, vmsd->version_id);
     QLIST_RAW_FOREACH(elm, pv, entry_offset) {
         qemu_put_byte(f, true);
-        ret = vmstate_save_state(f, vmsd, elm, vmdesc);
+        ret = vmstate_save_state(f, vmsd, elm, vmdesc, &error_warn);
         if (ret) {
             error_report("%s: failed to save %s (%d)", field->name,
                          vmsd->name, ret);
diff --git a/migration/vmstate.c b/migration/vmstate.c
index ef78a1e62ad92e9608de72d125da80ea496c8dd1..569e66ea983f833e6a0d651d2a751f34a64e8f5c 100644
--- a/migration/vmstate.c
+++ b/migration/vmstate.c
@@ -403,12 +403,6 @@ bool vmstate_section_needed(const VMStateDescription *vmsd, void *opaque)
 
 
 int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
-                       void *opaque, JSONWriter *vmdesc_id)
-{
-    return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id, vmsd->version_id, NULL);
-}
-
-int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription *vmsd,
                        void *opaque, JSONWriter *vmdesc_id, Error **errp)
 {
     return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id, vmsd->version_id, errp);
@@ -530,7 +524,7 @@ static int vmstate_save_dispatch(QEMUFile *f,
 
                 if (inner_field->flags & VMS_STRUCT) {
                     ret = vmstate_save_state(f, inner_field->vmsd,
-                                             curr_elem, vmdesc_loop);
+                                             curr_elem, vmdesc_loop, errp);
                 } else if (inner_field->flags & VMS_VSTRUCT) {
                     ret = vmstate_save_state_v(f, inner_field->vmsd,
                                                curr_elem, vmdesc_loop,
@@ -539,6 +533,10 @@ static int vmstate_save_dispatch(QEMUFile *f,
                 } else {
                     ret = inner_field->info->put(f, curr_elem, size,
                                                  inner_field, vmdesc_loop);
+                    if (ret) {
+                        error_setg(errp, "Save of field %s failed",
+                                   inner_field->name);
+                    }
                 }
 
                 written_bytes = qemu_file_transferred(f) - old_offset;
@@ -551,8 +549,8 @@ static int vmstate_save_dispatch(QEMUFile *f,
                 }
 
                 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);
                     ps_ret = post_save_dispatch(vmsd, opaque, &local_err);
                     if (ps_ret) {
                         ret = ps_ret;
@@ -723,7 +721,7 @@ static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
             qemu_put_byte(f, len);
             qemu_put_buffer(f, (uint8_t *)vmsdsub->name, len);
             qemu_put_be32(f, vmsdsub->version_id);
-            ret = vmstate_save_state_with_err(f, vmsdsub, opaque, vmdesc, errp);
+            ret = vmstate_save_state(f, vmsdsub, opaque, vmdesc, errp);
             if (ret) {
                 return ret;
             }
diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c
index 807deb3531f3079864ac99567d4dece7122571dd..5f31ed4b6969c4ebf72b298f965bcbdc752ff87f 100644
--- a/tests/unit/test-vmstate.c
+++ b/tests/unit/test-vmstate.c
@@ -69,7 +69,7 @@ static void save_vmstate(const VMStateDescription *desc, void *obj)
     QEMUFile *f = open_test_file(true);
 
     /* Save file with vmstate */
-    int ret = vmstate_save_state(f, desc, obj, NULL);
+    int ret = vmstate_save_state(f, desc, obj, NULL, &error_warn);
     g_assert(!ret);
     qemu_put_byte(f, QEMU_VM_EOF);
     g_assert(!qemu_file_get_error(f));
@@ -429,7 +429,8 @@ static void test_save_noskip(void)
     QEMUFile *fsave = open_test_file(true);
     TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
                        .skip_c_e = false };
-    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
+    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL,
+                                 &error_warn);
     g_assert(!ret);
     g_assert(!qemu_file_get_error(fsave));
 
@@ -451,7 +452,8 @@ static void test_save_skip(void)
     QEMUFile *fsave = open_test_file(true);
     TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
                        .skip_c_e = true };
-    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
+    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL,
+                                 &error_warn);
     g_assert(!ret);
     g_assert(!qemu_file_get_error(fsave));
 
diff --git a/ui/vdagent.c b/ui/vdagent.c
index b9a22a689d9acfeafb862ae73308db6fbd52331e..e0fc7ff71468bfe66761385387653c742c375f05 100644
--- a/ui/vdagent.c
+++ b/ui/vdagent.c
@@ -992,7 +992,8 @@ static int put_cbinfo(QEMUFile *f, void *pv, size_t size,
         }
     }
 
-    return vmstate_save_state(f, &vmstate_cbinfo_array, &cbinfo, vmdesc);
+    return vmstate_save_state(f, &vmstate_cbinfo_array, &cbinfo, vmdesc,
+                              &error_warn);
 }
 
 static int get_cbinfo(QEMUFile *f, void *pv, size_t size,

-- 
2.50.1



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

* [PATCH v9 26/27] migration: Add error-parameterized function variants in VMSD struct
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (24 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 25/27] migration: Remove error variant of vmstate_save_state() function Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  2025-08-06  5:45   ` Akihiko Odaki
  2025-08-05 18:25 ` [PATCH v9 27/27] backends/tpm: Propagate vTPM error on migration failure Arun Menon
  26 siblings, 1 reply; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon

- We need to have good error reporting in the callbacks in
  VMStateDescription struct. Specifically pre_save, post_save,
  pre_load and post_load callbacks.
- It is not possible to change these functions everywhere in one
  patch, therefore, we introduce a duplicate set of callbacks
  with Error object passed to them.
- So, in this commit, we implement 'errp' variants of these callbacks,
  introducing an explicit Error object parameter.
- This is a functional step towards transitioning the entire codebase
  to the new error-parameterized functions.
- Deliberately called in mutual exclusion from their counterparts,
  to prevent conflicts during the transition.
- New impls should preferentally use 'errp' variants of
  these methods, and existing impls incrementally converted.
  The variants without 'errp' are intended to be removed
  once all usage is converted.

Signed-off-by: Arun Menon <armenon@redhat.com>
---
 docs/devel/migration/main.rst | 24 ++++++++++++++++++++++++
 include/migration/vmstate.h   | 15 +++++++++++++++
 migration/vmstate.c           | 34 ++++++++++++++++++++++++++++++----
 3 files changed, 69 insertions(+), 4 deletions(-)

diff --git a/docs/devel/migration/main.rst b/docs/devel/migration/main.rst
index 6493c1d2bca48a2fa34d92f6c0979c215c56b8d5..35bf5ae26c87f3c82964eb15618be373ab5a41fc 100644
--- a/docs/devel/migration/main.rst
+++ b/docs/devel/migration/main.rst
@@ -444,6 +444,30 @@ The functions to do that are inside a vmstate definition, and are called:
   This function is called after we save the state of one device
   (even upon failure, unless the call to pre_save returned an error).
 
+Following are the errp variants of these functions.
+
+- ``int (*pre_load_errp)(void *opaque, Error **errp);``
+
+  This function is called before we load the state of one device.
+
+- ``int (*post_load_errp)(void *opaque, int version_id, Error **errp);``
+
+  This function is called after we load the state of one device.
+
+- ``int (*pre_save_errp)(void *opaque, Error **errp);``
+
+  This function is called before we save the state of one device.
+
+- ``int (*post_save_errp)(void *opaque, Error **errp);``
+
+  This function is called after we save the state of one device
+  (even upon failure, unless the call to pre_save returned an error).
+
+New impls should preferentally use 'errp' variants of these
+methods and existing impls incrementally converted.
+The variants without 'errp' are intended to be removed
+once all usage is converted.
+
 Example: You can look at hpet.c, that uses the first three functions
 to massage the state that is transferred.
 
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 5fe9bbf39058d0cf97c1adab54cc516dbe8dc32a..51baf6c7f9d061ee33949d7e798f2184e50b4cbf 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -200,15 +200,30 @@ struct VMStateDescription {
      * exclusive. For this reason, also early_setup VMSDs are migrated in a
      * QEMU_VM_SECTION_FULL section, while save_setup() data is migrated in
      * a QEMU_VM_SECTION_START section.
+     *
+     * There are duplicate impls of the post/pre save/load hooks.
+     * New impls should preferentally use 'errp' variants of these
+     * methods and existing impls incrementally converted.
+     * The variants without 'errp' are intended to be removed
+     * once all usage is converted.
+     *
+     * For the errp variants,
+     * Returns: 0 on success,
+     *          <0 on error where -value is an error number from errno.h
      */
+
     bool early_setup;
     int version_id;
     int minimum_version_id;
     MigrationPriority priority;
     int (*pre_load)(void *opaque);
+    int (*pre_load_errp)(void *opaque, Error **errp);
     int (*post_load)(void *opaque, int version_id);
+    int (*post_load_errp)(void *opaque, int version_id, Error **errp);
     int (*pre_save)(void *opaque);
+    int (*pre_save_errp)(void *opaque, Error **errp);
     int (*post_save)(void *opaque);
+    int (*post_save_errp)(void *opaque, Error **errp);
     bool (*needed)(void *opaque);
     bool (*dev_unplug_pending)(void *opaque);
 
diff --git a/migration/vmstate.c b/migration/vmstate.c
index 569e66ea983f833e6a0d651d2a751f34a64e8f5c..0acaa855cfec8ddac63e33d4562e39c345856213 100644
--- a/migration/vmstate.c
+++ b/migration/vmstate.c
@@ -152,7 +152,16 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
         trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL);
         return -EINVAL;
     }
-    if (vmsd->pre_load) {
+    if (vmsd->pre_load_errp) {
+        ret = vmsd->pre_load_errp(opaque, errp);
+        if (ret) {
+            error_prepend(errp, "VM pre load failed for: '%s', "
+                          "version_id: %d, minimum version_id: %d, "
+                          "ret: %d: ", vmsd->name, vmsd->version_id,
+                          vmsd->minimum_version_id, ret);
+            return ret;
+        }
+    } else if (vmsd->pre_load) {
         ret = vmsd->pre_load(opaque);
         if (ret) {
             error_setg(errp, "VM pre load failed for: '%s', "
@@ -242,7 +251,14 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
         qemu_file_set_error(f, ret);
         return ret;
     }
-    if (vmsd->post_load) {
+    if (vmsd->post_load_errp) {
+        ret = vmsd->post_load_errp(opaque, version_id, errp);
+        if (ret < 0) {
+            error_prepend(errp, "VM Post load failed for: %s, version_id: %d, "
+                          "minimum_version: %d, ret: %d: ", vmsd->name,
+                          vmsd->version_id, vmsd->minimum_version_id, ret);
+        }
+    } else if (vmsd->post_load) {
         ret = vmsd->post_load(opaque, version_id);
         if (ret < 0) {
             error_setg(errp,
@@ -411,8 +427,16 @@ int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
 static int pre_save_dispatch(const VMStateDescription *vmsd, void *opaque,
                              Error **errp)
 {
+    ERRP_GUARD();
     int ret = 0;
-    if (vmsd->pre_save) {
+    if (vmsd->pre_save_errp) {
+        ret = vmsd->pre_save_errp(opaque, errp);
+        trace_vmstate_save_state_pre_save_res(vmsd->name, ret);
+        if (ret) {
+            error_prepend(errp, "pre-save for %s failed, ret: %d: ",
+                          vmsd->name, ret);
+        }
+    } else if (vmsd->pre_save) {
         ret = vmsd->pre_save(opaque);
         trace_vmstate_save_state_pre_save_res(vmsd->name, ret);
         if (ret) {
@@ -427,7 +451,9 @@ static int post_save_dispatch(const VMStateDescription *vmsd, void *opaque,
                               Error **errp)
 {
     int ret = 0;
-    if (vmsd->post_save) {
+    if (vmsd->post_save_errp) {
+        ret = vmsd->post_save_errp(opaque, errp);
+    } else if (vmsd->post_save) {
         ret = vmsd->post_save(opaque);
         error_setg(errp, "post-save for %s failed, ret: %d",
                    vmsd->name, ret);

-- 
2.50.1



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

* [PATCH v9 27/27] backends/tpm: Propagate vTPM error on migration failure
  2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
                   ` (25 preceding siblings ...)
  2025-08-05 18:25 ` [PATCH v9 26/27] migration: Add error-parameterized function variants in VMSD struct Arun Menon
@ 2025-08-05 18:25 ` Arun Menon
  26 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-05 18:25 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Akihiko Odaki,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger, Arun Menon,
	Daniel P. Berrangé, Stefan Berger

- When migration of a VM with encrypted vTPM fails on the
  destination host, (e.g., due to a mismatch in secret values),
  the error message displayed on the source host is generic and unhelpful.
- For example, a typical error looks like this:
  "operation failed: job 'migration out' failed: Sibling indicated error 1.
  operation failed: job 'migration in' failed: load of migration failed:
  Input/output error"
- Such generic errors are logged using error_report(), which prints to
  the console/monitor but does not make the detailed error accessible via
  the QMP query-migrate command.
- This change, along with the set of changes of passing errp Error object
  to the VM state loading functions, help in addressing the issue.
  We use the post_load_errp hook of VMStateDescription to propagate errors
  by setting Error **errp objects in case of failure in the TPM backend.
- It can then be retrieved using QMP command:
  {"execute" : "query-migrate"}

Buglink: https://issues.redhat.com/browse/RHEL-82826

Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Arun Menon <armenon@redhat.com>
---
 backends/tpm/tpm_emulator.c | 40 +++++++++++++++++++++-------------------
 1 file changed, 21 insertions(+), 19 deletions(-)

diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c
index 4a234ab2c0b19b2604bf0dd8cb5f4540c72a9438..dacfca5ab7eb0445ddc1ced97513068501b468bf 100644
--- a/backends/tpm/tpm_emulator.c
+++ b/backends/tpm/tpm_emulator.c
@@ -819,7 +819,8 @@ static int tpm_emulator_get_state_blobs(TPMEmulator *tpm_emu)
 static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu,
                                        uint32_t type,
                                        TPMSizedBuffer *tsb,
-                                       uint32_t flags)
+                                       uint32_t flags,
+                                       Error **errp)
 {
     ssize_t n;
     ptm_setstate pss;
@@ -838,17 +839,18 @@ static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu,
     /* write the header only */
     if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_STATEBLOB, &pss,
                              offsetof(ptm_setstate, u.req.data), 0, 0) < 0) {
-        error_report("tpm-emulator: could not set state blob type %d : %s",
-                     type, strerror(errno));
+        error_setg_errno(errp, errno,
+                         "tpm-emulator: could not set state blob type %d",
+                         type);
         return -1;
     }
 
     /* now the body */
     n = qemu_chr_fe_write_all(&tpm_emu->ctrl_chr, tsb->buffer, tsb->size);
     if (n != tsb->size) {
-        error_report("tpm-emulator: Writing the stateblob (type %d) "
-                     "failed; could not write %u bytes, but only %zd",
-                     type, tsb->size, n);
+        error_setg(errp, "tpm-emulator: Writing the stateblob (type %d) "
+                   "failed; could not write %u bytes, but only %zd",
+                   type, tsb->size, n);
         return -1;
     }
 
@@ -856,17 +858,17 @@ static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu,
     n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr,
                              (uint8_t *)&pss, sizeof(pss.u.resp));
     if (n != sizeof(pss.u.resp)) {
-        error_report("tpm-emulator: Reading response from writing stateblob "
-                     "(type %d) failed; expected %zu bytes, got %zd", type,
-                     sizeof(pss.u.resp), n);
+        error_setg(errp, "tpm-emulator: Reading response from writing "
+                   "stateblob (type %d) failed; expected %zu bytes, "
+                   "got %zd", type, sizeof(pss.u.resp), n);
         return -1;
     }
 
     tpm_result = be32_to_cpu(pss.u.resp.tpm_result);
     if (tpm_result != 0) {
-        error_report("tpm-emulator: Setting the stateblob (type %d) failed "
-                     "with a TPM error 0x%x %s", type, tpm_result,
-                     tpm_emulator_strerror(tpm_result));
+        error_setg(errp, "tpm-emulator: Setting the stateblob (type %d) "
+                   "failed with a TPM error 0x%x %s", type, tpm_result,
+                   tpm_emulator_strerror(tpm_result));
         return -1;
     }
 
@@ -880,7 +882,7 @@ static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu,
  *
  * Returns a negative errno code in case of error.
  */
-static int tpm_emulator_set_state_blobs(TPMBackend *tb)
+static int tpm_emulator_set_state_blobs(TPMBackend *tb, Error **errp)
 {
     TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
     TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs;
@@ -894,13 +896,13 @@ static int tpm_emulator_set_state_blobs(TPMBackend *tb)
 
     if (tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT,
                                     &state_blobs->permanent,
-                                    state_blobs->permanent_flags) < 0 ||
+                                    state_blobs->permanent_flags, errp) < 0 ||
         tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE,
                                     &state_blobs->volatil,
-                                    state_blobs->volatil_flags) < 0 ||
+                                    state_blobs->volatil_flags, errp) < 0 ||
         tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE,
                                     &state_blobs->savestate,
-                                    state_blobs->savestate_flags) < 0) {
+                                    state_blobs->savestate_flags, errp) < 0) {
         return -EIO;
     }
 
@@ -948,12 +950,12 @@ static void tpm_emulator_vm_state_change(void *opaque, bool running,
  *
  * Returns negative errno codes in case of error.
  */
-static int tpm_emulator_post_load(void *opaque, int version_id)
+static int tpm_emulator_post_load(void *opaque, int version_id, Error **errp)
 {
     TPMBackend *tb = opaque;
     int ret;
 
-    ret = tpm_emulator_set_state_blobs(tb);
+    ret = tpm_emulator_set_state_blobs(tb, errp);
     if (ret < 0) {
         return ret;
     }
@@ -969,7 +971,7 @@ static const VMStateDescription vmstate_tpm_emulator = {
     .name = "tpm-emulator",
     .version_id = 0,
     .pre_save = tpm_emulator_pre_save,
-    .post_load = tpm_emulator_post_load,
+    .post_load_errp = tpm_emulator_post_load,
     .fields = (const VMStateField[]) {
         VMSTATE_UINT32(state_blobs.permanent_flags, TPMEmulator),
         VMSTATE_UINT32(state_blobs.permanent.size, TPMEmulator),

-- 
2.50.1



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

* Re: [PATCH v9 23/27] migration: Refactor vmstate_save_state_v() function
  2025-08-05 18:25 ` [PATCH v9 23/27] migration: Refactor vmstate_save_state_v() function Arun Menon
@ 2025-08-06  5:10   ` Akihiko Odaki
  2025-08-06  8:19   ` Marc-André Lureau
  1 sibling, 0 replies; 63+ messages in thread
From: Akihiko Odaki @ 2025-08-06  5:10 UTC (permalink / raw)
  To: Arun Menon, qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Dmitry Osipenko,
	Michael S. Tsirkin, Marcel Apfelbaum, Cornelia Huck, Halil Pasic,
	Eric Farman, Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare,
	Marc-André Lureau, qemu-s390x, qemu-ppc, Hailiang Zhang,
	Stefan Berger

On 2025/08/06 3:25, Arun Menon wrote:
> The original vmstate_save_state_v() function combined multiple
> responsibilities like calling pre-save hooks, saving the state of
> the device, handling subsection saves and invoking post-save hooks.
> This led to a lengthy and less readable function.
> 
> To address this, introduce wrapper functions for pre-save,
> post-save and the device-state save functionalities.
> 
> Signed-off-by: Arun Menon <armenon@redhat.com>
> ---
>   migration/vmstate.c | 78 ++++++++++++++++++++++++++++++++++++++++-------------
>   1 file changed, 60 insertions(+), 18 deletions(-)
> 
> diff --git a/migration/vmstate.c b/migration/vmstate.c
> index 60ff38858cf54277992fa5eddeadb6f3d70edec3..fbc59caadbbcc75fe6de27b459aa9aa25e76aa0a 100644
> --- a/migration/vmstate.c
> +++ b/migration/vmstate.c
> @@ -414,22 +414,43 @@ int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription *vmsd,
>       return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id, vmsd->version_id, errp);
>   }
>   
> -int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
> -                         void *opaque, JSONWriter *vmdesc, int version_id, Error **errp)
> +static int pre_save_dispatch(const VMStateDescription *vmsd, void *opaque,
> +                             Error **errp)
>   {
>       int ret = 0;
> -    const VMStateField *field = vmsd->fields;
> -
> -    trace_vmstate_save_state_top(vmsd->name);
> -
>       if (vmsd->pre_save) {
>           ret = vmsd->pre_save(opaque);
>           trace_vmstate_save_state_pre_save_res(vmsd->name, ret);
>           if (ret) {
> -            error_setg(errp, "pre-save failed: %s", vmsd->name);
> -            return ret;
> +            error_setg(errp, "pre-save for %s failed, ret: %d",
> +                       vmsd->name, ret);
>           }
>       }
> +    return ret;
> +}
> +
> +static int post_save_dispatch(const VMStateDescription *vmsd, void *opaque,
> +                              Error **errp)
> +{
> +    int ret = 0;
> +    if (vmsd->post_save) {
> +        ret = vmsd->post_save(opaque);
> +        error_setg(errp, "post-save for %s failed, ret: %d",
> +                   vmsd->name, ret);
> +    }
> +    return ret;
> +}
> +
> +static int vmstate_save_dispatch(QEMUFile *f,
> +                                 const VMStateDescription *vmsd,
> +                                 void *opaque, JSONWriter *vmdesc,
> +                                 int version_id, Error **errp)
> +{
> +    ERRP_GUARD();
> +    int ret = 0;
> +    int ps_ret = 0;
> +    Error *local_err = NULL;
> +    const VMStateField *field = vmsd->fields;
>   
>       if (vmdesc) {
>           json_writer_str(vmdesc, "vmsd_name", vmsd->name);
> @@ -532,9 +553,7 @@ 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);
> -                    if (vmsd->post_save) {
> -                        vmsd->post_save(opaque);
> -                    }
> +                    ps_ret = post_save_dispatch(vmsd, opaque, &local_err);
>                       return ret;
>                   }
>   
> @@ -557,16 +576,39 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
>       if (vmdesc) {
>           json_writer_end_array(vmdesc);
>       }
> +    return ret;
> +}
>   
> -    ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc, errp);
>   
> -    if (vmsd->post_save) {
> -        int ps_ret = vmsd->post_save(opaque);
> -        if (!ret && ps_ret) {
> -            ret = ps_ret;
> -            error_setg(errp, "post-save failed: %s", vmsd->name);
> -        }
> +int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
> +                         void *opaque, JSONWriter *vmdesc, int version_id,
> +                         Error **errp)
> +{
> +    ERRP_GUARD();
> +    int ret = 0;
> +    Error *local_err = NULL;
> +    int ps_ret = 0;
> +
> +    trace_vmstate_save_state_top(vmsd->name);
> +
> +    ret = pre_save_dispatch(vmsd, opaque, errp);
> +    if (ret) {
> +        return ret;
> +    }
> +
> +    ret = vmstate_save_dispatch(f, vmsd, opaque, vmdesc,
> +                                version_id, errp);
> +    if (ret) {
> +        return ret;
>       }
> +
> +    ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc, errp);
> +    ps_ret = post_save_dispatch(vmsd, opaque, &local_err);

local_err leaks here.

> +    if (!ret && ps_ret) {
> +        ret = ps_ret;
> +        error_setg(errp, "post-save failed: %s", vmsd->name);
> +    }
> +
>       return ret;
>   }
>   
> 



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

* Re: [PATCH v9 09/27] migration: push Error **errp into qemu_loadvm_state_main()
  2025-08-05 18:25 ` [PATCH v9 09/27] migration: push Error **errp into qemu_loadvm_state_main() Arun Menon
@ 2025-08-06  5:14   ` Akihiko Odaki
  2025-08-06  7:34   ` Marc-André Lureau
  1 sibling, 0 replies; 63+ messages in thread
From: Akihiko Odaki @ 2025-08-06  5:14 UTC (permalink / raw)
  To: Arun Menon, qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Dmitry Osipenko,
	Michael S. Tsirkin, Marcel Apfelbaum, Cornelia Huck, Halil Pasic,
	Eric Farman, Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare,
	Marc-André Lureau, qemu-s390x, qemu-ppc, Hailiang Zhang,
	Stefan Berger, Daniel P. Berrangé

On 2025/08/06 3:25, Arun Menon wrote:
> This is an incremental step in converting vmstate loading
> code to report error via Error objects instead of directly
> printing it to console/monitor.
> It is ensured that qemu_loadvm_state_main() must report an error
> in errp, in case of failure.
> loadvm_process_command also sets the errp object explicitly.
> 
> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
> Signed-off-by: Arun Menon <armenon@redhat.com>
> ---
>   migration/colo.c   |  5 +++--
>   migration/savevm.c | 33 ++++++++++++++++++++-------------
>   migration/savevm.h |  3 ++-
>   3 files changed, 25 insertions(+), 16 deletions(-)
> 
> diff --git a/migration/colo.c b/migration/colo.c
> index 0ba22ee76a13e313793f653f43a728e3c433bbc1..a96e4dba15516b71d1b315c736c3b4879ff04e58 100644
> --- a/migration/colo.c
> +++ b/migration/colo.c
> @@ -659,6 +659,7 @@ void migrate_start_colo_process(MigrationState *s)
>   static void colo_incoming_process_checkpoint(MigrationIncomingState *mis,
>                         QEMUFile *fb, QIOChannelBuffer *bioc, Error **errp)
>   {
> +    ERRP_GUARD();
>       uint64_t total_size;
>       uint64_t value;
>       Error *local_err = NULL;
> @@ -686,11 +687,11 @@ static void colo_incoming_process_checkpoint(MigrationIncomingState *mis,
>   
>       bql_lock();
>       cpu_synchronize_all_states();
> -    ret = qemu_loadvm_state_main(mis->from_src_file, mis);
> +    ret = qemu_loadvm_state_main(mis->from_src_file, mis, errp);
>       bql_unlock();
>   
>       if (ret < 0) {
> -        error_setg(errp, "Load VM's live state (ram) error");
> +        error_prepend(errp, "Load VM's live state (ram) error: ");
>           return;
>       }
>   
> diff --git a/migration/savevm.c b/migration/savevm.c
> index f3b91c8ae0eee6078406081f0bd7f686fed28601..ad3dd9b172afc541f104d2187a79bafa8e380466 100644
> --- a/migration/savevm.c
> +++ b/migration/savevm.c
> @@ -2105,7 +2105,7 @@ static void *postcopy_ram_listen_thread(void *opaque)
>       qemu_file_set_blocking(f, true);
>   
>       /* TODO: sanity check that only postcopiable data will be loaded here */
> -    load_res = qemu_loadvm_state_main(f, mis);
> +    load_res = qemu_loadvm_state_main(f, mis, NULL);
>   
>       /*
>        * This is tricky, but, mis->from_src_file can change after it
> @@ -2407,6 +2407,7 @@ static int loadvm_postcopy_handle_resume(MigrationIncomingState *mis)
>    */
>   static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis, Error **errp)
>   {
> +    ERRP_GUARD();
>       int ret;
>       size_t length;
>       QIOChannelBuffer *bioc;
> @@ -2456,9 +2457,9 @@ static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis, Error **errp)
>           qemu_coroutine_yield();
>       } while (1);
>   
> -    ret = qemu_loadvm_state_main(packf, mis);
> +    ret = qemu_loadvm_state_main(packf, mis, errp);
>       if (ret < 0) {
> -        error_setg(errp, "VM state load failed: %d", ret);
> +        error_prepend(errp, "Loading VM state failed: %d: ", ret);
>       }
>       trace_loadvm_handle_cmd_packaged_main(ret);
>       qemu_fclose(packf);
> @@ -3074,8 +3075,10 @@ static bool postcopy_pause_incoming(MigrationIncomingState *mis)
>       return true;
>   }
>   
> -int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis)
> +int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis,
> +                           Error **errp)
>   {
> +    ERRP_GUARD();
>       uint8_t section_type;
>       int ret = 0;
>   
> @@ -3083,8 +3086,10 @@ retry:
>       while (true) {
>           section_type = qemu_get_byte(f);
>   
> -        ret = qemu_file_get_error_obj_any(f, mis->postcopy_qemufile_dst, NULL);
> +        ret = qemu_file_get_error_obj_any(f, mis->postcopy_qemufile_dst, errp);
>           if (ret) {
> +            error_prepend(errp, "Failed to load device state section ID: %d: ",
> +                          ret);
>               break;
>           }
>   
> @@ -3105,7 +3110,7 @@ retry:
>               }
>               break;
>           case QEMU_VM_COMMAND:
> -            ret = loadvm_process_command(f, NULL);
> +            ret = loadvm_process_command(f, errp);
>               trace_qemu_loadvm_state_section_command(ret);
>               if ((ret < 0) || (ret == LOADVM_QUIT)) {
>                   goto out;
> @@ -3115,7 +3120,7 @@ retry:
>               /* This is the end of migration */
>               goto out;
>           default:
> -            error_report("Unknown savevm section type %d", section_type);
> +            error_setg(errp, "Unknown savevm section type %d", section_type);
>               ret = -EINVAL;
>               goto out;
>           }
> @@ -3123,6 +3128,9 @@ retry:
>   
>   out:
>       if (ret < 0) {
> +        if (*errp == NULL) {
> +            error_setg(errp, "Loading VM state failed: %d", ret);
> +        }
>           qemu_file_set_error(f, ret);
>   
>           /* Cancel bitmaps incoming regardless of recovery */
> @@ -3143,6 +3151,8 @@ out:
>               migrate_postcopy_ram() && postcopy_pause_incoming(mis)) {
>               /* Reset f to point to the newly created channel */
>               f = mis->from_src_file;
> +            error_free(*errp);
> +            *errp = NULL;

error_free_or_abort() is a convenient alternative that can save "*errp = 
NULL" and also catch a bug that called functions return a negative value 
without setting an error.

>               goto retry;
>           }
>       }
> @@ -3176,10 +3186,7 @@ int qemu_loadvm_state(QEMUFile *f, Error **errp)
>   
>       cpu_synchronize_all_pre_loadvm();
>   
> -    ret = qemu_loadvm_state_main(f, mis);
> -    if (ret < 0) {
> -        error_setg(errp, "Load VM state failed: %d", ret);
> -    }
> +    ret = qemu_loadvm_state_main(f, mis, errp);
>       qemu_event_set(&mis->main_thread_load_event);
>   
>       trace_qemu_loadvm_state_post_main(ret);
> @@ -3260,9 +3267,9 @@ int qemu_load_device_state(QEMUFile *f, Error **errp)
>       int ret;
>   
>       /* Load QEMU_VM_SECTION_FULL section */
> -    ret = qemu_loadvm_state_main(f, mis);
> +    ret = qemu_loadvm_state_main(f, mis, errp);
>       if (ret < 0) {
> -        error_setg(errp, "Failed to load device state: %d", ret);
> +        error_prepend(errp, "Failed to load device state: %d: ", ret);
>           return ret;
>       }
>   
> diff --git a/migration/savevm.h b/migration/savevm.h
> index b12681839f0b1afa3255e45215d99c13a224b19f..c337e3e3d111a7f28a57b90f61e8f70b71803d4e 100644
> --- a/migration/savevm.h
> +++ b/migration/savevm.h
> @@ -66,7 +66,8 @@ int qemu_save_device_state(QEMUFile *f);
>   
>   int qemu_loadvm_state(QEMUFile *f, Error **errp);
>   void qemu_loadvm_state_cleanup(MigrationIncomingState *mis);
> -int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis);
> +int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis,
> +                           Error **errp);
>   int qemu_load_device_state(QEMUFile *f, Error **errp);
>   int qemu_loadvm_approve_switchover(void);
>   int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
> 



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

* Re: [PATCH v9 07/27] migration: push Error **errp into qemu_loadvm_state()
  2025-08-05 18:25 ` [PATCH v9 07/27] migration: push Error **errp into qemu_loadvm_state() Arun Menon
@ 2025-08-06  5:17   ` Akihiko Odaki
  2025-08-07  6:07     ` Arun Menon
  2025-08-06  7:24   ` Marc-André Lureau
  1 sibling, 1 reply; 63+ messages in thread
From: Akihiko Odaki @ 2025-08-06  5:17 UTC (permalink / raw)
  To: Arun Menon, qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Dmitry Osipenko,
	Michael S. Tsirkin, Marcel Apfelbaum, Cornelia Huck, Halil Pasic,
	Eric Farman, Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare,
	Marc-André Lureau, qemu-s390x, qemu-ppc, Hailiang Zhang,
	Stefan Berger

On 2025/08/06 3:25, Arun Menon wrote:
> This is an incremental step in converting vmstate loading
> code to report error via Error objects instead of directly
> printing it to console/monitor.
> It is ensured that qemu_loadvm_state() must report an error
> in errp, in case of failure.
> 
> When postcopy live migration runs, the device states are loaded by
> both the qemu coroutine process_incoming_migration_co() and the
> postcopy_ram_listen_thread(). Therefore, it is important that the
> coroutine also reports the error in case of failure, with
> error_report_err(). Otherwise, the source qemu will not display
> any errors before going into the postcopy pause state.
> 
> Signed-off-by: Arun Menon <armenon@redhat.com>
> ---
>   migration/migration.c |  7 ++++---
>   migration/savevm.c    | 31 +++++++++++++++++++------------
>   migration/savevm.h    |  2 +-
>   3 files changed, 24 insertions(+), 16 deletions(-)
> 
> diff --git a/migration/migration.c b/migration/migration.c
> index 10c216d25dec01f206eacad2edd24d21f00e614c..bb7d5e1dee52692cbea1d2c8fdca541e6a75bedb 100644
> --- a/migration/migration.c
> +++ b/migration/migration.c
> @@ -881,7 +881,7 @@ process_incoming_migration_co(void *opaque)
>                         MIGRATION_STATUS_ACTIVE);
>   
>       mis->loadvm_co = qemu_coroutine_self();
> -    ret = qemu_loadvm_state(mis->from_src_file);
> +    ret = qemu_loadvm_state(mis->from_src_file, &local_err);
>       mis->loadvm_co = NULL;
>   
>       trace_vmstate_downtime_checkpoint("dst-precopy-loadvm-completed");
> @@ -908,7 +908,8 @@ process_incoming_migration_co(void *opaque)
>       }
>   
>       if (ret < 0) {
> -        error_setg(&local_err, "load of migration failed: %s", strerror(-ret));
> +        error_prepend(&local_err, "load of migration failed: %s: ",
> +                      strerror(-ret));
>           goto fail;
>       }
>   
> @@ -924,7 +925,7 @@ fail:
>       migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
>                         MIGRATION_STATUS_FAILED);
>       migrate_set_error(s, local_err);
> -    error_free(local_err);
> +    error_report_err(local_err);

There is "error_report_err(s->error)" after this so this can result in 
duplicate error printing.

>   
>       migration_incoming_state_destroy();
>   
> diff --git a/migration/savevm.c b/migration/savevm.c
> index 1bd27efe437d4d911728d776e995490d0a45dcf5..ca166ebd397ad80836ed2f9cb20a92f704fd4ed5 100644
> --- a/migration/savevm.c
> +++ b/migration/savevm.c
> @@ -3149,28 +3149,24 @@ out:
>       return ret;
>   }
>   
> -int qemu_loadvm_state(QEMUFile *f)
> +int qemu_loadvm_state(QEMUFile *f, Error **errp)
>   {
>       MigrationState *s = migrate_get_current();
>       MigrationIncomingState *mis = migration_incoming_get_current();
> -    Error *local_err = NULL;
>       int ret;
>   
> -    if (qemu_savevm_state_blocked(&local_err)) {
> -        error_report_err(local_err);
> +    if (qemu_savevm_state_blocked(errp)) {
>           return -EINVAL;
>       }
>   
>       qemu_loadvm_thread_pool_create(mis);
>   
> -    ret = qemu_loadvm_state_header(f, &local_err);
> +    ret = qemu_loadvm_state_header(f, errp);
>       if (ret) {
> -        error_report_err(local_err);
>           return ret;
>       }
>   
> -    if (qemu_loadvm_state_setup(f, &local_err) != 0) {
> -        error_report_err(local_err);
> +    if (qemu_loadvm_state_setup(f, errp) != 0) {
>           return -EINVAL;
>       }
>   
> @@ -3181,6 +3177,9 @@ int qemu_loadvm_state(QEMUFile *f)
>       cpu_synchronize_all_pre_loadvm();
>   
>       ret = qemu_loadvm_state_main(f, mis);
> +    if (ret < 0) {
> +        error_setg(errp, "Load VM state failed: %d", ret);
> +    }
>       qemu_event_set(&mis->main_thread_load_event);
>   
>       trace_qemu_loadvm_state_post_main(ret);
> @@ -3198,8 +3197,14 @@ int qemu_loadvm_state(QEMUFile *f)
>           if (migrate_has_error(migrate_get_current()) ||
>               !qemu_loadvm_thread_pool_wait(s, mis)) {
>               ret = -EINVAL;
> +            error_setg(errp,
> +                       "Error while loading VM state: "
> +                       "Migration stream has error");
>           } else {
>               ret = qemu_file_get_error(f);
> +            if (ret < 0) {
> +                error_setg(errp, "Error while loading vmstate : %d", ret);
> +            }
>           }
>       }
>       /*
> @@ -3464,6 +3469,7 @@ void qmp_xen_save_devices_state(const char *filename, bool has_live, bool live,
>   
>   void qmp_xen_load_devices_state(const char *filename, Error **errp)
>   {
> +    ERRP_GUARD();
>       QEMUFile *f;
>       QIOChannelFile *ioc;
>       int ret;
> @@ -3485,10 +3491,10 @@ void qmp_xen_load_devices_state(const char *filename, Error **errp)
>       f = qemu_file_new_input(QIO_CHANNEL(ioc));
>       object_unref(OBJECT(ioc));
>   
> -    ret = qemu_loadvm_state(f);
> +    ret = qemu_loadvm_state(f, errp);
>       qemu_fclose(f);
>       if (ret < 0) {
> -        error_setg(errp, "loading Xen device state failed");
> +        error_prepend(errp, "loading Xen device state failed: ");
>       }
>       migration_incoming_state_destroy();
>   }
> @@ -3496,6 +3502,7 @@ void qmp_xen_load_devices_state(const char *filename, Error **errp)
>   bool load_snapshot(const char *name, const char *vmstate,
>                      bool has_devices, strList *devices, Error **errp)
>   {
> +    ERRP_GUARD();
>       BlockDriverState *bs_vm_state;
>       QEMUSnapshotInfo sn;
>       QEMUFile *f;
> @@ -3559,13 +3566,13 @@ bool load_snapshot(const char *name, const char *vmstate,
>           ret = -EINVAL;
>           goto err_drain;
>       }
> -    ret = qemu_loadvm_state(f);
> +    ret = qemu_loadvm_state(f, errp);
>       migration_incoming_state_destroy();
>   
>       bdrv_drain_all_end();
>   
>       if (ret < 0) {
> -        error_setg(errp, "Error %d while loading VM state", ret);
> +        error_prepend(errp, "Error %d while loading VM state: ", ret);
>           return false;
>       }
>   
> diff --git a/migration/savevm.h b/migration/savevm.h
> index 2d5e9c716686f06720325e82fe90c75335ced1de..b80770b7461a60e2ad6ba5e24a7baeae73d90955 100644
> --- a/migration/savevm.h
> +++ b/migration/savevm.h
> @@ -64,7 +64,7 @@ void qemu_savevm_send_colo_enable(QEMUFile *f);
>   void qemu_savevm_live_state(QEMUFile *f);
>   int qemu_save_device_state(QEMUFile *f);
>   
> -int qemu_loadvm_state(QEMUFile *f);
> +int qemu_loadvm_state(QEMUFile *f, Error **errp);
>   void qemu_loadvm_state_cleanup(MigrationIncomingState *mis);
>   int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis);
>   int qemu_load_device_state(QEMUFile *f);
> 



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

* Re: [PATCH v9 24/27] migration: Propagate last encountered error in vmstate_save_state_v() function
  2025-08-05 18:25 ` [PATCH v9 24/27] migration: Propagate last encountered error in " Arun Menon
@ 2025-08-06  5:24   ` Akihiko Odaki
  2025-08-07  6:07     ` Arun Menon
  0 siblings, 1 reply; 63+ messages in thread
From: Akihiko Odaki @ 2025-08-06  5:24 UTC (permalink / raw)
  To: Arun Menon, qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Dmitry Osipenko,
	Michael S. Tsirkin, Marcel Apfelbaum, Cornelia Huck, Halil Pasic,
	Eric Farman, Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare,
	Marc-André Lureau, qemu-s390x, qemu-ppc, Hailiang Zhang,
	Stefan Berger

On 2025/08/06 3:25, Arun Menon wrote:
> We consider,
>    - the saving of the device state (save or subsection save) and
>    - running the cleanup after (post_save)
> as an atomic operation. And that is why, post_save() is called regardless
> of whether there is a preceeding error. This means that, it is possible
> that we have 2 distict errors, one from the preceeding function and the
> other from the post_save() function.
> 
> This commit changes the error handling behavior when two errors occur during
> a save operation.
> 
> 1) Preceding errors were propagated before, but they are now dismissed, if there
>     is a new post_save() error.
>      - A failure during the main save operation, means the system failed to
>        reach a new desired state. A failure during the post-save cleanup,
>        however, is a more critical issue as it can leave the system in an
>        inconsistent or corrupted state. At present, all post_save() calls
>        succeed. However, the introduction of error handling variants of these
>        functions (post_save_errp) in the following commit, imply that we need
>        to handle and propagate these errors. Therefore, we prioritize reporting
>        the more severe error.

This explains why the post_save() error is propagated instead of 
propagating the preceding error. But the preceding errors still do not 
have to be dismissed if we report them with error_report_err() instead.

> 
> 2) post_save() errors were dismissed before, but they are now propagated.
>      - The original design implicitly assumed post-save hooks were infallible
>        cleanup routines.
>        This will not be the case after introducing the post/pre save/load errp
>        variant functions. Dismissing these errors prevents users from
>        identifying the specific operation that failed.

Re-iterating previous comments, if introducing post save errp variant 
functions break the assumption, we just don't have to introduce one. The 
reason to introduce one needs to be explained.

>        The entire save-and-cleanup process is treated as a single
>        logical operation, and all potential failures are communicated.
> 
> Signed-off-by: Arun Menon <armenon@redhat.com>
> ---
>   migration/vmstate.c | 14 ++++++++++++--
>   1 file changed, 12 insertions(+), 2 deletions(-)
> 
> diff --git a/migration/vmstate.c b/migration/vmstate.c
> index fbc59caadbbcc75fe6de27b459aa9aa25e76aa0a..ef78a1e62ad92e9608de72d125da80ea496c8dd1 100644
> --- a/migration/vmstate.c
> +++ b/migration/vmstate.c
> @@ -554,6 +554,12 @@ static int vmstate_save_dispatch(QEMUFile *f,
>                       error_setg(errp, "Save of field %s/%s failed",
>                                   vmsd->name, field->name);
>                       ps_ret = post_save_dispatch(vmsd, opaque, &local_err);
> +                    if (ps_ret) {
> +                        ret = ps_ret;
> +                        error_free_or_abort(errp);
> +                        error_setg(errp, "post-save for %s failed, ret: %d",
> +                                   vmsd->name, ret);
> +                    }
>                       return ret;
>                   }
>   
> @@ -603,10 +609,14 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
>       }
>   
>       ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc, errp);
> +
>       ps_ret = post_save_dispatch(vmsd, opaque, &local_err);
> -    if (!ret && ps_ret) {
> +    if (ps_ret) {
> +        if (ret) {
> +            error_free_or_abort(errp);
> +        }
>           ret = ps_ret;
> -        error_setg(errp, "post-save failed: %s", vmsd->name);
> +        error_propagate(errp, local_err);
>       }
>   
>       return ret;
> 



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

* Re: [PATCH v9 26/27] migration: Add error-parameterized function variants in VMSD struct
  2025-08-05 18:25 ` [PATCH v9 26/27] migration: Add error-parameterized function variants in VMSD struct Arun Menon
@ 2025-08-06  5:45   ` Akihiko Odaki
  2025-08-06  7:27     ` Arun Menon
  0 siblings, 1 reply; 63+ messages in thread
From: Akihiko Odaki @ 2025-08-06  5:45 UTC (permalink / raw)
  To: Arun Menon, qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Dmitry Osipenko,
	Michael S. Tsirkin, Marcel Apfelbaum, Cornelia Huck, Halil Pasic,
	Eric Farman, Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare,
	Marc-André Lureau, qemu-s390x, qemu-ppc, Hailiang Zhang,
	Stefan Berger

On 2025/08/06 3:25, Arun Menon wrote:
> - We need to have good error reporting in the callbacks in
>    VMStateDescription struct. Specifically pre_save, post_save,
>    pre_load and post_load callbacks.
> - It is not possible to change these functions everywhere in one
>    patch, therefore, we introduce a duplicate set of callbacks
>    with Error object passed to them.
> - So, in this commit, we implement 'errp' variants of these callbacks,
>    introducing an explicit Error object parameter.
> - This is a functional step towards transitioning the entire codebase
>    to the new error-parameterized functions.
> - Deliberately called in mutual exclusion from their counterparts,
>    to prevent conflicts during the transition.
> - New impls should preferentally use 'errp' variants of
>    these methods, and existing impls incrementally converted.
>    The variants without 'errp' are intended to be removed
>    once all usage is converted.
> 
> Signed-off-by: Arun Menon <armenon@redhat.com>
> ---
>   docs/devel/migration/main.rst | 24 ++++++++++++++++++++++++
>   include/migration/vmstate.h   | 15 +++++++++++++++
>   migration/vmstate.c           | 34 ++++++++++++++++++++++++++++++----
>   3 files changed, 69 insertions(+), 4 deletions(-)
> 
> diff --git a/docs/devel/migration/main.rst b/docs/devel/migration/main.rst
> index 6493c1d2bca48a2fa34d92f6c0979c215c56b8d5..35bf5ae26c87f3c82964eb15618be373ab5a41fc 100644
> --- a/docs/devel/migration/main.rst
> +++ b/docs/devel/migration/main.rst
> @@ -444,6 +444,30 @@ The functions to do that are inside a vmstate definition, and are called:
>     This function is called after we save the state of one device
>     (even upon failure, unless the call to pre_save returned an error).
>   
> +Following are the errp variants of these functions.
> +
> +- ``int (*pre_load_errp)(void *opaque, Error **errp);``
> +
> +  This function is called before we load the state of one device.
> +
> +- ``int (*post_load_errp)(void *opaque, int version_id, Error **errp);``
> +
> +  This function is called after we load the state of one device.
> +
> +- ``int (*pre_save_errp)(void *opaque, Error **errp);``
> +
> +  This function is called before we save the state of one device.
> +
> +- ``int (*post_save_errp)(void *opaque, Error **errp);``
> +
> +  This function is called after we save the state of one device
> +  (even upon failure, unless the call to pre_save returned an error).

Reviewing "[PATCH v9 24/27] migration: Propagate last encountered error 
in vmstate_save_state_v() function", I wondered why it has never been a 
problem post_load(), and this says only post_save_errp() is called upon 
failure.

Now I suspect it may be better to clarify their differences and avoid 
introducing post_save_errp() instead.

My guess is that post_save_errp() is being introduced for consistency 
with post_load(), but they cannot have "consistency" if post_load() and 
post_save() are not corresponding functions of save/load but are 
independent two functions. Perhaps the true problem here is that 
post_load() and post_save() look similar; if so, that can be solved by:
- Renaming post_save() to e.g., cleanup_save()
- Changing it to return void

> +
> +New impls should preferentally use 'errp' variants of these
> +methods and existing impls incrementally converted.
> +The variants without 'errp' are intended to be removed
> +once all usage is converted.
> +
>   Example: You can look at hpet.c, that uses the first three functions
>   to massage the state that is transferred.
>   
> diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
> index 5fe9bbf39058d0cf97c1adab54cc516dbe8dc32a..51baf6c7f9d061ee33949d7e798f2184e50b4cbf 100644
> --- a/include/migration/vmstate.h
> +++ b/include/migration/vmstate.h
> @@ -200,15 +200,30 @@ struct VMStateDescription {
>        * exclusive. For this reason, also early_setup VMSDs are migrated in a
>        * QEMU_VM_SECTION_FULL section, while save_setup() data is migrated in
>        * a QEMU_VM_SECTION_START section.
> +     *
> +     * There are duplicate impls of the post/pre save/load hooks.
> +     * New impls should preferentally use 'errp' variants of these
> +     * methods and existing impls incrementally converted.
> +     * The variants without 'errp' are intended to be removed
> +     * once all usage is converted.
> +     *
> +     * For the errp variants,
> +     * Returns: 0 on success,
> +     *          <0 on error where -value is an error number from errno.h
>        */
> +
>       bool early_setup;
>       int version_id;
>       int minimum_version_id;
>       MigrationPriority priority;
>       int (*pre_load)(void *opaque);
> +    int (*pre_load_errp)(void *opaque, Error **errp);
>       int (*post_load)(void *opaque, int version_id);
> +    int (*post_load_errp)(void *opaque, int version_id, Error **errp);
>       int (*pre_save)(void *opaque);
> +    int (*pre_save_errp)(void *opaque, Error **errp);
>       int (*post_save)(void *opaque);
> +    int (*post_save_errp)(void *opaque, Error **errp);
>       bool (*needed)(void *opaque);
>       bool (*dev_unplug_pending)(void *opaque);
>   
> diff --git a/migration/vmstate.c b/migration/vmstate.c
> index 569e66ea983f833e6a0d651d2a751f34a64e8f5c..0acaa855cfec8ddac63e33d4562e39c345856213 100644
> --- a/migration/vmstate.c
> +++ b/migration/vmstate.c
> @@ -152,7 +152,16 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
>           trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL);
>           return -EINVAL;
>       }
> -    if (vmsd->pre_load) {
> +    if (vmsd->pre_load_errp) {
> +        ret = vmsd->pre_load_errp(opaque, errp);
> +        if (ret) {
> +            error_prepend(errp, "VM pre load failed for: '%s', "
> +                          "version_id: %d, minimum version_id: %d, "
> +                          "ret: %d: ", vmsd->name, vmsd->version_id,
> +                          vmsd->minimum_version_id, ret);
> +            return ret;
> +        }
> +    } else if (vmsd->pre_load) {
>           ret = vmsd->pre_load(opaque);
>           if (ret) {
>               error_setg(errp, "VM pre load failed for: '%s', "
> @@ -242,7 +251,14 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
>           qemu_file_set_error(f, ret);
>           return ret;
>       }
> -    if (vmsd->post_load) {
> +    if (vmsd->post_load_errp) {
> +        ret = vmsd->post_load_errp(opaque, version_id, errp);
> +        if (ret < 0) {
> +            error_prepend(errp, "VM Post load failed for: %s, version_id: %d, "
> +                          "minimum_version: %d, ret: %d: ", vmsd->name,
> +                          vmsd->version_id, vmsd->minimum_version_id, ret);
> +        }
> +    } else if (vmsd->post_load) {
>           ret = vmsd->post_load(opaque, version_id);
>           if (ret < 0) {
>               error_setg(errp,
> @@ -411,8 +427,16 @@ int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
>   static int pre_save_dispatch(const VMStateDescription *vmsd, void *opaque,
>                                Error **errp)
>   {
> +    ERRP_GUARD();
>       int ret = 0;
> -    if (vmsd->pre_save) {
> +    if (vmsd->pre_save_errp) {
> +        ret = vmsd->pre_save_errp(opaque, errp);
> +        trace_vmstate_save_state_pre_save_res(vmsd->name, ret);
> +        if (ret) {
> +            error_prepend(errp, "pre-save for %s failed, ret: %d: ",
> +                          vmsd->name, ret);
> +        }
> +    } else if (vmsd->pre_save) {
>           ret = vmsd->pre_save(opaque);
>           trace_vmstate_save_state_pre_save_res(vmsd->name, ret);
>           if (ret) {
> @@ -427,7 +451,9 @@ static int post_save_dispatch(const VMStateDescription *vmsd, void *opaque,
>                                 Error **errp)
>   {
>       int ret = 0;
> -    if (vmsd->post_save) {
> +    if (vmsd->post_save_errp) {
> +        ret = vmsd->post_save_errp(opaque, errp);
> +    } else if (vmsd->post_save) {
>           ret = vmsd->post_save(opaque);
>           error_setg(errp, "post-save for %s failed, ret: %d",
>                      vmsd->name, ret);
> 



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

* Re: [PATCH v9 07/27] migration: push Error **errp into qemu_loadvm_state()
  2025-08-05 18:25 ` [PATCH v9 07/27] migration: push Error **errp into qemu_loadvm_state() Arun Menon
  2025-08-06  5:17   ` Akihiko Odaki
@ 2025-08-06  7:24   ` Marc-André Lureau
  1 sibling, 0 replies; 63+ messages in thread
From: Marc-André Lureau @ 2025-08-06  7:24 UTC (permalink / raw)
  To: Arun Menon
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

[-- Attachment #1: Type: text/plain, Size: 6774 bytes --]

On Tue, Aug 5, 2025 at 10:28 PM Arun Menon <armenon@redhat.com> wrote:

> This is an incremental step in converting vmstate loading
> code to report error via Error objects instead of directly
> printing it to console/monitor.
> It is ensured that qemu_loadvm_state() must report an error
> in errp, in case of failure.
>
> When postcopy live migration runs, the device states are loaded by
> both the qemu coroutine process_incoming_migration_co() and the
> postcopy_ram_listen_thread(). Therefore, it is important that the
> coroutine also reports the error in case of failure, with
> error_report_err(). Otherwise, the source qemu will not display
> any errors before going into the postcopy pause state.
>
> Signed-off-by: Arun Menon <armenon@redhat.com>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> ---
>  migration/migration.c |  7 ++++---
>  migration/savevm.c    | 31 +++++++++++++++++++------------
>  migration/savevm.h    |  2 +-
>  3 files changed, 24 insertions(+), 16 deletions(-)
>
> diff --git a/migration/migration.c b/migration/migration.c
> index
> 10c216d25dec01f206eacad2edd24d21f00e614c..bb7d5e1dee52692cbea1d2c8fdca541e6a75bedb
> 100644
> --- a/migration/migration.c
> +++ b/migration/migration.c
> @@ -881,7 +881,7 @@ process_incoming_migration_co(void *opaque)
>                        MIGRATION_STATUS_ACTIVE);
>
>      mis->loadvm_co = qemu_coroutine_self();
> -    ret = qemu_loadvm_state(mis->from_src_file);
> +    ret = qemu_loadvm_state(mis->from_src_file, &local_err);
>      mis->loadvm_co = NULL;
>
>      trace_vmstate_downtime_checkpoint("dst-precopy-loadvm-completed");
> @@ -908,7 +908,8 @@ process_incoming_migration_co(void *opaque)
>      }
>
>      if (ret < 0) {
> -        error_setg(&local_err, "load of migration failed: %s",
> strerror(-ret));
> +        error_prepend(&local_err, "load of migration failed: %s: ",
> +                      strerror(-ret));
>          goto fail;
>      }
>
> @@ -924,7 +925,7 @@ fail:
>      migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
>                        MIGRATION_STATUS_FAILED);
>      migrate_set_error(s, local_err);
> -    error_free(local_err);
> +    error_report_err(local_err);
>
>      migration_incoming_state_destroy();
>
> diff --git a/migration/savevm.c b/migration/savevm.c
> index
> 1bd27efe437d4d911728d776e995490d0a45dcf5..ca166ebd397ad80836ed2f9cb20a92f704fd4ed5
> 100644
> --- a/migration/savevm.c
> +++ b/migration/savevm.c
> @@ -3149,28 +3149,24 @@ out:
>      return ret;
>  }
>
> -int qemu_loadvm_state(QEMUFile *f)
> +int qemu_loadvm_state(QEMUFile *f, Error **errp)
>  {
>      MigrationState *s = migrate_get_current();
>      MigrationIncomingState *mis = migration_incoming_get_current();
> -    Error *local_err = NULL;
>      int ret;
>
> -    if (qemu_savevm_state_blocked(&local_err)) {
> -        error_report_err(local_err);
> +    if (qemu_savevm_state_blocked(errp)) {
>          return -EINVAL;
>      }
>
>      qemu_loadvm_thread_pool_create(mis);
>
> -    ret = qemu_loadvm_state_header(f, &local_err);
> +    ret = qemu_loadvm_state_header(f, errp);
>      if (ret) {
> -        error_report_err(local_err);
>          return ret;
>      }
>
> -    if (qemu_loadvm_state_setup(f, &local_err) != 0) {
> -        error_report_err(local_err);
> +    if (qemu_loadvm_state_setup(f, errp) != 0) {
>          return -EINVAL;
>      }
>
> @@ -3181,6 +3177,9 @@ int qemu_loadvm_state(QEMUFile *f)
>      cpu_synchronize_all_pre_loadvm();
>
>      ret = qemu_loadvm_state_main(f, mis);
> +    if (ret < 0) {
> +        error_setg(errp, "Load VM state failed: %d", ret);
> +    }
>      qemu_event_set(&mis->main_thread_load_event);
>
>      trace_qemu_loadvm_state_post_main(ret);
> @@ -3198,8 +3197,14 @@ int qemu_loadvm_state(QEMUFile *f)
>          if (migrate_has_error(migrate_get_current()) ||
>              !qemu_loadvm_thread_pool_wait(s, mis)) {
>              ret = -EINVAL;
> +            error_setg(errp,
> +                       "Error while loading VM state: "
> +                       "Migration stream has error");
>          } else {
>              ret = qemu_file_get_error(f);
> +            if (ret < 0) {
> +                error_setg(errp, "Error while loading vmstate : %d", ret);
> +            }
>          }
>      }
>      /*
> @@ -3464,6 +3469,7 @@ void qmp_xen_save_devices_state(const char
> *filename, bool has_live, bool live,
>
>  void qmp_xen_load_devices_state(const char *filename, Error **errp)
>  {
> +    ERRP_GUARD();
>      QEMUFile *f;
>      QIOChannelFile *ioc;
>      int ret;
> @@ -3485,10 +3491,10 @@ void qmp_xen_load_devices_state(const char
> *filename, Error **errp)
>      f = qemu_file_new_input(QIO_CHANNEL(ioc));
>      object_unref(OBJECT(ioc));
>
> -    ret = qemu_loadvm_state(f);
> +    ret = qemu_loadvm_state(f, errp);
>      qemu_fclose(f);
>      if (ret < 0) {
> -        error_setg(errp, "loading Xen device state failed");
> +        error_prepend(errp, "loading Xen device state failed: ");
>      }
>      migration_incoming_state_destroy();
>  }
> @@ -3496,6 +3502,7 @@ void qmp_xen_load_devices_state(const char
> *filename, Error **errp)
>  bool load_snapshot(const char *name, const char *vmstate,
>                     bool has_devices, strList *devices, Error **errp)
>  {
> +    ERRP_GUARD();
>      BlockDriverState *bs_vm_state;
>      QEMUSnapshotInfo sn;
>      QEMUFile *f;
> @@ -3559,13 +3566,13 @@ bool load_snapshot(const char *name, const char
> *vmstate,
>          ret = -EINVAL;
>          goto err_drain;
>      }
> -    ret = qemu_loadvm_state(f);
> +    ret = qemu_loadvm_state(f, errp);
>      migration_incoming_state_destroy();
>
>      bdrv_drain_all_end();
>
>      if (ret < 0) {
> -        error_setg(errp, "Error %d while loading VM state", ret);
> +        error_prepend(errp, "Error %d while loading VM state: ", ret);
>          return false;
>      }
>
> diff --git a/migration/savevm.h b/migration/savevm.h
> index
> 2d5e9c716686f06720325e82fe90c75335ced1de..b80770b7461a60e2ad6ba5e24a7baeae73d90955
> 100644
> --- a/migration/savevm.h
> +++ b/migration/savevm.h
> @@ -64,7 +64,7 @@ void qemu_savevm_send_colo_enable(QEMUFile *f);
>  void qemu_savevm_live_state(QEMUFile *f);
>  int qemu_save_device_state(QEMUFile *f);
>
> -int qemu_loadvm_state(QEMUFile *f);
> +int qemu_loadvm_state(QEMUFile *f, Error **errp);
>  void qemu_loadvm_state_cleanup(MigrationIncomingState *mis);
>  int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis);
>  int qemu_load_device_state(QEMUFile *f);
>
> --
> 2.50.1
>
>

[-- Attachment #2: Type: text/html, Size: 8284 bytes --]

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

* Re: [PATCH v9 08/27] migration: push Error **errp into qemu_load_device_state()
  2025-08-05 18:25 ` [PATCH v9 08/27] migration: push Error **errp into qemu_load_device_state() Arun Menon
@ 2025-08-06  7:27   ` Marc-André Lureau
  0 siblings, 0 replies; 63+ messages in thread
From: Marc-André Lureau @ 2025-08-06  7:27 UTC (permalink / raw)
  To: Arun Menon
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

[-- Attachment #1: Type: text/plain, Size: 2920 bytes --]

On Tue, Aug 5, 2025 at 10:29 PM Arun Menon <armenon@redhat.com> wrote:

> This is an incremental step in converting vmstate loading
> code to report error via Error objects instead of directly
> printing it to console/monitor.
> It is ensured that qemu_load_device_state() must report an error
> in errp, in case of failure.
>
> Signed-off-by: Arun Menon <armenon@redhat.com>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> ---
>  migration/colo.c   | 4 ++--
>  migration/savevm.c | 5 +++--
>  migration/savevm.h | 2 +-
>  3 files changed, 6 insertions(+), 5 deletions(-)
>
> diff --git a/migration/colo.c b/migration/colo.c
> index
> e0f713c837f5da25d67afbd02ceb6c54024ca3af..0ba22ee76a13e313793f653f43a728e3c433bbc1
> 100644
> --- a/migration/colo.c
> +++ b/migration/colo.c
> @@ -729,9 +729,9 @@ static void
> colo_incoming_process_checkpoint(MigrationIncomingState *mis,
>      bql_lock();
>      vmstate_loading = true;
>      colo_flush_ram_cache();
> -    ret = qemu_load_device_state(fb);
> +    ret = qemu_load_device_state(fb, errp);
>      if (ret < 0) {
> -        error_setg(errp, "COLO: load device state failed");
> +        error_prepend(errp, "COLO: load device state failed: ");
>          vmstate_loading = false;
>          bql_unlock();
>          return;
> diff --git a/migration/savevm.c b/migration/savevm.c
> index
> ca166ebd397ad80836ed2f9cb20a92f704fd4ed5..f3b91c8ae0eee6078406081f0bd7f686fed28601
> 100644
> --- a/migration/savevm.c
> +++ b/migration/savevm.c
> @@ -3253,15 +3253,16 @@ int qemu_loadvm_state(QEMUFile *f, Error **errp)
>      return ret;
>  }
>
> -int qemu_load_device_state(QEMUFile *f)
> +int qemu_load_device_state(QEMUFile *f, Error **errp)
>  {
> +    ERRP_GUARD();
>      MigrationIncomingState *mis = migration_incoming_get_current();
>      int ret;
>
>      /* Load QEMU_VM_SECTION_FULL section */
>      ret = qemu_loadvm_state_main(f, mis);
>      if (ret < 0) {
> -        error_report("Failed to load device state: %d", ret);
> +        error_setg(errp, "Failed to load device state: %d", ret);
>          return ret;
>      }
>
> diff --git a/migration/savevm.h b/migration/savevm.h
> index
> b80770b7461a60e2ad6ba5e24a7baeae73d90955..b12681839f0b1afa3255e45215d99c13a224b19f
> 100644
> --- a/migration/savevm.h
> +++ b/migration/savevm.h
> @@ -67,7 +67,7 @@ int qemu_save_device_state(QEMUFile *f);
>  int qemu_loadvm_state(QEMUFile *f, Error **errp);
>  void qemu_loadvm_state_cleanup(MigrationIncomingState *mis);
>  int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis);
> -int qemu_load_device_state(QEMUFile *f);
> +int qemu_load_device_state(QEMUFile *f, Error **errp);
>  int qemu_loadvm_approve_switchover(void);
>  int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
>          bool in_postcopy);
>
> --
> 2.50.1
>
>

[-- Attachment #2: Type: text/html, Size: 3785 bytes --]

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

* Re: [PATCH v9 26/27] migration: Add error-parameterized function variants in VMSD struct
  2025-08-06  5:45   ` Akihiko Odaki
@ 2025-08-06  7:27     ` Arun Menon
  0 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-06  7:27 UTC (permalink / raw)
  To: Akihiko Odaki
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger

Hi Akihiko,
Thanks for the review.

On Wed, Aug 06, 2025 at 02:45:01PM +0900, Akihiko Odaki wrote:
> On 2025/08/06 3:25, Arun Menon wrote:
> > - We need to have good error reporting in the callbacks in
> >    VMStateDescription struct. Specifically pre_save, post_save,
> >    pre_load and post_load callbacks.
> > - It is not possible to change these functions everywhere in one
> >    patch, therefore, we introduce a duplicate set of callbacks
> >    with Error object passed to them.
> > - So, in this commit, we implement 'errp' variants of these callbacks,
> >    introducing an explicit Error object parameter.
> > - This is a functional step towards transitioning the entire codebase
> >    to the new error-parameterized functions.
> > - Deliberately called in mutual exclusion from their counterparts,
> >    to prevent conflicts during the transition.
> > - New impls should preferentally use 'errp' variants of
> >    these methods, and existing impls incrementally converted.
> >    The variants without 'errp' are intended to be removed
> >    once all usage is converted.
> > 
> > Signed-off-by: Arun Menon <armenon@redhat.com>
> > ---
> >   docs/devel/migration/main.rst | 24 ++++++++++++++++++++++++
> >   include/migration/vmstate.h   | 15 +++++++++++++++
> >   migration/vmstate.c           | 34 ++++++++++++++++++++++++++++++----
> >   3 files changed, 69 insertions(+), 4 deletions(-)
> > 
> > diff --git a/docs/devel/migration/main.rst b/docs/devel/migration/main.rst
> > index 6493c1d2bca48a2fa34d92f6c0979c215c56b8d5..35bf5ae26c87f3c82964eb15618be373ab5a41fc 100644
> > --- a/docs/devel/migration/main.rst
> > +++ b/docs/devel/migration/main.rst
> > @@ -444,6 +444,30 @@ The functions to do that are inside a vmstate definition, and are called:
> >     This function is called after we save the state of one device
> >     (even upon failure, unless the call to pre_save returned an error).
> > +Following are the errp variants of these functions.
> > +
> > +- ``int (*pre_load_errp)(void *opaque, Error **errp);``
> > +
> > +  This function is called before we load the state of one device.
> > +
> > +- ``int (*post_load_errp)(void *opaque, int version_id, Error **errp);``
> > +
> > +  This function is called after we load the state of one device.
> > +
> > +- ``int (*pre_save_errp)(void *opaque, Error **errp);``
> > +
> > +  This function is called before we save the state of one device.
> > +
> > +- ``int (*post_save_errp)(void *opaque, Error **errp);``
> > +
> > +  This function is called after we save the state of one device
> > +  (even upon failure, unless the call to pre_save returned an error).
> 
> Reviewing "[PATCH v9 24/27] migration: Propagate last encountered error in
> vmstate_save_state_v() function", I wondered why it has never been a problem
> post_load(), and this says only post_save_errp() is called upon failure.
> 
> Now I suspect it may be better to clarify their differences and avoid
> introducing post_save_errp() instead.
> 
> My guess is that post_save_errp() is being introduced for consistency with
> post_load(), but they cannot have "consistency" if post_load() and
> post_save() are not corresponding functions of save/load but are independent
> two functions. Perhaps the true problem here is that post_load() and
> post_save() look similar; if so, that can be solved by:
> - Renaming post_save() to e.g., cleanup_save()
> - Changing it to return void
> 
That is insightful. I agree. It was introduced because of the nomenclature. 
From what I have seen, the functionality indeed differs, in the sense that,
currently post_load does some sanity checks after loading the device state,
and therefore it can fail and return an error; whereas post_save is not known
to fail until now.

It is therefore possible to implement your suggestion : renaming it to
cleanup_save and returning void. This will avoid us in having the extra
patch 24/27.
While I have deviated a bit from TPM backend to make these changes in the
migration module, I believe they are important and hope the maintainers
will agree.

> > +
> > +New impls should preferentally use 'errp' variants of these
> > +methods and existing impls incrementally converted.
> > +The variants without 'errp' are intended to be removed
> > +once all usage is converted.
> > +
> >   Example: You can look at hpet.c, that uses the first three functions
> >   to massage the state that is transferred.
> > diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
> > index 5fe9bbf39058d0cf97c1adab54cc516dbe8dc32a..51baf6c7f9d061ee33949d7e798f2184e50b4cbf 100644
> > --- a/include/migration/vmstate.h
> > +++ b/include/migration/vmstate.h
> > @@ -200,15 +200,30 @@ struct VMStateDescription {
> >        * exclusive. For this reason, also early_setup VMSDs are migrated in a
> >        * QEMU_VM_SECTION_FULL section, while save_setup() data is migrated in
> >        * a QEMU_VM_SECTION_START section.
> > +     *
> > +     * There are duplicate impls of the post/pre save/load hooks.
> > +     * New impls should preferentally use 'errp' variants of these
> > +     * methods and existing impls incrementally converted.
> > +     * The variants without 'errp' are intended to be removed
> > +     * once all usage is converted.
> > +     *
> > +     * For the errp variants,
> > +     * Returns: 0 on success,
> > +     *          <0 on error where -value is an error number from errno.h
> >        */
> > +
> >       bool early_setup;
> >       int version_id;
> >       int minimum_version_id;
> >       MigrationPriority priority;
> >       int (*pre_load)(void *opaque);
> > +    int (*pre_load_errp)(void *opaque, Error **errp);
> >       int (*post_load)(void *opaque, int version_id);
> > +    int (*post_load_errp)(void *opaque, int version_id, Error **errp);
> >       int (*pre_save)(void *opaque);
> > +    int (*pre_save_errp)(void *opaque, Error **errp);
> >       int (*post_save)(void *opaque);
> > +    int (*post_save_errp)(void *opaque, Error **errp);
> >       bool (*needed)(void *opaque);
> >       bool (*dev_unplug_pending)(void *opaque);
> > diff --git a/migration/vmstate.c b/migration/vmstate.c
> > index 569e66ea983f833e6a0d651d2a751f34a64e8f5c..0acaa855cfec8ddac63e33d4562e39c345856213 100644
> > --- a/migration/vmstate.c
> > +++ b/migration/vmstate.c
> > @@ -152,7 +152,16 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
> >           trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL);
> >           return -EINVAL;
> >       }
> > -    if (vmsd->pre_load) {
> > +    if (vmsd->pre_load_errp) {
> > +        ret = vmsd->pre_load_errp(opaque, errp);
> > +        if (ret) {
> > +            error_prepend(errp, "VM pre load failed for: '%s', "
> > +                          "version_id: %d, minimum version_id: %d, "
> > +                          "ret: %d: ", vmsd->name, vmsd->version_id,
> > +                          vmsd->minimum_version_id, ret);
> > +            return ret;
> > +        }
> > +    } else if (vmsd->pre_load) {
> >           ret = vmsd->pre_load(opaque);
> >           if (ret) {
> >               error_setg(errp, "VM pre load failed for: '%s', "
> > @@ -242,7 +251,14 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
> >           qemu_file_set_error(f, ret);
> >           return ret;
> >       }
> > -    if (vmsd->post_load) {
> > +    if (vmsd->post_load_errp) {
> > +        ret = vmsd->post_load_errp(opaque, version_id, errp);
> > +        if (ret < 0) {
> > +            error_prepend(errp, "VM Post load failed for: %s, version_id: %d, "
> > +                          "minimum_version: %d, ret: %d: ", vmsd->name,
> > +                          vmsd->version_id, vmsd->minimum_version_id, ret);
> > +        }
> > +    } else if (vmsd->post_load) {
> >           ret = vmsd->post_load(opaque, version_id);
> >           if (ret < 0) {
> >               error_setg(errp,
> > @@ -411,8 +427,16 @@ int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
> >   static int pre_save_dispatch(const VMStateDescription *vmsd, void *opaque,
> >                                Error **errp)
> >   {
> > +    ERRP_GUARD();
> >       int ret = 0;
> > -    if (vmsd->pre_save) {
> > +    if (vmsd->pre_save_errp) {
> > +        ret = vmsd->pre_save_errp(opaque, errp);
> > +        trace_vmstate_save_state_pre_save_res(vmsd->name, ret);
> > +        if (ret) {
> > +            error_prepend(errp, "pre-save for %s failed, ret: %d: ",
> > +                          vmsd->name, ret);
> > +        }
> > +    } else if (vmsd->pre_save) {
> >           ret = vmsd->pre_save(opaque);
> >           trace_vmstate_save_state_pre_save_res(vmsd->name, ret);
> >           if (ret) {
> > @@ -427,7 +451,9 @@ static int post_save_dispatch(const VMStateDescription *vmsd, void *opaque,
> >                                 Error **errp)
> >   {
> >       int ret = 0;
> > -    if (vmsd->post_save) {
> > +    if (vmsd->post_save_errp) {
> > +        ret = vmsd->post_save_errp(opaque, errp);
> > +    } else if (vmsd->post_save) {
> >           ret = vmsd->post_save(opaque);
> >           error_setg(errp, "post-save for %s failed, ret: %d",
> >                      vmsd->name, ret);
> > 
> 
Regards,
Arun



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

* Re: [PATCH v9 09/27] migration: push Error **errp into qemu_loadvm_state_main()
  2025-08-05 18:25 ` [PATCH v9 09/27] migration: push Error **errp into qemu_loadvm_state_main() Arun Menon
  2025-08-06  5:14   ` Akihiko Odaki
@ 2025-08-06  7:34   ` Marc-André Lureau
  2025-08-06  7:43     ` Arun Menon
  1 sibling, 1 reply; 63+ messages in thread
From: Marc-André Lureau @ 2025-08-06  7:34 UTC (permalink / raw)
  To: Arun Menon
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger, Daniel P. Berrangé

[-- Attachment #1: Type: text/plain, Size: 7304 bytes --]

Hi

On Tue, Aug 5, 2025 at 10:29 PM Arun Menon <armenon@redhat.com> wrote:

> This is an incremental step in converting vmstate loading
> code to report error via Error objects instead of directly
> printing it to console/monitor.
> It is ensured that qemu_loadvm_state_main() must report an error
> in errp, in case of failure.
> loadvm_process_command also sets the errp object explicitly.
>
> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
> Signed-off-by: Arun Menon <armenon@redhat.com>
> ---
>  migration/colo.c   |  5 +++--
>  migration/savevm.c | 33 ++++++++++++++++++++-------------
>  migration/savevm.h |  3 ++-
>  3 files changed, 25 insertions(+), 16 deletions(-)
>
> diff --git a/migration/colo.c b/migration/colo.c
> index
> 0ba22ee76a13e313793f653f43a728e3c433bbc1..a96e4dba15516b71d1b315c736c3b4879ff04e58
> 100644
> --- a/migration/colo.c
> +++ b/migration/colo.c
> @@ -659,6 +659,7 @@ void migrate_start_colo_process(MigrationState *s)
>  static void colo_incoming_process_checkpoint(MigrationIncomingState *mis,
>                        QEMUFile *fb, QIOChannelBuffer *bioc, Error **errp)
>  {
> +    ERRP_GUARD();
>      uint64_t total_size;
>      uint64_t value;
>      Error *local_err = NULL;
> @@ -686,11 +687,11 @@ static void
> colo_incoming_process_checkpoint(MigrationIncomingState *mis,
>
>      bql_lock();
>      cpu_synchronize_all_states();
> -    ret = qemu_loadvm_state_main(mis->from_src_file, mis);
> +    ret = qemu_loadvm_state_main(mis->from_src_file, mis, errp);
>      bql_unlock();
>
>      if (ret < 0) {
> -        error_setg(errp, "Load VM's live state (ram) error");
> +        error_prepend(errp, "Load VM's live state (ram) error: ");
>          return;
>      }
>
> diff --git a/migration/savevm.c b/migration/savevm.c
> index
> f3b91c8ae0eee6078406081f0bd7f686fed28601..ad3dd9b172afc541f104d2187a79bafa8e380466
> 100644
> --- a/migration/savevm.c
> +++ b/migration/savevm.c
> @@ -2105,7 +2105,7 @@ static void *postcopy_ram_listen_thread(void *opaque)
>      qemu_file_set_blocking(f, true);
>
>      /* TODO: sanity check that only postcopiable data will be loaded here
> */
> -    load_res = qemu_loadvm_state_main(f, mis);
> +    load_res = qemu_loadvm_state_main(f, mis, NULL);
>

What's the rationale to make the function silent or not gather the error?
It seems error reporting could be improved below this. Maybe pass
&error_warn here, and add a second TODO to improve error handling?


>
>      /*
>       * This is tricky, but, mis->from_src_file can change after it
> @@ -2407,6 +2407,7 @@ static int
> loadvm_postcopy_handle_resume(MigrationIncomingState *mis)
>   */
>  static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis, Error
> **errp)
>  {
> +    ERRP_GUARD();
>      int ret;
>      size_t length;
>      QIOChannelBuffer *bioc;
> @@ -2456,9 +2457,9 @@ static int
> loadvm_handle_cmd_packaged(MigrationIncomingState *mis, Error **errp)
>          qemu_coroutine_yield();
>      } while (1);
>
> -    ret = qemu_loadvm_state_main(packf, mis);
> +    ret = qemu_loadvm_state_main(packf, mis, errp);
>      if (ret < 0) {
> -        error_setg(errp, "VM state load failed: %d", ret);
> +        error_prepend(errp, "Loading VM state failed: %d: ", ret);
>      }
>      trace_loadvm_handle_cmd_packaged_main(ret);
>      qemu_fclose(packf);
> @@ -3074,8 +3075,10 @@ static bool
> postcopy_pause_incoming(MigrationIncomingState *mis)
>      return true;
>  }
>
> -int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis)
> +int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis,
> +                           Error **errp)
>  {
> +    ERRP_GUARD();
>      uint8_t section_type;
>      int ret = 0;
>
> @@ -3083,8 +3086,10 @@ retry:
>      while (true) {
>          section_type = qemu_get_byte(f);
>
> -        ret = qemu_file_get_error_obj_any(f, mis->postcopy_qemufile_dst,
> NULL);
> +        ret = qemu_file_get_error_obj_any(f, mis->postcopy_qemufile_dst,
> errp);
>          if (ret) {
> +            error_prepend(errp, "Failed to load device state section ID:
> %d: ",
> +                          ret);
>              break;
>          }
>
> @@ -3105,7 +3110,7 @@ retry:
>              }
>              break;
>          case QEMU_VM_COMMAND:
> -            ret = loadvm_process_command(f, NULL);
> +            ret = loadvm_process_command(f, errp);
>              trace_qemu_loadvm_state_section_command(ret);
>              if ((ret < 0) || (ret == LOADVM_QUIT)) {
>                  goto out;
> @@ -3115,7 +3120,7 @@ retry:
>              /* This is the end of migration */
>              goto out;
>          default:
> -            error_report("Unknown savevm section type %d", section_type);
> +            error_setg(errp, "Unknown savevm section type %d",
> section_type);
>              ret = -EINVAL;
>              goto out;
>          }
> @@ -3123,6 +3128,9 @@ retry:
>
>  out:
>      if (ret < 0) {
> +        if (*errp == NULL) {
> +            error_setg(errp, "Loading VM state failed: %d", ret);
> +        }
>          qemu_file_set_error(f, ret);
>
>          /* Cancel bitmaps incoming regardless of recovery */
> @@ -3143,6 +3151,8 @@ out:
>              migrate_postcopy_ram() && postcopy_pause_incoming(mis)) {
>              /* Reset f to point to the newly created channel */
>              f = mis->from_src_file;
> +            error_free(*errp);
> +            *errp = NULL;
>              goto retry;
>          }
>      }
> @@ -3176,10 +3186,7 @@ int qemu_loadvm_state(QEMUFile *f, Error **errp)
>
>      cpu_synchronize_all_pre_loadvm();
>
> -    ret = qemu_loadvm_state_main(f, mis);
> -    if (ret < 0) {
> -        error_setg(errp, "Load VM state failed: %d", ret);
> -    }
> +    ret = qemu_loadvm_state_main(f, mis, errp);
>      qemu_event_set(&mis->main_thread_load_event);
>
>      trace_qemu_loadvm_state_post_main(ret);
> @@ -3260,9 +3267,9 @@ int qemu_load_device_state(QEMUFile *f, Error **errp)
>      int ret;
>
>      /* Load QEMU_VM_SECTION_FULL section */
> -    ret = qemu_loadvm_state_main(f, mis);
> +    ret = qemu_loadvm_state_main(f, mis, errp);
>      if (ret < 0) {
> -        error_setg(errp, "Failed to load device state: %d", ret);
> +        error_prepend(errp, "Failed to load device state: %d: ", ret);
>          return ret;
>      }
>
> diff --git a/migration/savevm.h b/migration/savevm.h
> index
> b12681839f0b1afa3255e45215d99c13a224b19f..c337e3e3d111a7f28a57b90f61e8f70b71803d4e
> 100644
> --- a/migration/savevm.h
> +++ b/migration/savevm.h
> @@ -66,7 +66,8 @@ int qemu_save_device_state(QEMUFile *f);
>
>  int qemu_loadvm_state(QEMUFile *f, Error **errp);
>  void qemu_loadvm_state_cleanup(MigrationIncomingState *mis);
> -int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis);
> +int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis,
> +                           Error **errp);
>  int qemu_load_device_state(QEMUFile *f, Error **errp);
>  int qemu_loadvm_approve_switchover(void);
>  int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
>
> --
> 2.50.1
>
>

[-- Attachment #2: Type: text/html, Size: 8854 bytes --]

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

* Re: [PATCH v9 10/27] migration: push Error **errp into qemu_loadvm_section_start_full()
  2025-08-05 18:25 ` [PATCH v9 10/27] migration: push Error **errp into qemu_loadvm_section_start_full() Arun Menon
@ 2025-08-06  7:37   ` Marc-André Lureau
  0 siblings, 0 replies; 63+ messages in thread
From: Marc-André Lureau @ 2025-08-06  7:37 UTC (permalink / raw)
  To: Arun Menon
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

On Tue, Aug 5, 2025 at 11:03 PM Arun Menon <armenon@redhat.com> wrote:
>
> This is an incremental step in converting vmstate loading
> code to report error via Error objects instead of directly
> printing it to console/monitor.
> It is ensured that qemu_loadvm_section_start_full() must report an error
> in errp, in case of failure.
>
> Signed-off-by: Arun Menon <armenon@redhat.com>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

> ---
>  migration/savevm.c | 36 ++++++++++++++++++++----------------
>  1 file changed, 20 insertions(+), 16 deletions(-)
>
> diff --git a/migration/savevm.c b/migration/savevm.c
> index ad3dd9b172afc541f104d2187a79bafa8e380466..e337e3c61e7627f09b853bf5e9b845c38cb5f082 100644
> --- a/migration/savevm.c
> +++ b/migration/savevm.c
> @@ -2722,8 +2722,9 @@ static bool check_section_footer(QEMUFile *f, SaveStateEntry *se)
>  }
>
>  static int
> -qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type)
> +qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type, Error **errp)
>  {
> +    ERRP_GUARD();
>      bool trace_downtime = (type == QEMU_VM_SECTION_FULL);
>      uint32_t instance_id, version_id, section_id;
>      int64_t start_ts, end_ts;
> @@ -2734,8 +2735,8 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type)
>      /* Read section start */
>      section_id = qemu_get_be32(f);
>      if (!qemu_get_counted_string(f, idstr)) {
> -        error_report("Unable to read ID string for section %u",
> -                     section_id);
> +        error_setg(errp, "Unable to read ID string for section %u",
> +                   section_id);
>          return -EINVAL;
>      }
>      instance_id = qemu_get_be32(f);
> @@ -2743,8 +2744,7 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type)
>
>      ret = qemu_file_get_error(f);
>      if (ret) {
> -        error_report("%s: Failed to read instance/version ID: %d",
> -                     __func__, ret);
> +        error_setg(errp, "Failed to read instance/version ID: %d", ret);
>          return ret;
>      }
>
> @@ -2753,17 +2753,17 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type)
>      /* Find savevm section */
>      se = find_se(idstr, instance_id);
>      if (se == NULL) {
> -        error_report("Unknown savevm section or instance '%s' %"PRIu32". "
> -                     "Make sure that your current VM setup matches your "
> -                     "saved VM setup, including any hotplugged devices",
> -                     idstr, instance_id);
> +        error_setg(errp, "Unknown savevm section or instance '%s' %"PRIu32". "
> +                   "Make sure that your current VM setup matches your "
> +                   "saved VM setup, including any hotplugged devices",
> +                   idstr, instance_id);
>          return -EINVAL;
>      }
>
>      /* Validate version */
>      if (version_id > se->version_id) {
> -        error_report("savevm: unsupported version %d for '%s' v%d",
> -                     version_id, idstr, se->version_id);
> +        error_setg(errp, "savevm: unsupported version %d for '%s' v%d",
> +                   version_id, idstr, se->version_id);
>          return -EINVAL;
>      }
>      se->load_version_id = version_id;
> @@ -2771,7 +2771,7 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type)
>
>      /* Validate if it is a device's state */
>      if (xen_enabled() && se->is_ram) {
> -        error_report("loadvm: %s RAM loading not allowed on Xen", idstr);
> +        error_setg(errp, "loadvm: %s RAM loading not allowed on Xen", idstr);
>          return -EINVAL;
>      }
>
> @@ -2779,10 +2779,11 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type)
>          start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
>      }
>
> -    ret = vmstate_load(f, se, NULL);
> +    ret = vmstate_load(f, se, errp);
>      if (ret < 0) {
> -        error_report("error while loading state for instance 0x%"PRIx32" of"
> -                     " device '%s'", instance_id, idstr);
> +        error_prepend(errp,
> +                      "error while loading state for instance 0x%"PRIx32" of"
> +                      " device '%s': ", instance_id, idstr);
>          return ret;
>      }
>
> @@ -2793,6 +2794,9 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type)
>      }
>
>      if (!check_section_footer(f, se)) {
> +        error_setg(errp, "Reading footer section of instance "
> +                   "0x%"PRIx32" of device '%s' for version_id: %d failed",
> +                   instance_id, idstr, version_id);
>          return -EINVAL;
>      }
>
> @@ -3097,7 +3101,7 @@ retry:
>          switch (section_type) {
>          case QEMU_VM_SECTION_START:
>          case QEMU_VM_SECTION_FULL:
> -            ret = qemu_loadvm_section_start_full(f, section_type);
> +            ret = qemu_loadvm_section_start_full(f, section_type, errp);
>              if (ret < 0) {
>                  goto out;
>              }
>
> --
> 2.50.1
>
>


-- 
Marc-André Lureau


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

* Re: [PATCH v9 09/27] migration: push Error **errp into qemu_loadvm_state_main()
  2025-08-06  7:34   ` Marc-André Lureau
@ 2025-08-06  7:43     ` Arun Menon
  0 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-06  7:43 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger, Daniel P. Berrangé

Hi Marc-André,
Thanks for the review.

On Wed, Aug 06, 2025 at 11:34:23AM +0400, Marc-André Lureau wrote:
> Hi
> 
> On Tue, Aug 5, 2025 at 10:29 PM Arun Menon <armenon@redhat.com> wrote:
> 
> > This is an incremental step in converting vmstate loading
> > code to report error via Error objects instead of directly
> > printing it to console/monitor.
> > It is ensured that qemu_loadvm_state_main() must report an error
> > in errp, in case of failure.
> > loadvm_process_command also sets the errp object explicitly.
> >
> > Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
> > Signed-off-by: Arun Menon <armenon@redhat.com>
> > ---
> >  migration/colo.c   |  5 +++--
> >  migration/savevm.c | 33 ++++++++++++++++++++-------------
> >  migration/savevm.h |  3 ++-
> >  3 files changed, 25 insertions(+), 16 deletions(-)
> >
> > diff --git a/migration/colo.c b/migration/colo.c
> > index
> > 0ba22ee76a13e313793f653f43a728e3c433bbc1..a96e4dba15516b71d1b315c736c3b4879ff04e58
> > 100644
> > --- a/migration/colo.c
> > +++ b/migration/colo.c
> > @@ -659,6 +659,7 @@ void migrate_start_colo_process(MigrationState *s)
> >  static void colo_incoming_process_checkpoint(MigrationIncomingState *mis,
> >                        QEMUFile *fb, QIOChannelBuffer *bioc, Error **errp)
> >  {
> > +    ERRP_GUARD();
> >      uint64_t total_size;
> >      uint64_t value;
> >      Error *local_err = NULL;
> > @@ -686,11 +687,11 @@ static void
> > colo_incoming_process_checkpoint(MigrationIncomingState *mis,
> >
> >      bql_lock();
> >      cpu_synchronize_all_states();
> > -    ret = qemu_loadvm_state_main(mis->from_src_file, mis);
> > +    ret = qemu_loadvm_state_main(mis->from_src_file, mis, errp);
> >      bql_unlock();
> >
> >      if (ret < 0) {
> > -        error_setg(errp, "Load VM's live state (ram) error");
> > +        error_prepend(errp, "Load VM's live state (ram) error: ");
> >          return;
> >      }
> >
> > diff --git a/migration/savevm.c b/migration/savevm.c
> > index
> > f3b91c8ae0eee6078406081f0bd7f686fed28601..ad3dd9b172afc541f104d2187a79bafa8e380466
> > 100644
> > --- a/migration/savevm.c
> > +++ b/migration/savevm.c
> > @@ -2105,7 +2105,7 @@ static void *postcopy_ram_listen_thread(void *opaque)
> >      qemu_file_set_blocking(f, true);
> >
> >      /* TODO: sanity check that only postcopiable data will be loaded here
> > */
> > -    load_res = qemu_loadvm_state_main(f, mis);
> > +    load_res = qemu_loadvm_state_main(f, mis, NULL);
> >
> 
> What's the rationale to make the function silent or not gather the error?
> It seems error reporting could be improved below this. Maybe pass
> &error_warn here, and add a second TODO to improve error handling?

There is a separate patch which handles postcopy_ram_listen_thread
 - PATCH 22/27
Therefore I left this as is. But yes, we can pass &error_warn here.
> 
> 
> >
> >      /*
> >       * This is tricky, but, mis->from_src_file can change after it
> > @@ -2407,6 +2407,7 @@ static int
> > loadvm_postcopy_handle_resume(MigrationIncomingState *mis)
> >   */
> >  static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis, Error
> > **errp)
> >  {
> > +    ERRP_GUARD();
> >      int ret;
> >      size_t length;
> >      QIOChannelBuffer *bioc;
> > @@ -2456,9 +2457,9 @@ static int
> > loadvm_handle_cmd_packaged(MigrationIncomingState *mis, Error **errp)
> >          qemu_coroutine_yield();
> >      } while (1);
> >
> > -    ret = qemu_loadvm_state_main(packf, mis);
> > +    ret = qemu_loadvm_state_main(packf, mis, errp);
> >      if (ret < 0) {
> > -        error_setg(errp, "VM state load failed: %d", ret);
> > +        error_prepend(errp, "Loading VM state failed: %d: ", ret);
> >      }
> >      trace_loadvm_handle_cmd_packaged_main(ret);
> >      qemu_fclose(packf);
> > @@ -3074,8 +3075,10 @@ static bool
> > postcopy_pause_incoming(MigrationIncomingState *mis)
> >      return true;
> >  }
> >
> > -int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis)
> > +int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis,
> > +                           Error **errp)
> >  {
> > +    ERRP_GUARD();
> >      uint8_t section_type;
> >      int ret = 0;
> >
> > @@ -3083,8 +3086,10 @@ retry:
> >      while (true) {
> >          section_type = qemu_get_byte(f);
> >
> > -        ret = qemu_file_get_error_obj_any(f, mis->postcopy_qemufile_dst,
> > NULL);
> > +        ret = qemu_file_get_error_obj_any(f, mis->postcopy_qemufile_dst,
> > errp);
> >          if (ret) {
> > +            error_prepend(errp, "Failed to load device state section ID:
> > %d: ",
> > +                          ret);
> >              break;
> >          }
> >
> > @@ -3105,7 +3110,7 @@ retry:
> >              }
> >              break;
> >          case QEMU_VM_COMMAND:
> > -            ret = loadvm_process_command(f, NULL);
> > +            ret = loadvm_process_command(f, errp);
> >              trace_qemu_loadvm_state_section_command(ret);
> >              if ((ret < 0) || (ret == LOADVM_QUIT)) {
> >                  goto out;
> > @@ -3115,7 +3120,7 @@ retry:
> >              /* This is the end of migration */
> >              goto out;
> >          default:
> > -            error_report("Unknown savevm section type %d", section_type);
> > +            error_setg(errp, "Unknown savevm section type %d",
> > section_type);
> >              ret = -EINVAL;
> >              goto out;
> >          }
> > @@ -3123,6 +3128,9 @@ retry:
> >
> >  out:
> >      if (ret < 0) {
> > +        if (*errp == NULL) {
> > +            error_setg(errp, "Loading VM state failed: %d", ret);
> > +        }
> >          qemu_file_set_error(f, ret);
> >
> >          /* Cancel bitmaps incoming regardless of recovery */
> > @@ -3143,6 +3151,8 @@ out:
> >              migrate_postcopy_ram() && postcopy_pause_incoming(mis)) {
> >              /* Reset f to point to the newly created channel */
> >              f = mis->from_src_file;
> > +            error_free(*errp);
> > +            *errp = NULL;
> >              goto retry;
> >          }
> >      }
> > @@ -3176,10 +3186,7 @@ int qemu_loadvm_state(QEMUFile *f, Error **errp)
> >
> >      cpu_synchronize_all_pre_loadvm();
> >
> > -    ret = qemu_loadvm_state_main(f, mis);
> > -    if (ret < 0) {
> > -        error_setg(errp, "Load VM state failed: %d", ret);
> > -    }
> > +    ret = qemu_loadvm_state_main(f, mis, errp);
> >      qemu_event_set(&mis->main_thread_load_event);
> >
> >      trace_qemu_loadvm_state_post_main(ret);
> > @@ -3260,9 +3267,9 @@ int qemu_load_device_state(QEMUFile *f, Error **errp)
> >      int ret;
> >
> >      /* Load QEMU_VM_SECTION_FULL section */
> > -    ret = qemu_loadvm_state_main(f, mis);
> > +    ret = qemu_loadvm_state_main(f, mis, errp);
> >      if (ret < 0) {
> > -        error_setg(errp, "Failed to load device state: %d", ret);
> > +        error_prepend(errp, "Failed to load device state: %d: ", ret);
> >          return ret;
> >      }
> >
> > diff --git a/migration/savevm.h b/migration/savevm.h
> > index
> > b12681839f0b1afa3255e45215d99c13a224b19f..c337e3e3d111a7f28a57b90f61e8f70b71803d4e
> > 100644
> > --- a/migration/savevm.h
> > +++ b/migration/savevm.h
> > @@ -66,7 +66,8 @@ int qemu_save_device_state(QEMUFile *f);
> >
> >  int qemu_loadvm_state(QEMUFile *f, Error **errp);
> >  void qemu_loadvm_state_cleanup(MigrationIncomingState *mis);
> > -int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis);
> > +int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis,
> > +                           Error **errp);
> >  int qemu_load_device_state(QEMUFile *f, Error **errp);
> >  int qemu_loadvm_approve_switchover(void);
> >  int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
> >
> > --
> > 2.50.1
> >
> >
Regards,
Arun



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

* Re: [PATCH v9 11/27] migration: push Error **errp into qemu_loadvm_section_part_end()
  2025-08-05 18:25 ` [PATCH v9 11/27] migration: push Error **errp into qemu_loadvm_section_part_end() Arun Menon
@ 2025-08-06  7:46   ` Marc-André Lureau
  0 siblings, 0 replies; 63+ messages in thread
From: Marc-André Lureau @ 2025-08-06  7:46 UTC (permalink / raw)
  To: Arun Menon
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

[-- Attachment #1: Type: text/plain, Size: 3396 bytes --]

On Tue, Aug 5, 2025 at 10:29 PM Arun Menon <armenon@redhat.com> wrote:

> This is an incremental step in converting vmstate loading
> code to report error via Error objects instead of directly
> printing it to console/monitor.
> It is ensured that qemu_loadvm_section_part_end() must report an error
> in errp, in case of failure.
>
> Signed-off-by: Arun Menon <armenon@redhat.com>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> ---
>  migration/savevm.c | 21 ++++++++++-----------
>  1 file changed, 10 insertions(+), 11 deletions(-)
>
> diff --git a/migration/savevm.c b/migration/savevm.c
> index
> e337e3c61e7627f09b853bf5e9b845c38cb5f082..1b93df7894dd332aea25da91e24d02fa16e73363
> 100644
> --- a/migration/savevm.c
> +++ b/migration/savevm.c
> @@ -2804,8 +2804,9 @@ qemu_loadvm_section_start_full(QEMUFile *f, uint8_t
> type, Error **errp)
>  }
>
>  static int
> -qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type)
> +qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type, Error **errp)
>  {
> +    ERRP_GUARD();
>      bool trace_downtime = (type == QEMU_VM_SECTION_END);
>      int64_t start_ts, end_ts;
>      uint32_t section_id;
> @@ -2816,8 +2817,7 @@ qemu_loadvm_section_part_end(QEMUFile *f, uint8_t
> type)
>
>      ret = qemu_file_get_error(f);
>      if (ret) {
> -        error_report("%s: Failed to read section ID: %d",
> -                     __func__, ret);
> +        error_setg(errp, "Failed to read section ID: %d", ret);
>          return ret;
>      }
>
> @@ -2828,7 +2828,7 @@ qemu_loadvm_section_part_end(QEMUFile *f, uint8_t
> type)
>          }
>      }
>      if (se == NULL) {
> -        error_report("Unknown savevm section %d", section_id);
> +        error_setg(errp, "Unknown savevm section %d", section_id);
>          return -EINVAL;
>      }
>
> @@ -2836,10 +2836,10 @@ qemu_loadvm_section_part_end(QEMUFile *f, uint8_t
> type)
>          start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
>      }
>
> -    ret = vmstate_load(f, se, NULL);
> +    ret = vmstate_load(f, se, errp);
>      if (ret < 0) {
> -        error_report("error while loading state section id %d(%s)",
> -                     section_id, se->idstr);
> +        error_prepend(errp, "error while loading state section id %d(%s):
> ",
> +                      section_id, se->idstr);
>          return ret;
>      }
>
> @@ -2850,6 +2850,8 @@ qemu_loadvm_section_part_end(QEMUFile *f, uint8_t
> type)
>      }
>
>      if (!check_section_footer(f, se)) {
> +        error_setg(errp, "Check section footer error, section_id: %d",
> +                   section_id);
>          return -EINVAL;
>      }
>
> @@ -3108,7 +3110,7 @@ retry:
>              break;
>          case QEMU_VM_SECTION_PART:
>          case QEMU_VM_SECTION_END:
> -            ret = qemu_loadvm_section_part_end(f, section_type);
> +            ret = qemu_loadvm_section_part_end(f, section_type, errp);
>              if (ret < 0) {
>                  goto out;
>              }
> @@ -3132,9 +3134,6 @@ retry:
>
>  out:
>      if (ret < 0) {
> -        if (*errp == NULL) {
> -            error_setg(errp, "Loading VM state failed: %d", ret);
> -        }
>          qemu_file_set_error(f, ret);
>
>          /* Cancel bitmaps incoming regardless of recovery */
>
> --
> 2.50.1
>
>

[-- Attachment #2: Type: text/html, Size: 4502 bytes --]

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

* Re: [PATCH v9 18/27] migration: push Error **errp into loadvm_postcopy_ram_handle_discard()
  2025-08-05 18:25 ` [PATCH v9 18/27] migration: push Error **errp into loadvm_postcopy_ram_handle_discard() Arun Menon
@ 2025-08-06  7:54   ` Marc-André Lureau
  2025-08-07  6:06     ` Arun Menon
  0 siblings, 1 reply; 63+ messages in thread
From: Marc-André Lureau @ 2025-08-06  7:54 UTC (permalink / raw)
  To: Arun Menon
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

[-- Attachment #1: Type: text/plain, Size: 4827 bytes --]

Hi

On Tue, Aug 5, 2025 at 10:30 PM Arun Menon <armenon@redhat.com> wrote:

> This is an incremental step in converting vmstate loading
> code to report error via Error objects instead of directly
> printing it to console/monitor.
> It is ensured that loadvm_postcopy_ram_handle_discard() must report an
> error
> in errp, in case of failure.
>
> Signed-off-by: Arun Menon <armenon@redhat.com>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> ---
>  migration/savevm.c | 26 +++++++++++++-------------
>  1 file changed, 13 insertions(+), 13 deletions(-)
>
> diff --git a/migration/savevm.c b/migration/savevm.c
> index
> eb90873a750ded354b3db31cba40b44d1be79864..3abe4193e02aae9c813ff07fb388a7ee470c8a6a
> 100644
> --- a/migration/savevm.c
> +++ b/migration/savevm.c
> @@ -2004,7 +2004,7 @@ static int
> loadvm_postcopy_handle_advise(MigrationIncomingState *mis,
>   * There can be 0..many of these messages, each encoding multiple pages.
>   */
>  static int loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis,
> -                                              uint16_t len)
> +                                              uint16_t len, Error **errp)
>  {
>      int tmp;
>      char ramid[256];
> @@ -2017,6 +2017,7 @@ static int
> loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis,
>          /* 1st discard */
>          tmp = postcopy_ram_prepare_discard(mis);
>          if (tmp) {
> +            error_setg(errp, "Failed to prepare for RAM discard: %d",
> tmp);
>              return tmp;
>          }
>          break;
> @@ -2026,8 +2027,9 @@ static int
> loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis,
>          break;
>
>      default:
> -        error_report("CMD_POSTCOPY_RAM_DISCARD in wrong postcopy state
> (%d)",
> -                     ps);
> +        error_setg(errp,
> +                   "CMD_POSTCOPY_RAM_DISCARD in wrong postcopy state
> (%d)",
> +                   ps);
>          return -1;
>      }
>      /* We're expecting a
> @@ -2036,29 +2038,30 @@ static int
> loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis,
>       *    then at least 1 16 byte chunk
>      */
>      if (len < (1 + 1 + 1 + 1 + 2 * 8)) {
> -        error_report("CMD_POSTCOPY_RAM_DISCARD invalid length (%d)", len);
> +        error_setg(errp, "CMD_POSTCOPY_RAM_DISCARD invalid length (%d)",
> len);
>          return -1;
>      }
>
>      tmp = qemu_get_byte(mis->from_src_file);
>      if (tmp != postcopy_ram_discard_version) {
> -        error_report("CMD_POSTCOPY_RAM_DISCARD invalid version (%d)",
> tmp);
> +        error_setg(errp, "CMD_POSTCOPY_RAM_DISCARD invalid version (%d)",
> tmp);
>          return -1;
>      }
>
>      if (!qemu_get_counted_string(mis->from_src_file, ramid)) {
> -        error_report("CMD_POSTCOPY_RAM_DISCARD Failed to read RAMBlock
> ID");
> +        error_setg(errp,
> +                   "CMD_POSTCOPY_RAM_DISCARD Failed to read RAMBlock ID");
>          return -1;
>      }
>      tmp = qemu_get_byte(mis->from_src_file);
>      if (tmp != 0) {
> -        error_report("CMD_POSTCOPY_RAM_DISCARD missing nil (%d)", tmp);
> +        error_setg(errp, "CMD_POSTCOPY_RAM_DISCARD missing nil (%d)",
> tmp);
>          return -1;
>      }
>
>      len -= 3 + strlen(ramid);
>      if (len % 16) {
> -        error_report("CMD_POSTCOPY_RAM_DISCARD invalid length (%d)", len);
> +        error_setg(errp, "CMD_POSTCOPY_RAM_DISCARD invalid length (%d)",
> len);
>          return -1;
>      }
>      trace_loadvm_postcopy_ram_handle_discard_header(ramid, len);
> @@ -2070,6 +2073,7 @@ static int
> loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis,
>          len -= 16;
>          int ret = ram_discard_range(ramid, start_addr, block_length);
>          if (ret) {
> +            error_setg(errp, "Failed to discard RAM range %s: %d", ramid,
> ret);
>

note: the ram_discard_range() and ram_block_discard_range() functions also
calls error_report()  Maybe they can be converted too.., let's not do this
in this already long series.




>              return ret;
>          }
>      }
> @@ -2629,11 +2633,7 @@ static int loadvm_process_command(QEMUFile *f,
> Error **errp)
>          return loadvm_postcopy_handle_run(mis, errp);
>
>      case MIG_CMD_POSTCOPY_RAM_DISCARD:
> -        ret = loadvm_postcopy_ram_handle_discard(mis, len);
> -        if (ret < 0) {
> -            error_setg(errp, "Failed to load device state command: %d",
> ret);
> -        }
> -        return ret;
> +        return loadvm_postcopy_ram_handle_discard(mis, len, errp);
>
>      case MIG_CMD_POSTCOPY_RESUME:
>          loadvm_postcopy_handle_resume(mis);
>
> --
> 2.50.1
>
>

[-- Attachment #2: Type: text/html, Size: 6290 bytes --]

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

* Re: [PATCH v9 20/27] migration: push Error **errp into loadvm_process_enable_colo()
  2025-08-05 18:25 ` [PATCH v9 20/27] migration: push Error **errp into loadvm_process_enable_colo() Arun Menon
@ 2025-08-06  8:07   ` Marc-André Lureau
  2025-08-06  9:56     ` Arun Menon
  0 siblings, 1 reply; 63+ messages in thread
From: Marc-André Lureau @ 2025-08-06  8:07 UTC (permalink / raw)
  To: Arun Menon
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

[-- Attachment #1: Type: text/plain, Size: 6765 bytes --]

Hi

On Tue, Aug 5, 2025 at 10:31 PM Arun Menon <armenon@redhat.com> wrote:

> This is an incremental step in converting vmstate loading
> code to report error via Error objects instead of directly
> printing it to console/monitor.
> It is ensured that loadvm_process_enable_colo() must report an error
> in errp, in case of failure.
>
> Signed-off-by: Arun Menon <armenon@redhat.com>
>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> ---
>  include/migration/colo.h |  2 +-
>  migration/migration.c    | 12 ++++++------
>  migration/ram.c          |  8 ++++----
>  migration/ram.h          |  2 +-
>  migration/savevm.c       | 26 ++++++++++++++------------
>  5 files changed, 26 insertions(+), 24 deletions(-)
>
> diff --git a/include/migration/colo.h b/include/migration/colo.h
> index
> 43222ef5ae6adc3f7d8aa6a48bef79af33d09208..d4fe422e4d335d3bef4f860f56400fcd73287a0e
> 100644
> --- a/include/migration/colo.h
> +++ b/include/migration/colo.h
> @@ -25,7 +25,7 @@ void migrate_start_colo_process(MigrationState *s);
>  bool migration_in_colo_state(void);
>
>  /* loadvm */
> -int migration_incoming_enable_colo(void);
> +int migration_incoming_enable_colo(Error **errp);
>  void migration_incoming_disable_colo(void);
>  bool migration_incoming_colo_enabled(void);
>  bool migration_incoming_in_colo_state(void);
> diff --git a/migration/migration.c b/migration/migration.c
> index
> 6962dc7d7f3e0121d28994c98f12f9f2258343d7..4a76d7ed730589bae87115368b0bf4819f8b161e
> 100644
> --- a/migration/migration.c
> +++ b/migration/migration.c
> @@ -623,22 +623,22 @@ void migration_incoming_disable_colo(void)
>      migration_colo_enabled = false;
>  }
>
> -int migration_incoming_enable_colo(void)
> +int migration_incoming_enable_colo(Error **errp)
>  {
>  #ifndef CONFIG_REPLICATION
> -    error_report("ENABLE_COLO command come in migration stream, but the "
> -                 "replication module is not built in");
> +    error_setg(errp, "ENABLE_COLO command come in migration stream, but
> the "
> +               "replication module is not built in");
>      return -ENOTSUP;
>  #endif
>
>      if (!migrate_colo()) {
> -        error_report("ENABLE_COLO command come in migration stream, but
> x-colo "
> -                     "capability is not set");
> +        error_setg(errp, "ENABLE_COLO command come in migration stream"
> +                   ", but x-colo capability is not set");
>          return -EINVAL;
>      }
>
>      if (ram_block_discard_disable(true)) {
> -        error_report("COLO: cannot disable RAM discard");
> +        error_setg(errp, "COLO: cannot disable RAM discard");
>          return -EBUSY;
>      }
>      migration_colo_enabled = true;
> diff --git a/migration/ram.c b/migration/ram.c
> index
> 6a0dcc04f436524a37672c41c38f201f06773374..995431c9e320f443c385c29d664d62e18c1afd90
> 100644
> --- a/migration/ram.c
> +++ b/migration/ram.c
> @@ -3576,7 +3576,7 @@ static void colo_init_ram_state(void)
>   * memory of the secondary VM, it is need to hold the global lock
>   * to call this helper.
>   */
> -int colo_init_ram_cache(void)
> +int colo_init_ram_cache(Error **errp)
>  {
>      RAMBlock *block;
>
> @@ -3585,9 +3585,9 @@ int colo_init_ram_cache(void)
>              block->colo_cache = qemu_anon_ram_alloc(block->used_length,
>                                                      NULL, false, false);
>              if (!block->colo_cache) {
> -                error_report("%s: Can't alloc memory for COLO cache of
> block %s,"
> -                             "size 0x" RAM_ADDR_FMT, __func__,
> block->idstr,
> -                             block->used_length);
> +                error_setg(errp, "Can't alloc memory for COLO cache of "
> +                           "block %s, size 0x" RAM_ADDR_FMT,
> +                           block->idstr, block->used_length);
>                  RAMBLOCK_FOREACH_NOT_IGNORED(block) {
>                      if (block->colo_cache) {
>                          qemu_anon_ram_free(block->colo_cache,
> block->used_length);
> diff --git a/migration/ram.h b/migration/ram.h
> index
> 275709a99187f9429ccb4111e05281ec268ba0db..24cd0bf585762cfa1e86834dc03c6baeea2f0627
> 100644
> --- a/migration/ram.h
> +++ b/migration/ram.h
> @@ -109,7 +109,7 @@ void ramblock_set_file_bmap_atomic(RAMBlock *block,
> ram_addr_t offset,
>                                     bool set);
>
>  /* ram cache */
> -int colo_init_ram_cache(void);
> +int colo_init_ram_cache(Error **errp);
>  void colo_flush_ram_cache(void);
>  void colo_release_ram_cache(void);
>  void colo_incoming_start_dirty_log(void);
> diff --git a/migration/savevm.c b/migration/savevm.c
> index
> 938adb20270adbf9546b0884d0877c25c3f0f816..a6b71a958aeda31e89043f8103bfe2fc89542fb5
> 100644
> --- a/migration/savevm.c
> +++ b/migration/savevm.c
> @@ -2519,15 +2519,21 @@ static int
> loadvm_handle_recv_bitmap(MigrationIncomingState *mis,
>      return 0;
>  }
>
> -static int loadvm_process_enable_colo(MigrationIncomingState *mis)
> +static int loadvm_process_enable_colo(MigrationIncomingState *mis,
> +                                      Error **errp)
>  {
> -    int ret = migration_incoming_enable_colo();
> +    ERRP_GUARD();
> +    int ret;
>
> -    if (!ret) {
> -        ret = colo_init_ram_cache();
> -        if (ret) {
> -            migration_incoming_disable_colo();
> -        }
> +    ret = migration_incoming_enable_colo(errp);
> +    if (ret < 0) {
> +        return ret;
> +    }
> +
> +    ret = colo_init_ram_cache(errp);
> +    if (ret) {
>

Note: here you keep the "ret" error condition !=0, ok.

colo_init_ram_cache(), returns -errno on error. Although errno should
remain unchanged on success (during qemu_anon_ram_free etc), I think it
would be safer to convert the function to follow the recommended
bool-valued function for true on success / false on failure instead.


> +        error_prepend(errp, "failed to init colo RAM cache: %d: ", ret);
> +        migration_incoming_disable_colo();
>
     }
>      return ret;
>  }
> @@ -2646,11 +2652,7 @@ static int loadvm_process_command(QEMUFile *f,
> Error **errp)
>          return loadvm_handle_recv_bitmap(mis, len, errp);
>
>      case MIG_CMD_ENABLE_COLO:
> -        ret = loadvm_process_enable_colo(mis);
> -        if (ret < 0) {
> -            error_setg(errp, "Failed to load device state command: %d",
> ret);
> -        }
> -        return ret;
> +        return loadvm_process_enable_colo(mis, errp);
>
>      case MIG_CMD_SWITCHOVER_START:
>          ret = loadvm_postcopy_handle_switchover_start();
>
> --
> 2.50.1
>
>

[-- Attachment #2: Type: text/html, Size: 8570 bytes --]

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

* Re: [PATCH v9 23/27] migration: Refactor vmstate_save_state_v() function
  2025-08-05 18:25 ` [PATCH v9 23/27] migration: Refactor vmstate_save_state_v() function Arun Menon
  2025-08-06  5:10   ` Akihiko Odaki
@ 2025-08-06  8:19   ` Marc-André Lureau
  2025-08-06  9:45     ` Arun Menon
  1 sibling, 1 reply; 63+ messages in thread
From: Marc-André Lureau @ 2025-08-06  8:19 UTC (permalink / raw)
  To: Arun Menon
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

[-- Attachment #1: Type: text/plain, Size: 5232 bytes --]

Hi

On Tue, Aug 5, 2025 at 10:31 PM Arun Menon <armenon@redhat.com> wrote:

> The original vmstate_save_state_v() function combined multiple
> responsibilities like calling pre-save hooks, saving the state of
> the device, handling subsection saves and invoking post-save hooks.
> This led to a lengthy and less readable function.
>
> To address this, introduce wrapper functions for pre-save,
> post-save and the device-state save functionalities.
>
> Signed-off-by: Arun Menon <armenon@redhat.com>
> ---
>  migration/vmstate.c | 78
> ++++++++++++++++++++++++++++++++++++++++-------------
>  1 file changed, 60 insertions(+), 18 deletions(-)
>
> diff --git a/migration/vmstate.c b/migration/vmstate.c
> index
> 60ff38858cf54277992fa5eddeadb6f3d70edec3..fbc59caadbbcc75fe6de27b459aa9aa25e76aa0a
> 100644
> --- a/migration/vmstate.c
> +++ b/migration/vmstate.c
> @@ -414,22 +414,43 @@ int vmstate_save_state_with_err(QEMUFile *f, const
> VMStateDescription *vmsd,
>      return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id,
> vmsd->version_id, errp);
>  }
>
> -int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
> -                         void *opaque, JSONWriter *vmdesc, int
> version_id, Error **errp)
> +static int pre_save_dispatch(const VMStateDescription *vmsd, void *opaque,
> +                             Error **errp)
>  {
>      int ret = 0;
> -    const VMStateField *field = vmsd->fields;
> -
> -    trace_vmstate_save_state_top(vmsd->name);
> -
>      if (vmsd->pre_save) {
>          ret = vmsd->pre_save(opaque);
>          trace_vmstate_save_state_pre_save_res(vmsd->name, ret);
>          if (ret) {
> -            error_setg(errp, "pre-save failed: %s", vmsd->name);
> -            return ret;
> +            error_setg(errp, "pre-save for %s failed, ret: %d",
> +                       vmsd->name, ret);
>          }
>      }
> +    return ret;
> +}
> +
> +static int post_save_dispatch(const VMStateDescription *vmsd, void
> *opaque,
> +                              Error **errp)
> +{
> +    int ret = 0;
> +    if (vmsd->post_save) {
> +        ret = vmsd->post_save(opaque);

+        error_setg(errp, "post-save for %s failed, ret: %d",
> +                   vmsd->name, ret);
>

Only set errp if ret != 0


> +    }
> +    return ret;
> +}
> +
> +static int vmstate_save_dispatch(QEMUFile *f,
> +                                 const VMStateDescription *vmsd,
> +                                 void *opaque, JSONWriter *vmdesc,
> +                                 int version_id, Error **errp)
> +{
> +    ERRP_GUARD();
> +    int ret = 0;
> +    int ps_ret = 0;
> +    Error *local_err = NULL;
> +    const VMStateField *field = vmsd->fields;
>
>      if (vmdesc) {
>          json_writer_str(vmdesc, "vmsd_name", vmsd->name);
> @@ -532,9 +553,7 @@ 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);
> -                    if (vmsd->post_save) {
> -                        vmsd->post_save(opaque);
> -                    }
> +                    ps_ret = post_save_dispatch(vmsd, opaque, &local_err);
>

why keep ps_ret ?

What do you do of local_err ?


>                      return ret;
>                  }
>
> @@ -557,16 +576,39 @@ int vmstate_save_state_v(QEMUFile *f, const
> VMStateDescription *vmsd,
>      if (vmdesc) {
>          json_writer_end_array(vmdesc);
>      }
> +    return ret;
> +}
>
> -    ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc, errp);
>
> -    if (vmsd->post_save) {
> -        int ps_ret = vmsd->post_save(opaque);
> -        if (!ret && ps_ret) {
> -            ret = ps_ret;
> -            error_setg(errp, "post-save failed: %s", vmsd->name);
> -        }
> +int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
> +                         void *opaque, JSONWriter *vmdesc, int version_id,
> +                         Error **errp)
> +{
> +    ERRP_GUARD();
> +    int ret = 0;
> +    Error *local_err = NULL;
> +    int ps_ret = 0;
> +
> +    trace_vmstate_save_state_top(vmsd->name);
> +
> +    ret = pre_save_dispatch(vmsd, opaque, errp);
> +    if (ret) {
> +        return ret;
> +    }
> +
> +    ret = vmstate_save_dispatch(f, vmsd, opaque, vmdesc,
> +                                version_id, errp);
> +    if (ret) {
> +        return ret


post_save_dispatch() should be called on failure.

>
>      }
> +
> +    ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc, errp);
>

Imho this should be moved back to the vmstate_save_dispatch()

+    ps_ret = post_save_dispatch(vmsd, opaque, &local_err);
> +    if (!ret && ps_ret) {
> +        ret = ps_ret;
> +        error_setg(errp, "post-save failed: %s", vmsd->name);
>

And then you can have a single place to call post_save_dispatch() - remove
it from vmstate_subsection_save.

It should probably call error_propagate() instead.



> +    }
> +
>      return ret;
>  }
>
>
> --
> 2.50.1
>
>

[-- Attachment #2: Type: text/html, Size: 7451 bytes --]

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

* Re: [PATCH v9 25/27] migration: Remove error variant of vmstate_save_state() function
  2025-08-05 18:25 ` [PATCH v9 25/27] migration: Remove error variant of vmstate_save_state() function Arun Menon
@ 2025-08-06  8:28   ` Marc-André Lureau
  2025-08-06  9:46     ` Arun Menon
  2025-08-06 11:16     ` Arun Menon
  0 siblings, 2 replies; 63+ messages in thread
From: Marc-André Lureau @ 2025-08-06  8:28 UTC (permalink / raw)
  To: Arun Menon
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

[-- Attachment #1: Type: text/plain, Size: 18304 bytes --]

Hi

On Tue, Aug 5, 2025 at 10:31 PM Arun Menon <armenon@redhat.com> wrote:

> This commit removes the redundant vmstate_save_state_with_err()
> function.
>
> Previously, commit 969298f9d7 introduced vmstate_save_state_with_err()
> to handle error propagation, while vmstate_save_state() existed for
> non-error scenarios.
> This is because there were code paths where vmstate_save_state_v()
> (called internally by vmstate_save_state) did not explicitly set
> errors on failure.
>
> This change unifies error handling by
>  - updating vmstate_save_state() to accept an Error **errp argument.
>  - vmstate_save_state_v() ensures errors are set directly within the errp
>    object, eliminating the need for two separate functions. Specifically
>    errors are handled by storing them in errp in vmstate_save_dispatch()
>
> All calls to vmstate_save_state_with_err() are replaced with
> vmstate_save_state(). This simplifies the API and improves code
> maintainability.
>
> Signed-off-by: Arun Menon <armenon@redhat.com>
>

I realize that vmstate_save_state() callers progressively became silent on
error with this series (switching to error_report to error_set). I suggest
you make a preliminary patch to make vmstate_save_state() verbose by
passing &error_warn to vmstate_save_state_v. wdyt?

patch lgtm otherwise




> ---
>  hw/display/virtio-gpu.c     |  3 ++-
>  hw/pci/pci.c                |  2 +-
>  hw/s390x/virtio-ccw.c       |  2 +-
>  hw/scsi/spapr_vscsi.c       |  2 +-
>  hw/vfio/pci.c               |  4 ++--
>  hw/virtio/virtio-mmio.c     |  2 +-
>  hw/virtio/virtio-pci.c      |  2 +-
>  hw/virtio/virtio.c          |  4 ++--
>  include/migration/vmstate.h |  2 --
>  migration/cpr.c             |  5 +++--
>  migration/savevm.c          |  7 ++++---
>  migration/vmstate-types.c   | 12 +++++++-----
>  migration/vmstate.c         | 18 ++++++++----------
>  tests/unit/test-vmstate.c   |  8 +++++---
>  ui/vdagent.c                |  3 ++-
>  15 files changed, 40 insertions(+), 36 deletions(-)
>
> diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
> index
> 5ff9f5e6dc0b6b112e36170318c900f893a73c5e..bddb26fa21312e5d0b2b6412cdf2a6c28dd06eb3
> 100644
> --- a/hw/display/virtio-gpu.c
> +++ b/hw/display/virtio-gpu.c
> @@ -1246,7 +1246,8 @@ 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);
> +    return vmstate_save_state(f, &vmstate_virtio_gpu_scanouts, g, NULL,
> +                              &error_warn);
>  }
>
>  static bool virtio_gpu_load_restore_mapping(VirtIOGPU *g,
> diff --git a/hw/pci/pci.c b/hw/pci/pci.c
> index
> 80545189980f176ca6a3dc9abce7043c8bc2708c..f7fb3931ff190c25ffa665ba80abe7737dbefdb6
> 100644
> --- a/hw/pci/pci.c
> +++ b/hw/pci/pci.c
> @@ -926,7 +926,7 @@ void pci_device_save(PCIDevice *s, QEMUFile *f)
>       * 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);
> +    vmstate_save_state(f, &vmstate_pci_device, s, NULL, &error_warn);
>      /* Restore the interrupt status bit. */
>      pci_update_irq_status(s);
>  }
> diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
> index
> bafcb9b76d81749925e31b5a0a1320b3332ad2cf..14304fd3e8901d28a7788266df2c7ef7334b691c
> 100644
> --- a/hw/s390x/virtio-ccw.c
> +++ b/hw/s390x/virtio-ccw.c
> @@ -1130,7 +1130,7 @@ 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);
> +    vmstate_save_state(f, &vmstate_virtio_ccw_dev, dev, NULL,
> &error_warn);
>  }
>
>  static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
> diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c
> index
> a4812afd0cc7c495080ef03a531c7d279af29b33..2204beb40c7cdc827db2bf86fb8612d8c09999ed
> 100644
> --- a/hw/scsi/spapr_vscsi.c
> +++ b/hw/scsi/spapr_vscsi.c
> @@ -630,7 +630,7 @@ static void vscsi_save_request(QEMUFile *f,
> SCSIRequest *sreq)
>      vscsi_req *req = sreq->hba_private;
>      assert(req->active);
>
> -    vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL);
> +    vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL,
> &error_warn);
>
>      trace_spapr_vscsi_save_request(req->qtag, req->cur_desc_num,
>                                     req->cur_desc_offset);
> diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
> index
> 04d385d7674f444844beeee7364ee0424f762758..8e2b6d611de8efdf98235355226f5e5a6fd87b19
> 100644
> --- a/hw/vfio/pci.c
> +++ b/hw/vfio/pci.c
> @@ -2780,8 +2780,8 @@ static int vfio_pci_save_config(VFIODevice
> *vbasedev, QEMUFile *f, Error **errp)
>  {
>      VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev);
>
> -    return vmstate_save_state_with_err(f, &vmstate_vfio_pci_config, vdev,
> NULL,
> -                                       errp);
> +    return vmstate_save_state(f, &vmstate_vfio_pci_config, vdev, NULL,
> +                              errp);
>  }
>
>  static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f)
> diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c
> index
> f14f0487fec3ed30d2cc3aec96950c2121265716..58957990743f061e78d0eafcfcb6cc5989252edc
> 100644
> --- a/hw/virtio/virtio-mmio.c
> +++ b/hw/virtio/virtio-mmio.c
> @@ -613,7 +613,7 @@ static void virtio_mmio_save_extra_state(DeviceState
> *opaque, QEMUFile *f)
>  {
>      VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
>
> -    vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL);
> +    vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL, &error_warn);
>  }
>
>  static int virtio_mmio_load_extra_state(DeviceState *opaque, QEMUFile *f)
> diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
> index
> 07f144d6d1bfbb561b28015de71762601080dc23..0826399cdfbf09b5b3f9acd9967de3417858c9d2
> 100644
> --- a/hw/virtio/virtio-pci.c
> +++ b/hw/virtio/virtio-pci.c
> @@ -154,7 +154,7 @@ static void virtio_pci_save_extra_state(DeviceState
> *d, QEMUFile *f)
>  {
>      VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
>
> -    vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL);
> +    vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL, &error_warn);
>  }
>
>  static int virtio_pci_load_extra_state(DeviceState *d, QEMUFile *f)
> diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
> index
> aac317a93c3cd55ece9b0fd858c7b2459f8242f9..552e167529e9c22fb4cba2b445ffad85fef619f3
> 100644
> --- a/hw/virtio/virtio.c
> +++ b/hw/virtio/virtio.c
> @@ -3025,14 +3025,14 @@ int virtio_save(VirtIODevice *vdev, QEMUFile *f)
>      }
>
>      if (vdc->vmsd) {
> -        int ret = vmstate_save_state(f, vdc->vmsd, vdev, NULL);
> +        int ret = vmstate_save_state(f, vdc->vmsd, vdev, NULL,
> &error_warn);
>          if (ret) {
>              return ret;
>          }
>      }
>
>      /* Subsections */
> -    return vmstate_save_state(f, &vmstate_virtio, vdev, NULL);
> +    return vmstate_save_state(f, &vmstate_virtio, vdev, NULL,
> &error_warn);
>  }
>
>  /* A wrapper for use as a VMState .put function */
> diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
> index
> 056781b1c21e737583f081594d9f88b32adfd674..5fe9bbf39058d0cf97c1adab54cc516dbe8dc32a
> 100644
> --- a/include/migration/vmstate.h
> +++ b/include/migration/vmstate.h
> @@ -1198,8 +1198,6 @@ extern const VMStateInfo vmstate_info_qlist;
>  int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
>                         void *opaque, int version_id, Error **errp);
>  int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
> -                       void *opaque, JSONWriter *vmdesc);
> -int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription
> *vmsd,
>                         void *opaque, JSONWriter *vmdesc, Error **errp);
>  int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
>                           void *opaque, JSONWriter *vmdesc,
> diff --git a/migration/cpr.c b/migration/cpr.c
> index
> bdb24736f44e91ba59b6e622a315597c97e7f64d..d48f013dac21b531e41a26f4bb33eefe32097757
> 100644
> --- a/migration/cpr.c
> +++ b/migration/cpr.c
> @@ -163,6 +163,7 @@ bool cpr_is_incoming(void)
>
>  int cpr_state_save(MigrationChannel *channel, Error **errp)
>  {
> +    ERRP_GUARD();
>      int ret;
>      QEMUFile *f;
>      MigMode mode = migrate_mode();
> @@ -182,9 +183,9 @@ int cpr_state_save(MigrationChannel *channel, Error
> **errp)
>      qemu_put_be32(f, QEMU_CPR_FILE_MAGIC);
>      qemu_put_be32(f, QEMU_CPR_FILE_VERSION);
>
> -    ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0);
> +    ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0, errp);
>      if (ret) {
> -        error_setg(errp, "vmstate_save_state error %d", ret);
> +        error_prepend(errp, "vmstate_save_state error %d: ", ret);
>          qemu_fclose(f);
>          return ret;
>      }
> diff --git a/migration/savevm.c b/migration/savevm.c
> index
> 4df50fb5c674e56c0173c357c1e23367373e2114..3cb9c34efb22fdcb2b05cf1fefcb6fbd80cadca5
> 100644
> --- a/migration/savevm.c
> +++ b/migration/savevm.c
> @@ -1056,8 +1056,8 @@ static int vmstate_save(QEMUFile *f, SaveStateEntry
> *se, JSONWriter *vmdesc,
>      if (!se->vmsd) {
>          vmstate_save_old_style(f, se, vmdesc);
>      } else {
> -        ret = vmstate_save_state_with_err(f, se->vmsd, se->opaque, vmdesc,
> -                                          errp);
> +        ret = vmstate_save_state(f, se->vmsd, se->opaque, vmdesc,
> +                                 errp);
>          if (ret) {
>              return ret;
>          }
> @@ -1303,7 +1303,8 @@ void qemu_savevm_state_header(QEMUFile *f)
>              json_writer_start_object(vmdesc, "configuration");
>          }
>
> -        vmstate_save_state(f, &vmstate_configuration, &savevm_state,
> vmdesc);
> +        vmstate_save_state(f, &vmstate_configuration, &savevm_state,
> vmdesc,
> +                           &error_warn);
>
>          if (vmdesc) {
>              json_writer_end_object(vmdesc);
> diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c
> index
> e2020a733d0921d748b6f832a193e5de8d302d5f..2b88dc145ab32072f09845cee95b79aad7ac3cb2
> 100644
> --- a/migration/vmstate-types.c
> +++ b/migration/vmstate-types.c
> @@ -564,7 +564,7 @@ static int put_tmp(QEMUFile *f, void *pv, size_t size,
>
>      /* Writes the parent field which is at the start of the tmp */
>      *(void **)tmp = pv;
> -    ret = vmstate_save_state(f, vmsd, tmp, vmdesc);
> +    ret = vmstate_save_state(f, vmsd, tmp, vmdesc, &error_warn);
>      g_free(tmp);
>
>      return ret;
> @@ -675,7 +675,7 @@ static int put_qtailq(QEMUFile *f, void *pv, size_t
> unused_size,
>
>      QTAILQ_RAW_FOREACH(elm, pv, entry_offset) {
>          qemu_put_byte(f, true);
> -        ret = vmstate_save_state(f, vmsd, elm, vmdesc);
> +        ret = vmstate_save_state(f, vmsd, elm, vmdesc, &error_warn);
>          if (ret) {
>              return ret;
>          }
> @@ -712,7 +712,8 @@ static gboolean put_gtree_elem(gpointer key, gpointer
> value, gpointer data)
>      if (!capsule->key_vmsd) {
>          qemu_put_be64(f, (uint64_t)(uintptr_t)(key)); /* direct key */
>      } else {
> -        ret = vmstate_save_state(f, capsule->key_vmsd, key,
> capsule->vmdesc);
> +        ret = vmstate_save_state(f, capsule->key_vmsd, key,
> capsule->vmdesc,
> +                                 &error_warn);
>          if (ret) {
>              capsule->ret = ret;
>              return true;
> @@ -720,7 +721,8 @@ static gboolean put_gtree_elem(gpointer key, gpointer
> value, gpointer data)
>      }
>
>      /* put the data */
> -    ret = vmstate_save_state(f, capsule->val_vmsd, value,
> capsule->vmdesc);
> +    ret = vmstate_save_state(f, capsule->val_vmsd, value, capsule->vmdesc,
> +                             &error_warn);
>      if (ret) {
>          capsule->ret = ret;
>          return true;
> @@ -856,7 +858,7 @@ static int put_qlist(QEMUFile *f, void *pv, size_t
> unused_size,
>      trace_put_qlist(field->name, vmsd->name, vmsd->version_id);
>      QLIST_RAW_FOREACH(elm, pv, entry_offset) {
>          qemu_put_byte(f, true);
> -        ret = vmstate_save_state(f, vmsd, elm, vmdesc);
> +        ret = vmstate_save_state(f, vmsd, elm, vmdesc, &error_warn);
>          if (ret) {
>              error_report("%s: failed to save %s (%d)", field->name,
>                           vmsd->name, ret);
> diff --git a/migration/vmstate.c b/migration/vmstate.c
> index
> ef78a1e62ad92e9608de72d125da80ea496c8dd1..569e66ea983f833e6a0d651d2a751f34a64e8f5c
> 100644
> --- a/migration/vmstate.c
> +++ b/migration/vmstate.c
> @@ -403,12 +403,6 @@ bool vmstate_section_needed(const VMStateDescription
> *vmsd, void *opaque)
>
>
>  int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
> -                       void *opaque, JSONWriter *vmdesc_id)
> -{
> -    return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id,
> vmsd->version_id, NULL);
> -}
> -
> -int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription
> *vmsd,
>                         void *opaque, JSONWriter *vmdesc_id, Error **errp)
>  {
>      return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id,
> vmsd->version_id, errp);
> @@ -530,7 +524,7 @@ static int vmstate_save_dispatch(QEMUFile *f,
>
>                  if (inner_field->flags & VMS_STRUCT) {
>                      ret = vmstate_save_state(f, inner_field->vmsd,
> -                                             curr_elem, vmdesc_loop);
> +                                             curr_elem, vmdesc_loop,
> errp);
>                  } else if (inner_field->flags & VMS_VSTRUCT) {
>                      ret = vmstate_save_state_v(f, inner_field->vmsd,
>                                                 curr_elem, vmdesc_loop,
> @@ -539,6 +533,10 @@ static int vmstate_save_dispatch(QEMUFile *f,
>                  } else {
>                      ret = inner_field->info->put(f, curr_elem, size,
>                                                   inner_field,
> vmdesc_loop);
> +                    if (ret) {
> +                        error_setg(errp, "Save of field %s failed",
> +                                   inner_field->name);
> +                    }
>                  }
>
>                  written_bytes = qemu_file_transferred(f) - old_offset;
> @@ -551,8 +549,8 @@ static int vmstate_save_dispatch(QEMUFile *f,
>                  }
>
>                  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);
>                      ps_ret = post_save_dispatch(vmsd, opaque, &local_err);
>                      if (ps_ret) {
>                          ret = ps_ret;
> @@ -723,7 +721,7 @@ static int vmstate_subsection_save(QEMUFile *f, const
> VMStateDescription *vmsd,
>              qemu_put_byte(f, len);
>              qemu_put_buffer(f, (uint8_t *)vmsdsub->name, len);
>              qemu_put_be32(f, vmsdsub->version_id);
> -            ret = vmstate_save_state_with_err(f, vmsdsub, opaque, vmdesc,
> errp);
> +            ret = vmstate_save_state(f, vmsdsub, opaque, vmdesc, errp);
>              if (ret) {
>                  return ret;
>              }
> diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c
> index
> 807deb3531f3079864ac99567d4dece7122571dd..5f31ed4b6969c4ebf72b298f965bcbdc752ff87f
> 100644
> --- a/tests/unit/test-vmstate.c
> +++ b/tests/unit/test-vmstate.c
> @@ -69,7 +69,7 @@ static void save_vmstate(const VMStateDescription *desc,
> void *obj)
>      QEMUFile *f = open_test_file(true);
>
>      /* Save file with vmstate */
> -    int ret = vmstate_save_state(f, desc, obj, NULL);
> +    int ret = vmstate_save_state(f, desc, obj, NULL, &error_warn);
>      g_assert(!ret);
>      qemu_put_byte(f, QEMU_VM_EOF);
>      g_assert(!qemu_file_get_error(f));
> @@ -429,7 +429,8 @@ static void test_save_noskip(void)
>      QEMUFile *fsave = open_test_file(true);
>      TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
>                         .skip_c_e = false };
> -    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
> +    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL,
> +                                 &error_warn);
>      g_assert(!ret);
>      g_assert(!qemu_file_get_error(fsave));
>
> @@ -451,7 +452,8 @@ static void test_save_skip(void)
>      QEMUFile *fsave = open_test_file(true);
>      TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
>                         .skip_c_e = true };
> -    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
> +    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL,
> +                                 &error_warn);
>      g_assert(!ret);
>      g_assert(!qemu_file_get_error(fsave));
>
> diff --git a/ui/vdagent.c b/ui/vdagent.c
> index
> b9a22a689d9acfeafb862ae73308db6fbd52331e..e0fc7ff71468bfe66761385387653c742c375f05
> 100644
> --- a/ui/vdagent.c
> +++ b/ui/vdagent.c
> @@ -992,7 +992,8 @@ static int put_cbinfo(QEMUFile *f, void *pv, size_t
> size,
>          }
>      }
>
> -    return vmstate_save_state(f, &vmstate_cbinfo_array, &cbinfo, vmdesc);
> +    return vmstate_save_state(f, &vmstate_cbinfo_array, &cbinfo, vmdesc,
> +                              &error_warn);
>  }
>
>  static int get_cbinfo(QEMUFile *f, void *pv, size_t size,
>
> --
> 2.50.1
>
>

[-- Attachment #2: Type: text/html, Size: 21190 bytes --]

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

* Re: [PATCH v9 05/27] migration: push Error **errp into loadvm_process_command()
  2025-08-05 18:25 ` [PATCH v9 05/27] migration: push Error **errp into loadvm_process_command() Arun Menon
@ 2025-08-06  8:31   ` Marc-André Lureau
  2025-08-06  9:46     ` Arun Menon
  0 siblings, 1 reply; 63+ messages in thread
From: Marc-André Lureau @ 2025-08-06  8:31 UTC (permalink / raw)
  To: Arun Menon
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

[-- Attachment #1: Type: text/plain, Size: 6513 bytes --]

Hi

On Tue, Aug 5, 2025 at 10:28 PM Arun Menon <armenon@redhat.com> wrote:

> This is an incremental step in converting vmstate loading
> code to report error via Error objects instead of directly
> printing it to console/monitor.
> It is ensured that loadvm_process_command() must report an error
> in errp, in case of failure.
>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Signed-off-by: Arun Menon <armenon@redhat.com>
> ---
>  migration/savevm.c | 72
> +++++++++++++++++++++++++++++++++++++++---------------
>  1 file changed, 52 insertions(+), 20 deletions(-)
>
> diff --git a/migration/savevm.c b/migration/savevm.c
> index
> f37c4455dcf839d46f026fc7c7ff02e2dfffe7b4..cb673f43b174249ff1525dba41284de2e5a70735
> 100644
> --- a/migration/savevm.c
> +++ b/migration/savevm.c
> @@ -2546,12 +2546,13 @@ static int
> loadvm_postcopy_handle_switchover_start(void)
>   * LOADVM_QUIT All good, but exit the loop
>   * <0          Error
>   */
> -static int loadvm_process_command(QEMUFile *f)
> +static int loadvm_process_command(QEMUFile *f, Error **errp)
>  {
>      MigrationIncomingState *mis = migration_incoming_get_current();
>      uint16_t cmd;
>      uint16_t len;
>      uint32_t tmp32;
> +    int ret;
>
>      cmd = qemu_get_be16(f);
>      len = qemu_get_be16(f);
> @@ -2562,16 +2563,16 @@ static int loadvm_process_command(QEMUFile *f)
>      }
>
>      if (cmd >= MIG_CMD_MAX || cmd == MIG_CMD_INVALID) {
> -        error_report("MIG_CMD 0x%x unknown (len 0x%x)", cmd, len);
> +        error_setg(errp, "MIG_CMD 0x%x unknown (len 0x%x)", cmd, len);
>          return -EINVAL;
>      }
>
>      trace_loadvm_process_command(mig_cmd_args[cmd].name, len);
>
>      if (mig_cmd_args[cmd].len != -1 && mig_cmd_args[cmd].len != len) {
> -        error_report("%s received with bad length - expecting %zu, got
> %d",
> -                     mig_cmd_args[cmd].name,
> -                     (size_t)mig_cmd_args[cmd].len, len);
> +        error_setg(errp, "%s received with bad length - expecting %zu,
> got %d",
> +                   mig_cmd_args[cmd].name,
> +                   (size_t)mig_cmd_args[cmd].len, len);
>          return -ERANGE;
>      }
>
> @@ -2594,11 +2595,10 @@ static int loadvm_process_command(QEMUFile *f)
>           * been created.
>           */
>          if (migrate_switchover_ack() && !mis->switchover_ack_pending_num)
> {
> -            int ret = migrate_send_rp_switchover_ack(mis);
> +            ret = migrate_send_rp_switchover_ack(mis);
>              if (ret) {
> -                error_report(
> -                    "Could not send switchover ack RP MSG, err %d (%s)",
> ret,
> -                    strerror(-ret));
> +                error_setg_errno(errp, -ret,
> +                                 "Could not send switchover ack RP MSG");
>                  return ret;
>              }
>          }
> @@ -2608,39 +2608,71 @@ static int loadvm_process_command(QEMUFile *f)
>          tmp32 = qemu_get_be32(f);
>          trace_loadvm_process_command_ping(tmp32);
>          if (!mis->to_src_file) {
> -            error_report("CMD_PING (0x%x) received with no return path",
> -                         tmp32);
> +            error_setg(errp, "CMD_PING (0x%x) received with no return
> path",
> +                       tmp32);
>              return -1;
>          }
>          migrate_send_rp_pong(mis, tmp32);
>          break;
>
>      case MIG_CMD_PACKAGED:
> -        return loadvm_handle_cmd_packaged(mis);
> +        ret = loadvm_handle_cmd_packaged(mis);
> +        if (ret < 0) {
> +            error_setg(errp, "Failed to load device state command: %d",
> ret);
> +        }
> +        return ret;
>
>      case MIG_CMD_POSTCOPY_ADVISE:
> -        return loadvm_postcopy_handle_advise(mis, len);
> +        ret = loadvm_postcopy_handle_advise(mis, len);
> +        if (ret < 0) {
> +            error_setg(errp, "Failed to load device state command: %d",
> ret);
> +        }
> +        return ret;
>
>      case MIG_CMD_POSTCOPY_LISTEN:
> -        return loadvm_postcopy_handle_listen(mis);
> +        ret = loadvm_postcopy_handle_listen(mis);
> +        if (ret < 0) {
> +            error_setg(errp, "Failed to load device state command: %d",
> ret);
> +        }
> +        return ret;
>
>      case MIG_CMD_POSTCOPY_RUN:
> -        return loadvm_postcopy_handle_run(mis);
> +        ret = loadvm_postcopy_handle_run(mis);
> +        if (ret < 0) {
> +            error_setg(errp, "Failed to load device state command: %d",
> ret);
> +        }
> +        return ret;
>
>      case MIG_CMD_POSTCOPY_RAM_DISCARD:
> -        return loadvm_postcopy_ram_handle_discard(mis, len);
> +        ret = loadvm_postcopy_ram_handle_discard(mis, len);
> +        if (ret < 0) {
> +            error_setg(errp, "Failed to load device state command: %d",
> ret);
> +        }
> +        return ret;
>
>      case MIG_CMD_POSTCOPY_RESUME:
>          return loadvm_postcopy_handle_resume(mis);
>
>      case MIG_CMD_RECV_BITMAP:
> -        return loadvm_handle_recv_bitmap(mis, len);
> +        ret = loadvm_handle_recv_bitmap(mis, len);
> +        if (ret < 0) {
> +            error_setg(errp, "Failed to load device state command: %d",
> ret);
> +        }
> +        return ret;
>
>      case MIG_CMD_ENABLE_COLO:
> -        return loadvm_process_enable_colo(mis);
> +        ret = loadvm_process_enable_colo(mis);
> +        if (ret < 0) {
> +            error_setg(errp, "Failed to load device state command: %d",
> ret);
> +        }
> +        return ret;
>
>      case MIG_CMD_SWITCHOVER_START:
> -        return loadvm_postcopy_handle_switchover_start();
> +        ret = loadvm_postcopy_handle_switchover_start();
> +        if (ret < 0) {
> +            error_setg(errp, "Failed to load device state command: %d",
> ret);
> +        }
> +        return ret;
>      }
>
>      return 0;
> @@ -3074,7 +3106,7 @@ retry:
>              }
>              break;
>          case QEMU_VM_COMMAND:
> -            ret = loadvm_process_command(f);
> +            ret = loadvm_process_command(f, NULL);
>

The function used to error_report(), you should pass &error_warn to keep
reporting.


>              trace_qemu_loadvm_state_section_command(ret);
>              if ((ret < 0) || (ret == LOADVM_QUIT)) {
>                  goto out;
>
> --
> 2.50.1
>
>

[-- Attachment #2: Type: text/html, Size: 8249 bytes --]

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

* Re: [PATCH v9 02/27] migration: push Error **errp into vmstate_load_state()
  2025-08-05 18:25 ` [PATCH v9 02/27] migration: push Error **errp into vmstate_load_state() Arun Menon
@ 2025-08-06  8:31   ` Marc-André Lureau
  2025-08-06  9:47     ` Arun Menon
  2025-08-10  4:47   ` Akihiko Odaki
  1 sibling, 1 reply; 63+ messages in thread
From: Marc-André Lureau @ 2025-08-06  8:31 UTC (permalink / raw)
  To: Arun Menon
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

[-- Attachment #1: Type: text/plain, Size: 23449 bytes --]

Hi

On Tue, Aug 5, 2025 at 10:28 PM Arun Menon <armenon@redhat.com> wrote:

> This is an incremental step in converting vmstate loading
> code to report error via Error objects instead of directly
> printing it to console/monitor.
> It is ensured that vmstate_load_state() must report an error
> in errp, in case of failure.
>
> In cases where we do not want to essentially set the error
> in errp object and also not abort/exit, the caller only
> passes &error_warn, to warn on error.
>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Signed-off-by: Arun Menon <armenon@redhat.com>
> ---
>  hw/display/virtio-gpu.c     |  2 +-
>  hw/pci/pci.c                |  3 ++-
>  hw/s390x/virtio-ccw.c       |  2 +-
>  hw/scsi/spapr_vscsi.c       |  2 +-
>  hw/vfio/pci.c               |  2 +-
>  hw/virtio/virtio-mmio.c     |  3 ++-
>  hw/virtio/virtio-pci.c      |  2 +-
>  hw/virtio/virtio.c          |  4 ++--
>  include/migration/vmstate.h |  2 +-
>  migration/cpr.c             |  5 ++--
>  migration/savevm.c          |  6 +++--
>  migration/vmstate-types.c   | 11 +++++----
>  migration/vmstate.c         | 56
> ++++++++++++++++++++++++++++++---------------
>  tests/unit/test-vmstate.c   | 20 ++++++++--------
>  ui/vdagent.c                |  2 +-
>  15 files changed, 75 insertions(+), 47 deletions(-)
>
> diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
> index
> 0a1a625b0ea6cf26cb0d799171a57ed3d3ab2442..5ff9f5e6dc0b6b112e36170318c900f893a73c5e
> 100644
> --- a/hw/display/virtio-gpu.c
> +++ b/hw/display/virtio-gpu.c
> @@ -1343,7 +1343,7 @@ 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);
> +    vmstate_load_state(f, &vmstate_virtio_gpu_scanouts, g, 1,
> &error_warn);
>
>      return 0;
>  }
> diff --git a/hw/pci/pci.c b/hw/pci/pci.c
> index
> c70b5ceebaf1f2b10768bd030526cbb518da2b8d..80545189980f176ca6a3dc9abce7043c8bc2708c
> 100644
> --- a/hw/pci/pci.c
> +++ b/hw/pci/pci.c
> @@ -934,7 +934,8 @@ void pci_device_save(PCIDevice *s, QEMUFile *f)
>  int pci_device_load(PCIDevice *s, QEMUFile *f)
>  {
>      int ret;
> -    ret = vmstate_load_state(f, &vmstate_pci_device, s, s->version_id);
> +    ret = vmstate_load_state(f, &vmstate_pci_device, s, s->version_id,
> +                             &error_warn);
>      /* 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
> d2f85b39f30f7fc82e0c600144c0a958e1269b2c..bafcb9b76d81749925e31b5a0a1320b3332ad2cf
> 100644
> --- a/hw/s390x/virtio-ccw.c
> +++ b/hw/s390x/virtio-ccw.c
> @@ -1136,7 +1136,7 @@ static void virtio_ccw_save_config(DeviceState *d,
> QEMUFile *f)
>  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);
> +    return vmstate_load_state(f, &vmstate_virtio_ccw_dev, dev, 1,
> &error_warn);
>  }
>
>  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
> 20f70fb2729de78b9636a6b8c869695dab4f8902..a4812afd0cc7c495080ef03a531c7d279af29b33
> 100644
> --- a/hw/scsi/spapr_vscsi.c
> +++ b/hw/scsi/spapr_vscsi.c
> @@ -648,7 +648,7 @@ static void *vscsi_load_request(QEMUFile *f,
> SCSIRequest *sreq)
>      assert(!req->active);
>
>      memset(req, 0, sizeof(*req));
> -    rc = vmstate_load_state(f, &vmstate_spapr_vscsi_req, req, 1);
> +    rc = vmstate_load_state(f, &vmstate_spapr_vscsi_req, req, 1,
> &error_warn);
>      if (rc) {
>          fprintf(stderr, "VSCSI: failed loading request tag#%u\n",
> sreq->tag);
>          return NULL;
> diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
> index
> 4fa692c1a32bcfa4e4939e5fcb64f2bf19905b3b..04d385d7674f444844beeee7364ee0424f762758
> 100644
> --- a/hw/vfio/pci.c
> +++ b/hw/vfio/pci.c
> @@ -2795,7 +2795,7 @@ static int vfio_pci_load_config(VFIODevice
> *vbasedev, QEMUFile *f)
>          old_addr[bar] = pdev->io_regions[bar].addr;
>      }
>
> -    ret = vmstate_load_state(f, &vmstate_vfio_pci_config, vdev, 1);
> +    ret = vmstate_load_state(f, &vmstate_vfio_pci_config, vdev, 1,
> &error_warn);
>      if (ret) {
>          return ret;
>      }
> diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c
> index
> 532c67107ba1d2978a76cf49f9cdc1de1dea3e11..f14f0487fec3ed30d2cc3aec96950c2121265716
> 100644
> --- a/hw/virtio/virtio-mmio.c
> +++ b/hw/virtio/virtio-mmio.c
> @@ -34,6 +34,7 @@
>  #include "qemu/error-report.h"
>  #include "qemu/log.h"
>  #include "trace.h"
> +#include "qapi/error.h"
>
>  static bool virtio_mmio_ioeventfd_enabled(DeviceState *d)
>  {
> @@ -619,7 +620,7 @@ static int virtio_mmio_load_extra_state(DeviceState
> *opaque, QEMUFile *f)
>  {
>      VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
>
> -    return vmstate_load_state(f, &vmstate_virtio_mmio, proxy, 1);
> +    return vmstate_load_state(f, &vmstate_virtio_mmio, proxy, 1,
> &error_warn);
>  }
>
>  static bool virtio_mmio_has_extra_state(DeviceState *opaque)
> diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
> index
> 767216d795998708f5716a23ae16c79cd90ff489..07f144d6d1bfbb561b28015de71762601080dc23
> 100644
> --- a/hw/virtio/virtio-pci.c
> +++ b/hw/virtio/virtio-pci.c
> @@ -161,7 +161,7 @@ static int virtio_pci_load_extra_state(DeviceState *d,
> QEMUFile *f)
>  {
>      VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
>
> -    return vmstate_load_state(f, &vmstate_virtio_pci, proxy, 1);
> +    return vmstate_load_state(f, &vmstate_virtio_pci, proxy, 1,
> &error_warn);
>  }
>
>  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
> 2ab1d20769495ea39445b87e3673b076ad172510..aac317a93c3cd55ece9b0fd858c7b2459f8242f9
> 100644
> --- a/hw/virtio/virtio.c
> +++ b/hw/virtio/virtio.c
> @@ -3317,14 +3317,14 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int
> version_id)
>      }
>
>      if (vdc->vmsd) {
> -        ret = vmstate_load_state(f, vdc->vmsd, vdev, version_id);
> +        ret = vmstate_load_state(f, vdc->vmsd, vdev, version_id,
> &error_warn);
>          if (ret) {
>              return ret;
>          }
>      }
>
>      /* Subsections */
> -    ret = vmstate_load_state(f, &vmstate_virtio, vdev, 1);
> +    ret = vmstate_load_state(f, &vmstate_virtio, vdev, 1, &error_warn);
>      if (ret) {
>          return ret;
>      }
> diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
> index
> 1ff7bd9ac425ba67cd5ca7ad97bcf570f9e19abe..056781b1c21e737583f081594d9f88b32adfd674
> 100644
> --- a/include/migration/vmstate.h
> +++ b/include/migration/vmstate.h
> @@ -1196,7 +1196,7 @@ extern const VMStateInfo vmstate_info_qlist;
>      }
>
>  int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
> -                       void *opaque, int version_id);
> +                       void *opaque, int version_id, Error **errp);
>  int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
>                         void *opaque, JSONWriter *vmdesc);
>  int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription
> *vmsd,
> diff --git a/migration/cpr.c b/migration/cpr.c
> index
> 42ad0b0d500e5de57faf0c6517e216b2d1c0cacf..bdb24736f44e91ba59b6e622a315597c97e7f64d
> 100644
> --- a/migration/cpr.c
> +++ b/migration/cpr.c
> @@ -202,6 +202,7 @@ int cpr_state_save(MigrationChannel *channel, Error
> **errp)
>
>  int cpr_state_load(MigrationChannel *channel, Error **errp)
>  {
> +    ERRP_GUARD();
>      int ret;
>      uint32_t v;
>      QEMUFile *f;
> @@ -233,9 +234,9 @@ int cpr_state_load(MigrationChannel *channel, Error
> **errp)
>          return -ENOTSUP;
>      }
>
> -    ret = vmstate_load_state(f, &vmstate_cpr_state, &cpr_state, 1);
> +    ret = vmstate_load_state(f, &vmstate_cpr_state, &cpr_state, 1, errp);
>      if (ret) {
> -        error_setg(errp, "vmstate_load_state error %d", ret);
> +        error_prepend(errp, "vmstate_load_state error %d: ", ret);
>          qemu_fclose(f);
>          return ret;
>      }
> diff --git a/migration/savevm.c b/migration/savevm.c
> index
> fabbeb296ae987d0c06ba6dafda63720205fecfd..ab947620f724874f325fb9fb59bef50b7c16fb51
> 100644
> --- a/migration/savevm.c
> +++ b/migration/savevm.c
> @@ -969,7 +969,8 @@ static int vmstate_load(QEMUFile *f, SaveStateEntry
> *se)
>      if (!se->vmsd) {         /* Old style */
>          return se->ops->load_state(f, se->opaque, se->load_version_id);
>      }
> -    return vmstate_load_state(f, se->vmsd, se->opaque,
> se->load_version_id);
> +    return vmstate_load_state(f, se->vmsd, se->opaque,
> se->load_version_id,
> +                              NULL);
>  }
>
>  static void vmstate_save_old_style(QEMUFile *f, SaveStateEntry *se,
> @@ -2839,7 +2840,8 @@ static int qemu_loadvm_state_header(QEMUFile *f)
>              error_report("Configuration section missing");
>              return -EINVAL;
>          }
> -        ret = vmstate_load_state(f, &vmstate_configuration,
> &savevm_state, 0);
> +        ret = vmstate_load_state(f, &vmstate_configuration,
> &savevm_state, 0,
> +                                 NULL);
>

Why not &error_warn in the above 2 calls? (even if it is changed in the
following patches)


>
>          if (ret) {
>              return ret;
> diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c
> index
> 741a588b7e18c6d37724b08a0101edc8bc74a0a5..e2020a733d0921d748b6f832a193e5de8d302d5f
> 100644
> --- a/migration/vmstate-types.c
> +++ b/migration/vmstate-types.c
> @@ -19,6 +19,7 @@
>  #include "qemu/error-report.h"
>  #include "qemu/queue.h"
>  #include "trace.h"
> +#include "qapi/error.h"
>
>  /* bool */
>
> @@ -549,7 +550,7 @@ static int get_tmp(QEMUFile *f, void *pv, size_t size,
>
>      /* Writes the parent field which is at the start of the tmp */
>      *(void **)tmp = pv;
> -    ret = vmstate_load_state(f, vmsd, tmp, version_id);
> +    ret = vmstate_load_state(f, vmsd, tmp, version_id, &error_warn);
>      g_free(tmp);
>      return ret;
>  }
> @@ -649,7 +650,7 @@ static int get_qtailq(QEMUFile *f, void *pv, size_t
> unused_size,
>
>      while (qemu_get_byte(f)) {
>          elm = g_malloc(size);
> -        ret = vmstate_load_state(f, vmsd, elm, version_id);
> +        ret = vmstate_load_state(f, vmsd, elm, version_id, &error_warn);
>          if (ret) {
>              return ret;
>          }
> @@ -803,7 +804,7 @@ static int get_gtree(QEMUFile *f, void *pv, size_t
> unused_size,
>              key = (void *)(uintptr_t)qemu_get_be64(f);
>          } else {
>              key = g_malloc0(key_size);
> -            ret = vmstate_load_state(f, key_vmsd, key, version_id);
> +            ret = vmstate_load_state(f, key_vmsd, key, version_id,
> &error_warn);
>              if (ret) {
>                  error_report("%s : failed to load %s (%d)",
>                               field->name, key_vmsd->name, ret);
> @@ -811,7 +812,7 @@ static int get_gtree(QEMUFile *f, void *pv, size_t
> unused_size,
>              }
>          }
>          val = g_malloc0(val_size);
> -        ret = vmstate_load_state(f, val_vmsd, val, version_id);
> +        ret = vmstate_load_state(f, val_vmsd, val, version_id,
> &error_warn);
>          if (ret) {
>              error_report("%s : failed to load %s (%d)",
>                           field->name, val_vmsd->name, ret);
> @@ -892,7 +893,7 @@ static int get_qlist(QEMUFile *f, void *pv, size_t
> unused_size,
>
>      while (qemu_get_byte(f)) {
>          elm = g_malloc(size);
> -        ret = vmstate_load_state(f, vmsd, elm, version_id);
> +        ret = vmstate_load_state(f, vmsd, elm, version_id, &error_warn);
>          if (ret) {
>              error_report("%s: failed to load %s (%d)", field->name,
>                           vmsd->name, ret);
> diff --git a/migration/vmstate.c b/migration/vmstate.c
> index
> 24451b054c11dfca2d76e24b053d604bb7184e1c..60ff38858cf54277992fa5eddeadb6f3d70edec3
> 100644
> --- a/migration/vmstate.c
> +++ b/migration/vmstate.c
> @@ -132,29 +132,33 @@ static void vmstate_handle_alloc(void *ptr, const
> VMStateField *field,
>  }
>
>  int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
> -                       void *opaque, int version_id)
> +                       void *opaque, int version_id, Error **errp)
>  {
>      const VMStateField *field = vmsd->fields;
>      int ret = 0;
>
>      trace_vmstate_load_state(vmsd->name, version_id);
>      if (version_id > vmsd->version_id) {
> -        error_report("%s: incoming version_id %d is too new "
> -                     "for local version_id %d",
> -                     vmsd->name, version_id, vmsd->version_id);
> +        error_setg(errp, "%s: incoming version_id %d is too new "
> +                   "for local version_id %d",
> +                   vmsd->name, version_id, vmsd->version_id);
>          trace_vmstate_load_state_end(vmsd->name, "too new", -EINVAL);
>          return -EINVAL;
>      }
>      if  (version_id < vmsd->minimum_version_id) {
> -        error_report("%s: incoming version_id %d is too old "
> -                     "for local minimum version_id  %d",
> -                     vmsd->name, version_id, vmsd->minimum_version_id);
> +        error_setg(errp, "%s: incoming version_id %d is too old "
> +                   "for local minimum version_id %d",
> +                   vmsd->name, version_id, vmsd->minimum_version_id);
>          trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL);
>          return -EINVAL;
>      }
>      if (vmsd->pre_load) {
>          ret = vmsd->pre_load(opaque);
>          if (ret) {
> +            error_setg(errp, "VM pre load failed for: '%s', "
> +                       "version_id: %d, minimum version_id: %d, ret: %d",
> +                       vmsd->name, vmsd->version_id,
> vmsd->minimum_version_id,
> +                       ret);
>              return ret;
>          }
>      }
> @@ -192,10 +196,12 @@ int vmstate_load_state(QEMUFile *f, const
> VMStateDescription *vmsd,
>
>                  if (inner_field->flags & VMS_STRUCT) {
>                      ret = vmstate_load_state(f, inner_field->vmsd,
> curr_elem,
> -
>  inner_field->vmsd->version_id);
> +
>  inner_field->vmsd->version_id,
> +                                             errp);
>                  } else if (inner_field->flags & VMS_VSTRUCT) {
>                      ret = vmstate_load_state(f, inner_field->vmsd,
> curr_elem,
> -
>  inner_field->struct_version_id);
> +
>  inner_field->struct_version_id,
> +                                             errp);
>                  } else {
>                      ret = inner_field->info->get(f, curr_elem, size,
>                                                   inner_field);
> @@ -208,30 +214,43 @@ int vmstate_load_state(QEMUFile *f, const
> VMStateDescription *vmsd,
>
>                  if (ret >= 0) {
>                      ret = qemu_file_get_error(f);
> +                    if (ret < 0) {
> +                        error_setg(errp, "Failed to load %s state: %d",
> +                                   vmsd->name, ret);
> +                        return ret;
> +                    }
>                  }
>                  if (ret < 0) {
>                      qemu_file_set_error(f, ret);
> -                    error_report("Failed to load %s:%s", vmsd->name,
> -                                 field->name);
> +                    error_setg(errp,
> +                               "Failed to load %s:%s version_id: %d: ",
> +                               vmsd->name, field->name, vmsd->version_id);
>                      trace_vmstate_load_field_error(field->name, ret);
>                      return ret;
>                  }
>              }
>          } else if (field->flags & VMS_MUST_EXIST) {
> -            error_report("Input validation failed: %s/%s",
> -                         vmsd->name, field->name);
> +            error_setg(errp, "Input validation failed: %s/%s version_id:
> %d",
> +                       vmsd->name, field->name, vmsd->version_id);
>              return -1;
>          }
>          field++;
>      }
>      assert(field->flags == VMS_END);
> -    ret = vmstate_subsection_load(f, vmsd, opaque, NULL);
> +    ret = vmstate_subsection_load(f, vmsd, opaque, errp);
>      if (ret != 0) {
>          qemu_file_set_error(f, ret);
>          return ret;
>      }
>      if (vmsd->post_load) {
>          ret = vmsd->post_load(opaque, version_id);
> +        if (ret < 0) {
> +            error_setg(errp,
> +                       "VM Post load failed for: %s, version_id: %d, "
> +                       "minimum_version: %d, ret: %d",
> +                       vmsd->name, vmsd->version_id,
> vmsd->minimum_version_id,
> +                       ret);
> +        }
>      }
>      trace_vmstate_load_state_end(vmsd->name, "end", ret);
>      return ret;
> @@ -568,6 +587,7 @@ vmstate_get_subsection(const VMStateDescription *
> const *sub,
>  static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription
> *vmsd,
>                                     void *opaque, Error **errp)
>  {
> +    ERRP_GUARD();
>      trace_vmstate_subsection_load(vmsd->name);
>
>      while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) {
> @@ -607,12 +627,12 @@ static int vmstate_subsection_load(QEMUFile *f,
> const VMStateDescription *vmsd,
>          qemu_file_skip(f, len); /* idstr */
>          version_id = qemu_get_be32(f);
>
> -        ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
> +        ret = vmstate_load_state(f, sub_vmsd, opaque, version_id, errp);
>          if (ret) {
>              trace_vmstate_subsection_load_bad(vmsd->name, idstr,
> "(child)");
> -            error_setg(errp,
> -                       "Loading VM subsection '%s' in '%s' failed: %d",
> -                       idstr, vmsd->name, ret);
> +            error_prepend(errp,
> +                          "Loading VM subsection '%s' in '%s' failed: %d:
> ",
> +                          idstr, vmsd->name, ret);
>              return ret;
>          }
>      }
> diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c
> index
> 63f28f26f45691a70936d33e7341d16477a3471f..807deb3531f3079864ac99567d4dece7122571dd
> 100644
> --- a/tests/unit/test-vmstate.c
> +++ b/tests/unit/test-vmstate.c
> @@ -30,6 +30,7 @@
>  #include "../migration/savevm.h"
>  #include "qemu/module.h"
>  #include "io/channel-file.h"
> +#include "qapi/error.h"
>
>  static int temp_fd;
>
> @@ -114,7 +115,7 @@ static int load_vmstate_one(const VMStateDescription
> *desc, void *obj,
>      qemu_fclose(f);
>
>      f = open_test_file(false);
> -    ret = vmstate_load_state(f, desc, obj, version);
> +    ret = vmstate_load_state(f, desc, obj, version, &error_warn);
>      if (ret) {
>          g_assert(qemu_file_get_error(f));
>      } else{
> @@ -365,7 +366,7 @@ static void test_load_v1(void)
>
>      QEMUFile *loading = open_test_file(false);
>      TestStruct obj = { .b = 200, .e = 500, .f = 600 };
> -    vmstate_load_state(loading, &vmstate_versioned, &obj, 1);
> +    vmstate_load_state(loading, &vmstate_versioned, &obj, 1, &error_warn);
>      g_assert(!qemu_file_get_error(loading));
>      g_assert_cmpint(obj.a, ==, 10);
>      g_assert_cmpint(obj.b, ==, 200);
> @@ -391,7 +392,7 @@ static void test_load_v2(void)
>
>      QEMUFile *loading = open_test_file(false);
>      TestStruct obj;
> -    vmstate_load_state(loading, &vmstate_versioned, &obj, 2);
> +    vmstate_load_state(loading, &vmstate_versioned, &obj, 2, &error_warn);
>      g_assert_cmpint(obj.a, ==, 10);
>      g_assert_cmpint(obj.b, ==, 20);
>      g_assert_cmpint(obj.c, ==, 30);
> @@ -480,7 +481,7 @@ static void test_load_noskip(void)
>
>      QEMUFile *loading = open_test_file(false);
>      TestStruct obj = { .skip_c_e = false };
> -    vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
> +    vmstate_load_state(loading, &vmstate_skipping, &obj, 2, &error_warn);
>      g_assert(!qemu_file_get_error(loading));
>      g_assert_cmpint(obj.a, ==, 10);
>      g_assert_cmpint(obj.b, ==, 20);
> @@ -504,7 +505,7 @@ static void test_load_skip(void)
>
>      QEMUFile *loading = open_test_file(false);
>      TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 };
> -    vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
> +    vmstate_load_state(loading, &vmstate_skipping, &obj, 2, &error_warn);
>      g_assert(!qemu_file_get_error(loading));
>      g_assert_cmpint(obj.a, ==, 10);
>      g_assert_cmpint(obj.b, ==, 20);
> @@ -773,7 +774,7 @@ static void test_load_q(void)
>      TestQtailq tgt;
>
>      QTAILQ_INIT(&tgt.q);
> -    vmstate_load_state(fload, &vmstate_q, &tgt, 1);
> +    vmstate_load_state(fload, &vmstate_q, &tgt, 1, &error_warn);
>      char eof = qemu_get_byte(fload);
>      g_assert(!qemu_file_get_error(fload));
>      g_assert_cmpint(tgt.i16, ==, obj_q.i16);
> @@ -1127,7 +1128,7 @@ static void test_gtree_load_domain(void)
>
>      fload = open_test_file(false);
>
> -    vmstate_load_state(fload, &vmstate_domain, dest_domain, 1);
> +    vmstate_load_state(fload, &vmstate_domain, dest_domain, 1,
> &error_warn);
>      eof = qemu_get_byte(fload);
>      g_assert(!qemu_file_get_error(fload));
>      g_assert_cmpint(orig_domain->id, ==, dest_domain->id);
> @@ -1241,7 +1242,7 @@ static void test_gtree_load_iommu(void)
>      qemu_fclose(fsave);
>
>      fload = open_test_file(false);
> -    vmstate_load_state(fload, &vmstate_iommu, dest_iommu, 1);
> +    vmstate_load_state(fload, &vmstate_iommu, dest_iommu, 1, &error_warn);
>      eof = qemu_get_byte(fload);
>      g_assert(!qemu_file_get_error(fload));
>      g_assert_cmpint(orig_iommu->id, ==, dest_iommu->id);
> @@ -1376,7 +1377,8 @@ static void test_load_qlist(void)
>      qemu_fclose(fsave);
>
>      fload = open_test_file(false);
> -    vmstate_load_state(fload, &vmstate_container, dest_container, 1);
> +    vmstate_load_state(fload, &vmstate_container, dest_container, 1,
> +                       &error_warn);
>      eof = qemu_get_byte(fload);
>      g_assert(!qemu_file_get_error(fload));
>      g_assert_cmpint(eof, ==, QEMU_VM_EOF);
> diff --git a/ui/vdagent.c b/ui/vdagent.c
> index
> c0746fe5b168fdc7aeb4866de2ba0c3387566649..b9a22a689d9acfeafb862ae73308db6fbd52331e
> 100644
> --- a/ui/vdagent.c
> +++ b/ui/vdagent.c
> @@ -1008,7 +1008,7 @@ static int get_cbinfo(QEMUFile *f, void *pv, size_t
> size,
>
>      vdagent_clipboard_peer_register(vd);
>
> -    ret = vmstate_load_state(f, &vmstate_cbinfo_array, &cbinfo, 0);
> +    ret = vmstate_load_state(f, &vmstate_cbinfo_array, &cbinfo, 0,
> &error_warn);
>      if (ret) {
>          return ret;
>      }
>
> --
> 2.50.1
>
>

[-- Attachment #2: Type: text/html, Size: 28013 bytes --]

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

* Re: [PATCH v9 23/27] migration: Refactor vmstate_save_state_v() function
  2025-08-06  8:19   ` Marc-André Lureau
@ 2025-08-06  9:45     ` Arun Menon
  0 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-06  9:45 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

Hi Marc-André,
Thanks for the review.

On Wed, Aug 06, 2025 at 12:19:29PM +0400, Marc-André Lureau wrote:
> Hi
> 
> On Tue, Aug 5, 2025 at 10:31 PM Arun Menon <armenon@redhat.com> wrote:
> 
> > The original vmstate_save_state_v() function combined multiple
> > responsibilities like calling pre-save hooks, saving the state of
> > the device, handling subsection saves and invoking post-save hooks.
> > This led to a lengthy and less readable function.
> >
> > To address this, introduce wrapper functions for pre-save,
> > post-save and the device-state save functionalities.
> >
> > Signed-off-by: Arun Menon <armenon@redhat.com>
> > ---
> >  migration/vmstate.c | 78
> > ++++++++++++++++++++++++++++++++++++++++-------------
> >  1 file changed, 60 insertions(+), 18 deletions(-)
> >
> > diff --git a/migration/vmstate.c b/migration/vmstate.c
> > index
> > 60ff38858cf54277992fa5eddeadb6f3d70edec3..fbc59caadbbcc75fe6de27b459aa9aa25e76aa0a
> > 100644
> > --- a/migration/vmstate.c
> > +++ b/migration/vmstate.c
> > @@ -414,22 +414,43 @@ int vmstate_save_state_with_err(QEMUFile *f, const
> > VMStateDescription *vmsd,
> >      return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id,
> > vmsd->version_id, errp);
> >  }
> >
> > -int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
> > -                         void *opaque, JSONWriter *vmdesc, int
> > version_id, Error **errp)
> > +static int pre_save_dispatch(const VMStateDescription *vmsd, void *opaque,
> > +                             Error **errp)
> >  {
> >      int ret = 0;
> > -    const VMStateField *field = vmsd->fields;
> > -
> > -    trace_vmstate_save_state_top(vmsd->name);
> > -
> >      if (vmsd->pre_save) {
> >          ret = vmsd->pre_save(opaque);
> >          trace_vmstate_save_state_pre_save_res(vmsd->name, ret);
> >          if (ret) {
> > -            error_setg(errp, "pre-save failed: %s", vmsd->name);
> > -            return ret;
> > +            error_setg(errp, "pre-save for %s failed, ret: %d",
> > +                       vmsd->name, ret);
> >          }
> >      }
> > +    return ret;
> > +}
> > +
> > +static int post_save_dispatch(const VMStateDescription *vmsd, void
> > *opaque,
> > +                              Error **errp)
> > +{
> > +    int ret = 0;
> > +    if (vmsd->post_save) {
> > +        ret = vmsd->post_save(opaque);
> 
> +        error_setg(errp, "post-save for %s failed, ret: %d",
> > +                   vmsd->name, ret);
> >
> 
> Only set errp if ret != 0
> 
Yes, missed this one.
> 
> > +    }
> > +    return ret;
> > +}
> > +
> > +static int vmstate_save_dispatch(QEMUFile *f,
> > +                                 const VMStateDescription *vmsd,
> > +                                 void *opaque, JSONWriter *vmdesc,
> > +                                 int version_id, Error **errp)
> > +{
> > +    ERRP_GUARD();
> > +    int ret = 0;
> > +    int ps_ret = 0;
> > +    Error *local_err = NULL;
> > +    const VMStateField *field = vmsd->fields;
> >
> >      if (vmdesc) {
> >          json_writer_str(vmdesc, "vmsd_name", vmsd->name);
> > @@ -532,9 +553,7 @@ 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);
> > -                    if (vmsd->post_save) {
> > -                        vmsd->post_save(opaque);
> > -                    }
> > +                    ps_ret = post_save_dispatch(vmsd, opaque, &local_err);
> >
> 
> why keep ps_ret ?
It is not required.
> 
> What do you do of local_err ?
Should be freed. We do nothing with this. In the next patch it is propagated, and
previous error is dismissed. But that will no longer be the case if the behaviour of
post_save is is kept the same (from the discussion on changeing post_save() to cleanup_save())

> 
> 
> >                      return ret;
> >                  }
> >
> > @@ -557,16 +576,39 @@ int vmstate_save_state_v(QEMUFile *f, const
> > VMStateDescription *vmsd,
> >      if (vmdesc) {
> >          json_writer_end_array(vmdesc);
> >      }
> > +    return ret;
> > +}
> >
> > -    ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc, errp);
> >
> > -    if (vmsd->post_save) {
> > -        int ps_ret = vmsd->post_save(opaque);
> > -        if (!ret && ps_ret) {
> > -            ret = ps_ret;
> > -            error_setg(errp, "post-save failed: %s", vmsd->name);
> > -        }
> > +int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
> > +                         void *opaque, JSONWriter *vmdesc, int version_id,
> > +                         Error **errp)
> > +{
> > +    ERRP_GUARD();
> > +    int ret = 0;
> > +    Error *local_err = NULL;
> > +    int ps_ret = 0;
> > +
> > +    trace_vmstate_save_state_top(vmsd->name);
> > +
> > +    ret = pre_save_dispatch(vmsd, opaque, errp);
> > +    if (ret) {
> > +        return ret;
> > +    }
> > +
> > +    ret = vmstate_save_dispatch(f, vmsd, opaque, vmdesc,
> > +                                version_id, errp);
> > +    if (ret) {
> > +        return ret
> 
> 
> post_save_dispatch() should be called on failure.
> 
> >
> >      }
> > +
> > +    ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc, errp);
> >
> 
> Imho this should be moved back to the vmstate_save_dispatch()
> 
> +    ps_ret = post_save_dispatch(vmsd, opaque, &local_err);
> > +    if (!ret && ps_ret) {
> > +        ret = ps_ret;
> > +        error_setg(errp, "post-save failed: %s", vmsd->name);
> >
> 
> And then you can have a single place to call post_save_dispatch() - remove
> it from vmstate_subsection_save -> you mean vmstate_save_state_v()?
> 
> It should probably call error_propagate() instead.
Yes, we can move vmstate_subsection_save() and the subsequent call
to post_save() here.
Shall do, if we are planning to keep this behavior for post_save.
Otherwise we can just keep things as is, and just change post_save() return
value and naming.
> 
> 
> 
> > +    }
> > +
> >      return ret;
> >  }
> >
> >
> > --
> > 2.50.1
> >
> >

Regards,
Arun



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

* Re: [PATCH v9 25/27] migration: Remove error variant of vmstate_save_state() function
  2025-08-06  8:28   ` Marc-André Lureau
@ 2025-08-06  9:46     ` Arun Menon
  2025-08-06 10:01       ` Marc-André Lureau
  2025-08-06 11:16     ` Arun Menon
  1 sibling, 1 reply; 63+ messages in thread
From: Arun Menon @ 2025-08-06  9:46 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

Hi Marc-André,
Thanks for the review.

On Wed, Aug 06, 2025 at 12:28:54PM +0400, Marc-André Lureau wrote:
> Hi
> 
> On Tue, Aug 5, 2025 at 10:31 PM Arun Menon <armenon@redhat.com> wrote:
> 
> > This commit removes the redundant vmstate_save_state_with_err()
> > function.
> >
> > Previously, commit 969298f9d7 introduced vmstate_save_state_with_err()
> > to handle error propagation, while vmstate_save_state() existed for
> > non-error scenarios.
> > This is because there were code paths where vmstate_save_state_v()
> > (called internally by vmstate_save_state) did not explicitly set
> > errors on failure.
> >
> > This change unifies error handling by
> >  - updating vmstate_save_state() to accept an Error **errp argument.
> >  - vmstate_save_state_v() ensures errors are set directly within the errp
> >    object, eliminating the need for two separate functions. Specifically
> >    errors are handled by storing them in errp in vmstate_save_dispatch()
> >
> > All calls to vmstate_save_state_with_err() are replaced with
> > vmstate_save_state(). This simplifies the API and improves code
> > maintainability.
> >
> > Signed-off-by: Arun Menon <armenon@redhat.com>
> >
> 
> I realize that vmstate_save_state() callers progressively became silent on
> error with this series (switching to error_report to error_set). I suggest
> you make a preliminary patch to make vmstate_save_state() verbose by
> passing &error_warn to vmstate_save_state_v. wdyt?

I am sorry, I did not understand the suggestion completely.
vmstate_save_state() callers have always been silent before. Even before this series.
Because, by default NULL was passed in vmstate_save_state_v().

This commit in the series changes that by passing &error_warn to vmstate_save_state_v()
inside vmstate_save_state() and removing the need for alternate vmstate_save_state_with_err().

Do you mean that we need to pass &error_warn to vmstate_save_state_v() in vmstate_save_state()
as a preliminary patch before this patch? Please correct me if I am wrong.

> 
> patch lgtm otherwise
> 
> 
> 
> 
> > ---
> >  hw/display/virtio-gpu.c     |  3 ++-
> >  hw/pci/pci.c                |  2 +-
> >  hw/s390x/virtio-ccw.c       |  2 +-
> >  hw/scsi/spapr_vscsi.c       |  2 +-
> >  hw/vfio/pci.c               |  4 ++--
> >  hw/virtio/virtio-mmio.c     |  2 +-
> >  hw/virtio/virtio-pci.c      |  2 +-
> >  hw/virtio/virtio.c          |  4 ++--
> >  include/migration/vmstate.h |  2 --
> >  migration/cpr.c             |  5 +++--
> >  migration/savevm.c          |  7 ++++---
> >  migration/vmstate-types.c   | 12 +++++++-----
> >  migration/vmstate.c         | 18 ++++++++----------
> >  tests/unit/test-vmstate.c   |  8 +++++---
> >  ui/vdagent.c                |  3 ++-
> >  15 files changed, 40 insertions(+), 36 deletions(-)
> >
> > diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
> > index
> > 5ff9f5e6dc0b6b112e36170318c900f893a73c5e..bddb26fa21312e5d0b2b6412cdf2a6c28dd06eb3
> > 100644
> > --- a/hw/display/virtio-gpu.c
> > +++ b/hw/display/virtio-gpu.c
> > @@ -1246,7 +1246,8 @@ 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);
> > +    return vmstate_save_state(f, &vmstate_virtio_gpu_scanouts, g, NULL,
> > +                              &error_warn);
> >  }
> >
> >  static bool virtio_gpu_load_restore_mapping(VirtIOGPU *g,
> > diff --git a/hw/pci/pci.c b/hw/pci/pci.c
> > index
> > 80545189980f176ca6a3dc9abce7043c8bc2708c..f7fb3931ff190c25ffa665ba80abe7737dbefdb6
> > 100644
> > --- a/hw/pci/pci.c
> > +++ b/hw/pci/pci.c
> > @@ -926,7 +926,7 @@ void pci_device_save(PCIDevice *s, QEMUFile *f)
> >       * 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);
> > +    vmstate_save_state(f, &vmstate_pci_device, s, NULL, &error_warn);
> >      /* Restore the interrupt status bit. */
> >      pci_update_irq_status(s);
> >  }
> > diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
> > index
> > bafcb9b76d81749925e31b5a0a1320b3332ad2cf..14304fd3e8901d28a7788266df2c7ef7334b691c
> > 100644
> > --- a/hw/s390x/virtio-ccw.c
> > +++ b/hw/s390x/virtio-ccw.c
> > @@ -1130,7 +1130,7 @@ 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);
> > +    vmstate_save_state(f, &vmstate_virtio_ccw_dev, dev, NULL,
> > &error_warn);
> >  }
> >
> >  static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
> > diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c
> > index
> > a4812afd0cc7c495080ef03a531c7d279af29b33..2204beb40c7cdc827db2bf86fb8612d8c09999ed
> > 100644
> > --- a/hw/scsi/spapr_vscsi.c
> > +++ b/hw/scsi/spapr_vscsi.c
> > @@ -630,7 +630,7 @@ static void vscsi_save_request(QEMUFile *f,
> > SCSIRequest *sreq)
> >      vscsi_req *req = sreq->hba_private;
> >      assert(req->active);
> >
> > -    vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL);
> > +    vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL,
> > &error_warn);
> >
> >      trace_spapr_vscsi_save_request(req->qtag, req->cur_desc_num,
> >                                     req->cur_desc_offset);
> > diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
> > index
> > 04d385d7674f444844beeee7364ee0424f762758..8e2b6d611de8efdf98235355226f5e5a6fd87b19
> > 100644
> > --- a/hw/vfio/pci.c
> > +++ b/hw/vfio/pci.c
> > @@ -2780,8 +2780,8 @@ static int vfio_pci_save_config(VFIODevice
> > *vbasedev, QEMUFile *f, Error **errp)
> >  {
> >      VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev);
> >
> > -    return vmstate_save_state_with_err(f, &vmstate_vfio_pci_config, vdev,
> > NULL,
> > -                                       errp);
> > +    return vmstate_save_state(f, &vmstate_vfio_pci_config, vdev, NULL,
> > +                              errp);
> >  }
> >
> >  static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f)
> > diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c
> > index
> > f14f0487fec3ed30d2cc3aec96950c2121265716..58957990743f061e78d0eafcfcb6cc5989252edc
> > 100644
> > --- a/hw/virtio/virtio-mmio.c
> > +++ b/hw/virtio/virtio-mmio.c
> > @@ -613,7 +613,7 @@ static void virtio_mmio_save_extra_state(DeviceState
> > *opaque, QEMUFile *f)
> >  {
> >      VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
> >
> > -    vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL);
> > +    vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL, &error_warn);
> >  }
> >
> >  static int virtio_mmio_load_extra_state(DeviceState *opaque, QEMUFile *f)
> > diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
> > index
> > 07f144d6d1bfbb561b28015de71762601080dc23..0826399cdfbf09b5b3f9acd9967de3417858c9d2
> > 100644
> > --- a/hw/virtio/virtio-pci.c
> > +++ b/hw/virtio/virtio-pci.c
> > @@ -154,7 +154,7 @@ static void virtio_pci_save_extra_state(DeviceState
> > *d, QEMUFile *f)
> >  {
> >      VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
> >
> > -    vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL);
> > +    vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL, &error_warn);
> >  }
> >
> >  static int virtio_pci_load_extra_state(DeviceState *d, QEMUFile *f)
> > diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
> > index
> > aac317a93c3cd55ece9b0fd858c7b2459f8242f9..552e167529e9c22fb4cba2b445ffad85fef619f3
> > 100644
> > --- a/hw/virtio/virtio.c
> > +++ b/hw/virtio/virtio.c
> > @@ -3025,14 +3025,14 @@ int virtio_save(VirtIODevice *vdev, QEMUFile *f)
> >      }
> >
> >      if (vdc->vmsd) {
> > -        int ret = vmstate_save_state(f, vdc->vmsd, vdev, NULL);
> > +        int ret = vmstate_save_state(f, vdc->vmsd, vdev, NULL,
> > &error_warn);
> >          if (ret) {
> >              return ret;
> >          }
> >      }
> >
> >      /* Subsections */
> > -    return vmstate_save_state(f, &vmstate_virtio, vdev, NULL);
> > +    return vmstate_save_state(f, &vmstate_virtio, vdev, NULL,
> > &error_warn);
> >  }
> >
> >  /* A wrapper for use as a VMState .put function */
> > diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
> > index
> > 056781b1c21e737583f081594d9f88b32adfd674..5fe9bbf39058d0cf97c1adab54cc516dbe8dc32a
> > 100644
> > --- a/include/migration/vmstate.h
> > +++ b/include/migration/vmstate.h
> > @@ -1198,8 +1198,6 @@ extern const VMStateInfo vmstate_info_qlist;
> >  int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
> >                         void *opaque, int version_id, Error **errp);
> >  int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
> > -                       void *opaque, JSONWriter *vmdesc);
> > -int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription
> > *vmsd,
> >                         void *opaque, JSONWriter *vmdesc, Error **errp);
> >  int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
> >                           void *opaque, JSONWriter *vmdesc,
> > diff --git a/migration/cpr.c b/migration/cpr.c
> > index
> > bdb24736f44e91ba59b6e622a315597c97e7f64d..d48f013dac21b531e41a26f4bb33eefe32097757
> > 100644
> > --- a/migration/cpr.c
> > +++ b/migration/cpr.c
> > @@ -163,6 +163,7 @@ bool cpr_is_incoming(void)
> >
> >  int cpr_state_save(MigrationChannel *channel, Error **errp)
> >  {
> > +    ERRP_GUARD();
> >      int ret;
> >      QEMUFile *f;
> >      MigMode mode = migrate_mode();
> > @@ -182,9 +183,9 @@ int cpr_state_save(MigrationChannel *channel, Error
> > **errp)
> >      qemu_put_be32(f, QEMU_CPR_FILE_MAGIC);
> >      qemu_put_be32(f, QEMU_CPR_FILE_VERSION);
> >
> > -    ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0);
> > +    ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0, errp);
> >      if (ret) {
> > -        error_setg(errp, "vmstate_save_state error %d", ret);
> > +        error_prepend(errp, "vmstate_save_state error %d: ", ret);
> >          qemu_fclose(f);
> >          return ret;
> >      }
> > diff --git a/migration/savevm.c b/migration/savevm.c
> > index
> > 4df50fb5c674e56c0173c357c1e23367373e2114..3cb9c34efb22fdcb2b05cf1fefcb6fbd80cadca5
> > 100644
> > --- a/migration/savevm.c
> > +++ b/migration/savevm.c
> > @@ -1056,8 +1056,8 @@ static int vmstate_save(QEMUFile *f, SaveStateEntry
> > *se, JSONWriter *vmdesc,
> >      if (!se->vmsd) {
> >          vmstate_save_old_style(f, se, vmdesc);
> >      } else {
> > -        ret = vmstate_save_state_with_err(f, se->vmsd, se->opaque, vmdesc,
> > -                                          errp);
> > +        ret = vmstate_save_state(f, se->vmsd, se->opaque, vmdesc,
> > +                                 errp);
> >          if (ret) {
> >              return ret;
> >          }
> > @@ -1303,7 +1303,8 @@ void qemu_savevm_state_header(QEMUFile *f)
> >              json_writer_start_object(vmdesc, "configuration");
> >          }
> >
> > -        vmstate_save_state(f, &vmstate_configuration, &savevm_state,
> > vmdesc);
> > +        vmstate_save_state(f, &vmstate_configuration, &savevm_state,
> > vmdesc,
> > +                           &error_warn);
> >
> >          if (vmdesc) {
> >              json_writer_end_object(vmdesc);
> > diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c
> > index
> > e2020a733d0921d748b6f832a193e5de8d302d5f..2b88dc145ab32072f09845cee95b79aad7ac3cb2
> > 100644
> > --- a/migration/vmstate-types.c
> > +++ b/migration/vmstate-types.c
> > @@ -564,7 +564,7 @@ static int put_tmp(QEMUFile *f, void *pv, size_t size,
> >
> >      /* Writes the parent field which is at the start of the tmp */
> >      *(void **)tmp = pv;
> > -    ret = vmstate_save_state(f, vmsd, tmp, vmdesc);
> > +    ret = vmstate_save_state(f, vmsd, tmp, vmdesc, &error_warn);
> >      g_free(tmp);
> >
> >      return ret;
> > @@ -675,7 +675,7 @@ static int put_qtailq(QEMUFile *f, void *pv, size_t
> > unused_size,
> >
> >      QTAILQ_RAW_FOREACH(elm, pv, entry_offset) {
> >          qemu_put_byte(f, true);
> > -        ret = vmstate_save_state(f, vmsd, elm, vmdesc);
> > +        ret = vmstate_save_state(f, vmsd, elm, vmdesc, &error_warn);
> >          if (ret) {
> >              return ret;
> >          }
> > @@ -712,7 +712,8 @@ static gboolean put_gtree_elem(gpointer key, gpointer
> > value, gpointer data)
> >      if (!capsule->key_vmsd) {
> >          qemu_put_be64(f, (uint64_t)(uintptr_t)(key)); /* direct key */
> >      } else {
> > -        ret = vmstate_save_state(f, capsule->key_vmsd, key,
> > capsule->vmdesc);
> > +        ret = vmstate_save_state(f, capsule->key_vmsd, key,
> > capsule->vmdesc,
> > +                                 &error_warn);
> >          if (ret) {
> >              capsule->ret = ret;
> >              return true;
> > @@ -720,7 +721,8 @@ static gboolean put_gtree_elem(gpointer key, gpointer
> > value, gpointer data)
> >      }
> >
> >      /* put the data */
> > -    ret = vmstate_save_state(f, capsule->val_vmsd, value,
> > capsule->vmdesc);
> > +    ret = vmstate_save_state(f, capsule->val_vmsd, value, capsule->vmdesc,
> > +                             &error_warn);
> >      if (ret) {
> >          capsule->ret = ret;
> >          return true;
> > @@ -856,7 +858,7 @@ static int put_qlist(QEMUFile *f, void *pv, size_t
> > unused_size,
> >      trace_put_qlist(field->name, vmsd->name, vmsd->version_id);
> >      QLIST_RAW_FOREACH(elm, pv, entry_offset) {
> >          qemu_put_byte(f, true);
> > -        ret = vmstate_save_state(f, vmsd, elm, vmdesc);
> > +        ret = vmstate_save_state(f, vmsd, elm, vmdesc, &error_warn);
> >          if (ret) {
> >              error_report("%s: failed to save %s (%d)", field->name,
> >                           vmsd->name, ret);
> > diff --git a/migration/vmstate.c b/migration/vmstate.c
> > index
> > ef78a1e62ad92e9608de72d125da80ea496c8dd1..569e66ea983f833e6a0d651d2a751f34a64e8f5c
> > 100644
> > --- a/migration/vmstate.c
> > +++ b/migration/vmstate.c
> > @@ -403,12 +403,6 @@ bool vmstate_section_needed(const VMStateDescription
> > *vmsd, void *opaque)
> >
> >
> >  int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
> > -                       void *opaque, JSONWriter *vmdesc_id)
> > -{
> > -    return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id,
> > vmsd->version_id, NULL);
> > -}
> > -
> > -int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription
> > *vmsd,
> >                         void *opaque, JSONWriter *vmdesc_id, Error **errp)
> >  {
> >      return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id,
> > vmsd->version_id, errp);
> > @@ -530,7 +524,7 @@ static int vmstate_save_dispatch(QEMUFile *f,
> >
> >                  if (inner_field->flags & VMS_STRUCT) {
> >                      ret = vmstate_save_state(f, inner_field->vmsd,
> > -                                             curr_elem, vmdesc_loop);
> > +                                             curr_elem, vmdesc_loop,
> > errp);
> >                  } else if (inner_field->flags & VMS_VSTRUCT) {
> >                      ret = vmstate_save_state_v(f, inner_field->vmsd,
> >                                                 curr_elem, vmdesc_loop,
> > @@ -539,6 +533,10 @@ static int vmstate_save_dispatch(QEMUFile *f,
> >                  } else {
> >                      ret = inner_field->info->put(f, curr_elem, size,
> >                                                   inner_field,
> > vmdesc_loop);
> > +                    if (ret) {
> > +                        error_setg(errp, "Save of field %s failed",
> > +                                   inner_field->name);
> > +                    }
> >                  }
> >
> >                  written_bytes = qemu_file_transferred(f) - old_offset;
> > @@ -551,8 +549,8 @@ static int vmstate_save_dispatch(QEMUFile *f,
> >                  }
> >
> >                  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);
> >                      ps_ret = post_save_dispatch(vmsd, opaque, &local_err);
> >                      if (ps_ret) {
> >                          ret = ps_ret;
> > @@ -723,7 +721,7 @@ static int vmstate_subsection_save(QEMUFile *f, const
> > VMStateDescription *vmsd,
> >              qemu_put_byte(f, len);
> >              qemu_put_buffer(f, (uint8_t *)vmsdsub->name, len);
> >              qemu_put_be32(f, vmsdsub->version_id);
> > -            ret = vmstate_save_state_with_err(f, vmsdsub, opaque, vmdesc,
> > errp);
> > +            ret = vmstate_save_state(f, vmsdsub, opaque, vmdesc, errp);
> >              if (ret) {
> >                  return ret;
> >              }
> > diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c
> > index
> > 807deb3531f3079864ac99567d4dece7122571dd..5f31ed4b6969c4ebf72b298f965bcbdc752ff87f
> > 100644
> > --- a/tests/unit/test-vmstate.c
> > +++ b/tests/unit/test-vmstate.c
> > @@ -69,7 +69,7 @@ static void save_vmstate(const VMStateDescription *desc,
> > void *obj)
> >      QEMUFile *f = open_test_file(true);
> >
> >      /* Save file with vmstate */
> > -    int ret = vmstate_save_state(f, desc, obj, NULL);
> > +    int ret = vmstate_save_state(f, desc, obj, NULL, &error_warn);
> >      g_assert(!ret);
> >      qemu_put_byte(f, QEMU_VM_EOF);
> >      g_assert(!qemu_file_get_error(f));
> > @@ -429,7 +429,8 @@ static void test_save_noskip(void)
> >      QEMUFile *fsave = open_test_file(true);
> >      TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
> >                         .skip_c_e = false };
> > -    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
> > +    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL,
> > +                                 &error_warn);
> >      g_assert(!ret);
> >      g_assert(!qemu_file_get_error(fsave));
> >
> > @@ -451,7 +452,8 @@ static void test_save_skip(void)
> >      QEMUFile *fsave = open_test_file(true);
> >      TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
> >                         .skip_c_e = true };
> > -    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
> > +    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL,
> > +                                 &error_warn);
> >      g_assert(!ret);
> >      g_assert(!qemu_file_get_error(fsave));
> >
> > diff --git a/ui/vdagent.c b/ui/vdagent.c
> > index
> > b9a22a689d9acfeafb862ae73308db6fbd52331e..e0fc7ff71468bfe66761385387653c742c375f05
> > 100644
> > --- a/ui/vdagent.c
> > +++ b/ui/vdagent.c
> > @@ -992,7 +992,8 @@ static int put_cbinfo(QEMUFile *f, void *pv, size_t
> > size,
> >          }
> >      }
> >
> > -    return vmstate_save_state(f, &vmstate_cbinfo_array, &cbinfo, vmdesc);
> > +    return vmstate_save_state(f, &vmstate_cbinfo_array, &cbinfo, vmdesc,
> > +                              &error_warn);
> >  }
> >
> >  static int get_cbinfo(QEMUFile *f, void *pv, size_t size,
> >
> > --
> > 2.50.1
> >
> >


Regards,
Arun



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

* Re: [PATCH v9 05/27] migration: push Error **errp into loadvm_process_command()
  2025-08-06  8:31   ` Marc-André Lureau
@ 2025-08-06  9:46     ` Arun Menon
  0 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-06  9:46 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

Hi Marc-André,
Thanks for the review.

On Wed, Aug 06, 2025 at 12:31:31PM +0400, Marc-André Lureau wrote:
> Hi
> 
> On Tue, Aug 5, 2025 at 10:28 PM Arun Menon <armenon@redhat.com> wrote:
> 
> > This is an incremental step in converting vmstate loading
> > code to report error via Error objects instead of directly
> > printing it to console/monitor.
> > It is ensured that loadvm_process_command() must report an error
> > in errp, in case of failure.
> >
> > Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > Signed-off-by: Arun Menon <armenon@redhat.com>
> > ---
> >  migration/savevm.c | 72
> > +++++++++++++++++++++++++++++++++++++++---------------
> >  1 file changed, 52 insertions(+), 20 deletions(-)
> >
> > diff --git a/migration/savevm.c b/migration/savevm.c
> > index
> > f37c4455dcf839d46f026fc7c7ff02e2dfffe7b4..cb673f43b174249ff1525dba41284de2e5a70735
> > 100644
> > --- a/migration/savevm.c
> > +++ b/migration/savevm.c
> > @@ -2546,12 +2546,13 @@ static int
> > loadvm_postcopy_handle_switchover_start(void)
> >   * LOADVM_QUIT All good, but exit the loop
> >   * <0          Error
> >   */
> > -static int loadvm_process_command(QEMUFile *f)
> > +static int loadvm_process_command(QEMUFile *f, Error **errp)
> >  {
> >      MigrationIncomingState *mis = migration_incoming_get_current();
> >      uint16_t cmd;
> >      uint16_t len;
> >      uint32_t tmp32;
> > +    int ret;
> >
> >      cmd = qemu_get_be16(f);
> >      len = qemu_get_be16(f);
> > @@ -2562,16 +2563,16 @@ static int loadvm_process_command(QEMUFile *f)
> >      }
> >
> >      if (cmd >= MIG_CMD_MAX || cmd == MIG_CMD_INVALID) {
> > -        error_report("MIG_CMD 0x%x unknown (len 0x%x)", cmd, len);
> > +        error_setg(errp, "MIG_CMD 0x%x unknown (len 0x%x)", cmd, len);
> >          return -EINVAL;
> >      }
> >
> >      trace_loadvm_process_command(mig_cmd_args[cmd].name, len);
> >
> >      if (mig_cmd_args[cmd].len != -1 && mig_cmd_args[cmd].len != len) {
> > -        error_report("%s received with bad length - expecting %zu, got
> > %d",
> > -                     mig_cmd_args[cmd].name,
> > -                     (size_t)mig_cmd_args[cmd].len, len);
> > +        error_setg(errp, "%s received with bad length - expecting %zu,
> > got %d",
> > +                   mig_cmd_args[cmd].name,
> > +                   (size_t)mig_cmd_args[cmd].len, len);
> >          return -ERANGE;
> >      }
> >
> > @@ -2594,11 +2595,10 @@ static int loadvm_process_command(QEMUFile *f)
> >           * been created.
> >           */
> >          if (migrate_switchover_ack() && !mis->switchover_ack_pending_num)
> > {
> > -            int ret = migrate_send_rp_switchover_ack(mis);
> > +            ret = migrate_send_rp_switchover_ack(mis);
> >              if (ret) {
> > -                error_report(
> > -                    "Could not send switchover ack RP MSG, err %d (%s)",
> > ret,
> > -                    strerror(-ret));
> > +                error_setg_errno(errp, -ret,
> > +                                 "Could not send switchover ack RP MSG");
> >                  return ret;
> >              }
> >          }
> > @@ -2608,39 +2608,71 @@ static int loadvm_process_command(QEMUFile *f)
> >          tmp32 = qemu_get_be32(f);
> >          trace_loadvm_process_command_ping(tmp32);
> >          if (!mis->to_src_file) {
> > -            error_report("CMD_PING (0x%x) received with no return path",
> > -                         tmp32);
> > +            error_setg(errp, "CMD_PING (0x%x) received with no return
> > path",
> > +                       tmp32);
> >              return -1;
> >          }
> >          migrate_send_rp_pong(mis, tmp32);
> >          break;
> >
> >      case MIG_CMD_PACKAGED:
> > -        return loadvm_handle_cmd_packaged(mis);
> > +        ret = loadvm_handle_cmd_packaged(mis);
> > +        if (ret < 0) {
> > +            error_setg(errp, "Failed to load device state command: %d",
> > ret);
> > +        }
> > +        return ret;
> >
> >      case MIG_CMD_POSTCOPY_ADVISE:
> > -        return loadvm_postcopy_handle_advise(mis, len);
> > +        ret = loadvm_postcopy_handle_advise(mis, len);
> > +        if (ret < 0) {
> > +            error_setg(errp, "Failed to load device state command: %d",
> > ret);
> > +        }
> > +        return ret;
> >
> >      case MIG_CMD_POSTCOPY_LISTEN:
> > -        return loadvm_postcopy_handle_listen(mis);
> > +        ret = loadvm_postcopy_handle_listen(mis);
> > +        if (ret < 0) {
> > +            error_setg(errp, "Failed to load device state command: %d",
> > ret);
> > +        }
> > +        return ret;
> >
> >      case MIG_CMD_POSTCOPY_RUN:
> > -        return loadvm_postcopy_handle_run(mis);
> > +        ret = loadvm_postcopy_handle_run(mis);
> > +        if (ret < 0) {
> > +            error_setg(errp, "Failed to load device state command: %d",
> > ret);
> > +        }
> > +        return ret;
> >
> >      case MIG_CMD_POSTCOPY_RAM_DISCARD:
> > -        return loadvm_postcopy_ram_handle_discard(mis, len);
> > +        ret = loadvm_postcopy_ram_handle_discard(mis, len);
> > +        if (ret < 0) {
> > +            error_setg(errp, "Failed to load device state command: %d",
> > ret);
> > +        }
> > +        return ret;
> >
> >      case MIG_CMD_POSTCOPY_RESUME:
> >          return loadvm_postcopy_handle_resume(mis);
> >
> >      case MIG_CMD_RECV_BITMAP:
> > -        return loadvm_handle_recv_bitmap(mis, len);
> > +        ret = loadvm_handle_recv_bitmap(mis, len);
> > +        if (ret < 0) {
> > +            error_setg(errp, "Failed to load device state command: %d",
> > ret);
> > +        }
> > +        return ret;
> >
> >      case MIG_CMD_ENABLE_COLO:
> > -        return loadvm_process_enable_colo(mis);
> > +        ret = loadvm_process_enable_colo(mis);
> > +        if (ret < 0) {
> > +            error_setg(errp, "Failed to load device state command: %d",
> > ret);
> > +        }
> > +        return ret;
> >
> >      case MIG_CMD_SWITCHOVER_START:
> > -        return loadvm_postcopy_handle_switchover_start();
> > +        ret = loadvm_postcopy_handle_switchover_start();
> > +        if (ret < 0) {
> > +            error_setg(errp, "Failed to load device state command: %d",
> > ret);
> > +        }
> > +        return ret;
> >      }
> >
> >      return 0;
> > @@ -3074,7 +3106,7 @@ retry:
> >              }
> >              break;
> >          case QEMU_VM_COMMAND:
> > -            ret = loadvm_process_command(f);
> > +            ret = loadvm_process_command(f, NULL);
> >
> 
> The function used to error_report(), you should pass &error_warn to keep
> reporting.
Yes, will do. Thanks
> 
> 
> >              trace_qemu_loadvm_state_section_command(ret);
> >              if ((ret < 0) || (ret == LOADVM_QUIT)) {
> >                  goto out;
> >
> > --
> > 2.50.1
> >
> >

Regards,
Arun



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

* Re: [PATCH v9 02/27] migration: push Error **errp into vmstate_load_state()
  2025-08-06  8:31   ` Marc-André Lureau
@ 2025-08-06  9:47     ` Arun Menon
  0 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-06  9:47 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

Hi Marc-André,
Thank you for the review.

On Wed, Aug 06, 2025 at 12:31:48PM +0400, Marc-André Lureau wrote:
> Hi
> 
> On Tue, Aug 5, 2025 at 10:28 PM Arun Menon <armenon@redhat.com> wrote:
> 
> > This is an incremental step in converting vmstate loading
> > code to report error via Error objects instead of directly
> > printing it to console/monitor.
> > It is ensured that vmstate_load_state() must report an error
> > in errp, in case of failure.
> >
> > In cases where we do not want to essentially set the error
> > in errp object and also not abort/exit, the caller only
> > passes &error_warn, to warn on error.
> >
> > Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > Signed-off-by: Arun Menon <armenon@redhat.com>
> > ---
> >  hw/display/virtio-gpu.c     |  2 +-
> >  hw/pci/pci.c                |  3 ++-
> >  hw/s390x/virtio-ccw.c       |  2 +-
> >  hw/scsi/spapr_vscsi.c       |  2 +-
> >  hw/vfio/pci.c               |  2 +-
> >  hw/virtio/virtio-mmio.c     |  3 ++-
> >  hw/virtio/virtio-pci.c      |  2 +-
> >  hw/virtio/virtio.c          |  4 ++--
> >  include/migration/vmstate.h |  2 +-
> >  migration/cpr.c             |  5 ++--
> >  migration/savevm.c          |  6 +++--
> >  migration/vmstate-types.c   | 11 +++++----
> >  migration/vmstate.c         | 56
> > ++++++++++++++++++++++++++++++---------------
> >  tests/unit/test-vmstate.c   | 20 ++++++++--------
> >  ui/vdagent.c                |  2 +-
> >  15 files changed, 75 insertions(+), 47 deletions(-)
> >
> > diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
> > index
> > 0a1a625b0ea6cf26cb0d799171a57ed3d3ab2442..5ff9f5e6dc0b6b112e36170318c900f893a73c5e
> > 100644
> > --- a/hw/display/virtio-gpu.c
> > +++ b/hw/display/virtio-gpu.c
> > @@ -1343,7 +1343,7 @@ 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);
> > +    vmstate_load_state(f, &vmstate_virtio_gpu_scanouts, g, 1,
> > &error_warn);
> >
> >      return 0;
> >  }
> > diff --git a/hw/pci/pci.c b/hw/pci/pci.c
> > index
> > c70b5ceebaf1f2b10768bd030526cbb518da2b8d..80545189980f176ca6a3dc9abce7043c8bc2708c
> > 100644
> > --- a/hw/pci/pci.c
> > +++ b/hw/pci/pci.c
> > @@ -934,7 +934,8 @@ void pci_device_save(PCIDevice *s, QEMUFile *f)
> >  int pci_device_load(PCIDevice *s, QEMUFile *f)
> >  {
> >      int ret;
> > -    ret = vmstate_load_state(f, &vmstate_pci_device, s, s->version_id);
> > +    ret = vmstate_load_state(f, &vmstate_pci_device, s, s->version_id,
> > +                             &error_warn);
> >      /* 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
> > d2f85b39f30f7fc82e0c600144c0a958e1269b2c..bafcb9b76d81749925e31b5a0a1320b3332ad2cf
> > 100644
> > --- a/hw/s390x/virtio-ccw.c
> > +++ b/hw/s390x/virtio-ccw.c
> > @@ -1136,7 +1136,7 @@ static void virtio_ccw_save_config(DeviceState *d,
> > QEMUFile *f)
> >  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);
> > +    return vmstate_load_state(f, &vmstate_virtio_ccw_dev, dev, 1,
> > &error_warn);
> >  }
> >
> >  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
> > 20f70fb2729de78b9636a6b8c869695dab4f8902..a4812afd0cc7c495080ef03a531c7d279af29b33
> > 100644
> > --- a/hw/scsi/spapr_vscsi.c
> > +++ b/hw/scsi/spapr_vscsi.c
> > @@ -648,7 +648,7 @@ static void *vscsi_load_request(QEMUFile *f,
> > SCSIRequest *sreq)
> >      assert(!req->active);
> >
> >      memset(req, 0, sizeof(*req));
> > -    rc = vmstate_load_state(f, &vmstate_spapr_vscsi_req, req, 1);
> > +    rc = vmstate_load_state(f, &vmstate_spapr_vscsi_req, req, 1,
> > &error_warn);
> >      if (rc) {
> >          fprintf(stderr, "VSCSI: failed loading request tag#%u\n",
> > sreq->tag);
> >          return NULL;
> > diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
> > index
> > 4fa692c1a32bcfa4e4939e5fcb64f2bf19905b3b..04d385d7674f444844beeee7364ee0424f762758
> > 100644
> > --- a/hw/vfio/pci.c
> > +++ b/hw/vfio/pci.c
> > @@ -2795,7 +2795,7 @@ static int vfio_pci_load_config(VFIODevice
> > *vbasedev, QEMUFile *f)
> >          old_addr[bar] = pdev->io_regions[bar].addr;
> >      }
> >
> > -    ret = vmstate_load_state(f, &vmstate_vfio_pci_config, vdev, 1);
> > +    ret = vmstate_load_state(f, &vmstate_vfio_pci_config, vdev, 1,
> > &error_warn);
> >      if (ret) {
> >          return ret;
> >      }
> > diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c
> > index
> > 532c67107ba1d2978a76cf49f9cdc1de1dea3e11..f14f0487fec3ed30d2cc3aec96950c2121265716
> > 100644
> > --- a/hw/virtio/virtio-mmio.c
> > +++ b/hw/virtio/virtio-mmio.c
> > @@ -34,6 +34,7 @@
> >  #include "qemu/error-report.h"
> >  #include "qemu/log.h"
> >  #include "trace.h"
> > +#include "qapi/error.h"
> >
> >  static bool virtio_mmio_ioeventfd_enabled(DeviceState *d)
> >  {
> > @@ -619,7 +620,7 @@ static int virtio_mmio_load_extra_state(DeviceState
> > *opaque, QEMUFile *f)
> >  {
> >      VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
> >
> > -    return vmstate_load_state(f, &vmstate_virtio_mmio, proxy, 1);
> > +    return vmstate_load_state(f, &vmstate_virtio_mmio, proxy, 1,
> > &error_warn);
> >  }
> >
> >  static bool virtio_mmio_has_extra_state(DeviceState *opaque)
> > diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
> > index
> > 767216d795998708f5716a23ae16c79cd90ff489..07f144d6d1bfbb561b28015de71762601080dc23
> > 100644
> > --- a/hw/virtio/virtio-pci.c
> > +++ b/hw/virtio/virtio-pci.c
> > @@ -161,7 +161,7 @@ static int virtio_pci_load_extra_state(DeviceState *d,
> > QEMUFile *f)
> >  {
> >      VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
> >
> > -    return vmstate_load_state(f, &vmstate_virtio_pci, proxy, 1);
> > +    return vmstate_load_state(f, &vmstate_virtio_pci, proxy, 1,
> > &error_warn);
> >  }
> >
> >  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
> > 2ab1d20769495ea39445b87e3673b076ad172510..aac317a93c3cd55ece9b0fd858c7b2459f8242f9
> > 100644
> > --- a/hw/virtio/virtio.c
> > +++ b/hw/virtio/virtio.c
> > @@ -3317,14 +3317,14 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int
> > version_id)
> >      }
> >
> >      if (vdc->vmsd) {
> > -        ret = vmstate_load_state(f, vdc->vmsd, vdev, version_id);
> > +        ret = vmstate_load_state(f, vdc->vmsd, vdev, version_id,
> > &error_warn);
> >          if (ret) {
> >              return ret;
> >          }
> >      }
> >
> >      /* Subsections */
> > -    ret = vmstate_load_state(f, &vmstate_virtio, vdev, 1);
> > +    ret = vmstate_load_state(f, &vmstate_virtio, vdev, 1, &error_warn);
> >      if (ret) {
> >          return ret;
> >      }
> > diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
> > index
> > 1ff7bd9ac425ba67cd5ca7ad97bcf570f9e19abe..056781b1c21e737583f081594d9f88b32adfd674
> > 100644
> > --- a/include/migration/vmstate.h
> > +++ b/include/migration/vmstate.h
> > @@ -1196,7 +1196,7 @@ extern const VMStateInfo vmstate_info_qlist;
> >      }
> >
> >  int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
> > -                       void *opaque, int version_id);
> > +                       void *opaque, int version_id, Error **errp);
> >  int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
> >                         void *opaque, JSONWriter *vmdesc);
> >  int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription
> > *vmsd,
> > diff --git a/migration/cpr.c b/migration/cpr.c
> > index
> > 42ad0b0d500e5de57faf0c6517e216b2d1c0cacf..bdb24736f44e91ba59b6e622a315597c97e7f64d
> > 100644
> > --- a/migration/cpr.c
> > +++ b/migration/cpr.c
> > @@ -202,6 +202,7 @@ int cpr_state_save(MigrationChannel *channel, Error
> > **errp)
> >
> >  int cpr_state_load(MigrationChannel *channel, Error **errp)
> >  {
> > +    ERRP_GUARD();
> >      int ret;
> >      uint32_t v;
> >      QEMUFile *f;
> > @@ -233,9 +234,9 @@ int cpr_state_load(MigrationChannel *channel, Error
> > **errp)
> >          return -ENOTSUP;
> >      }
> >
> > -    ret = vmstate_load_state(f, &vmstate_cpr_state, &cpr_state, 1);
> > +    ret = vmstate_load_state(f, &vmstate_cpr_state, &cpr_state, 1, errp);
> >      if (ret) {
> > -        error_setg(errp, "vmstate_load_state error %d", ret);
> > +        error_prepend(errp, "vmstate_load_state error %d: ", ret);
> >          qemu_fclose(f);
> >          return ret;
> >      }
> > diff --git a/migration/savevm.c b/migration/savevm.c
> > index
> > fabbeb296ae987d0c06ba6dafda63720205fecfd..ab947620f724874f325fb9fb59bef50b7c16fb51
> > 100644
> > --- a/migration/savevm.c
> > +++ b/migration/savevm.c
> > @@ -969,7 +969,8 @@ static int vmstate_load(QEMUFile *f, SaveStateEntry
> > *se)
> >      if (!se->vmsd) {         /* Old style */
> >          return se->ops->load_state(f, se->opaque, se->load_version_id);
> >      }
> > -    return vmstate_load_state(f, se->vmsd, se->opaque,
> > se->load_version_id);
> > +    return vmstate_load_state(f, se->vmsd, se->opaque,
> > se->load_version_id,
> > +                              NULL);
> >  }
> >
> >  static void vmstate_save_old_style(QEMUFile *f, SaveStateEntry *se,
> > @@ -2839,7 +2840,8 @@ static int qemu_loadvm_state_header(QEMUFile *f)
> >              error_report("Configuration section missing");
> >              return -EINVAL;
> >          }
> > -        ret = vmstate_load_state(f, &vmstate_configuration,
> > &savevm_state, 0);
> > +        ret = vmstate_load_state(f, &vmstate_configuration,
> > &savevm_state, 0,
> > +                                 NULL);
> >
> 
> Why not &error_warn in the above 2 calls? (even if it is changed in the
> following patches)
> 
> 
Sure, will do. Thanks
> >
> >          if (ret) {
> >              return ret;
> > diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c
> > index
> > 741a588b7e18c6d37724b08a0101edc8bc74a0a5..e2020a733d0921d748b6f832a193e5de8d302d5f
> > 100644
> > --- a/migration/vmstate-types.c
> > +++ b/migration/vmstate-types.c
> > @@ -19,6 +19,7 @@
> >  #include "qemu/error-report.h"
> >  #include "qemu/queue.h"
> >  #include "trace.h"
> > +#include "qapi/error.h"
> >
> >  /* bool */
> >
> > @@ -549,7 +550,7 @@ static int get_tmp(QEMUFile *f, void *pv, size_t size,
> >
> >      /* Writes the parent field which is at the start of the tmp */
> >      *(void **)tmp = pv;
> > -    ret = vmstate_load_state(f, vmsd, tmp, version_id);
> > +    ret = vmstate_load_state(f, vmsd, tmp, version_id, &error_warn);
> >      g_free(tmp);
> >      return ret;
> >  }
> > @@ -649,7 +650,7 @@ static int get_qtailq(QEMUFile *f, void *pv, size_t
> > unused_size,
> >
> >      while (qemu_get_byte(f)) {
> >          elm = g_malloc(size);
> > -        ret = vmstate_load_state(f, vmsd, elm, version_id);
> > +        ret = vmstate_load_state(f, vmsd, elm, version_id, &error_warn);
> >          if (ret) {
> >              return ret;
> >          }
> > @@ -803,7 +804,7 @@ static int get_gtree(QEMUFile *f, void *pv, size_t
> > unused_size,
> >              key = (void *)(uintptr_t)qemu_get_be64(f);
> >          } else {
> >              key = g_malloc0(key_size);
> > -            ret = vmstate_load_state(f, key_vmsd, key, version_id);
> > +            ret = vmstate_load_state(f, key_vmsd, key, version_id,
> > &error_warn);
> >              if (ret) {
> >                  error_report("%s : failed to load %s (%d)",
> >                               field->name, key_vmsd->name, ret);
> > @@ -811,7 +812,7 @@ static int get_gtree(QEMUFile *f, void *pv, size_t
> > unused_size,
> >              }
> >          }
> >          val = g_malloc0(val_size);
> > -        ret = vmstate_load_state(f, val_vmsd, val, version_id);
> > +        ret = vmstate_load_state(f, val_vmsd, val, version_id,
> > &error_warn);
> >          if (ret) {
> >              error_report("%s : failed to load %s (%d)",
> >                           field->name, val_vmsd->name, ret);
> > @@ -892,7 +893,7 @@ static int get_qlist(QEMUFile *f, void *pv, size_t
> > unused_size,
> >
> >      while (qemu_get_byte(f)) {
> >          elm = g_malloc(size);
> > -        ret = vmstate_load_state(f, vmsd, elm, version_id);
> > +        ret = vmstate_load_state(f, vmsd, elm, version_id, &error_warn);
> >          if (ret) {
> >              error_report("%s: failed to load %s (%d)", field->name,
> >                           vmsd->name, ret);
> > diff --git a/migration/vmstate.c b/migration/vmstate.c
> > index
> > 24451b054c11dfca2d76e24b053d604bb7184e1c..60ff38858cf54277992fa5eddeadb6f3d70edec3
> > 100644
> > --- a/migration/vmstate.c
> > +++ b/migration/vmstate.c
> > @@ -132,29 +132,33 @@ static void vmstate_handle_alloc(void *ptr, const
> > VMStateField *field,
> >  }
> >
> >  int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
> > -                       void *opaque, int version_id)
> > +                       void *opaque, int version_id, Error **errp)
> >  {
> >      const VMStateField *field = vmsd->fields;
> >      int ret = 0;
> >
> >      trace_vmstate_load_state(vmsd->name, version_id);
> >      if (version_id > vmsd->version_id) {
> > -        error_report("%s: incoming version_id %d is too new "
> > -                     "for local version_id %d",
> > -                     vmsd->name, version_id, vmsd->version_id);
> > +        error_setg(errp, "%s: incoming version_id %d is too new "
> > +                   "for local version_id %d",
> > +                   vmsd->name, version_id, vmsd->version_id);
> >          trace_vmstate_load_state_end(vmsd->name, "too new", -EINVAL);
> >          return -EINVAL;
> >      }
> >      if  (version_id < vmsd->minimum_version_id) {
> > -        error_report("%s: incoming version_id %d is too old "
> > -                     "for local minimum version_id  %d",
> > -                     vmsd->name, version_id, vmsd->minimum_version_id);
> > +        error_setg(errp, "%s: incoming version_id %d is too old "
> > +                   "for local minimum version_id %d",
> > +                   vmsd->name, version_id, vmsd->minimum_version_id);
> >          trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL);
> >          return -EINVAL;
> >      }
> >      if (vmsd->pre_load) {
> >          ret = vmsd->pre_load(opaque);
> >          if (ret) {
> > +            error_setg(errp, "VM pre load failed for: '%s', "
> > +                       "version_id: %d, minimum version_id: %d, ret: %d",
> > +                       vmsd->name, vmsd->version_id,
> > vmsd->minimum_version_id,
> > +                       ret);
> >              return ret;
> >          }
> >      }
> > @@ -192,10 +196,12 @@ int vmstate_load_state(QEMUFile *f, const
> > VMStateDescription *vmsd,
> >
> >                  if (inner_field->flags & VMS_STRUCT) {
> >                      ret = vmstate_load_state(f, inner_field->vmsd,
> > curr_elem,
> > -
> >  inner_field->vmsd->version_id);
> > +
> >  inner_field->vmsd->version_id,
> > +                                             errp);
> >                  } else if (inner_field->flags & VMS_VSTRUCT) {
> >                      ret = vmstate_load_state(f, inner_field->vmsd,
> > curr_elem,
> > -
> >  inner_field->struct_version_id);
> > +
> >  inner_field->struct_version_id,
> > +                                             errp);
> >                  } else {
> >                      ret = inner_field->info->get(f, curr_elem, size,
> >                                                   inner_field);
> > @@ -208,30 +214,43 @@ int vmstate_load_state(QEMUFile *f, const
> > VMStateDescription *vmsd,
> >
> >                  if (ret >= 0) {
> >                      ret = qemu_file_get_error(f);
> > +                    if (ret < 0) {
> > +                        error_setg(errp, "Failed to load %s state: %d",
> > +                                   vmsd->name, ret);
> > +                        return ret;
> > +                    }
> >                  }
> >                  if (ret < 0) {
> >                      qemu_file_set_error(f, ret);
> > -                    error_report("Failed to load %s:%s", vmsd->name,
> > -                                 field->name);
> > +                    error_setg(errp,
> > +                               "Failed to load %s:%s version_id: %d: ",
> > +                               vmsd->name, field->name, vmsd->version_id);
> >                      trace_vmstate_load_field_error(field->name, ret);
> >                      return ret;
> >                  }
> >              }
> >          } else if (field->flags & VMS_MUST_EXIST) {
> > -            error_report("Input validation failed: %s/%s",
> > -                         vmsd->name, field->name);
> > +            error_setg(errp, "Input validation failed: %s/%s version_id:
> > %d",
> > +                       vmsd->name, field->name, vmsd->version_id);
> >              return -1;
> >          }
> >          field++;
> >      }
> >      assert(field->flags == VMS_END);
> > -    ret = vmstate_subsection_load(f, vmsd, opaque, NULL);
> > +    ret = vmstate_subsection_load(f, vmsd, opaque, errp);
> >      if (ret != 0) {
> >          qemu_file_set_error(f, ret);
> >          return ret;
> >      }
> >      if (vmsd->post_load) {
> >          ret = vmsd->post_load(opaque, version_id);
> > +        if (ret < 0) {
> > +            error_setg(errp,
> > +                       "VM Post load failed for: %s, version_id: %d, "
> > +                       "minimum_version: %d, ret: %d",
> > +                       vmsd->name, vmsd->version_id,
> > vmsd->minimum_version_id,
> > +                       ret);
> > +        }
> >      }
> >      trace_vmstate_load_state_end(vmsd->name, "end", ret);
> >      return ret;
> > @@ -568,6 +587,7 @@ vmstate_get_subsection(const VMStateDescription *
> > const *sub,
> >  static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription
> > *vmsd,
> >                                     void *opaque, Error **errp)
> >  {
> > +    ERRP_GUARD();
> >      trace_vmstate_subsection_load(vmsd->name);
> >
> >      while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) {
> > @@ -607,12 +627,12 @@ static int vmstate_subsection_load(QEMUFile *f,
> > const VMStateDescription *vmsd,
> >          qemu_file_skip(f, len); /* idstr */
> >          version_id = qemu_get_be32(f);
> >
> > -        ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
> > +        ret = vmstate_load_state(f, sub_vmsd, opaque, version_id, errp);
> >          if (ret) {
> >              trace_vmstate_subsection_load_bad(vmsd->name, idstr,
> > "(child)");
> > -            error_setg(errp,
> > -                       "Loading VM subsection '%s' in '%s' failed: %d",
> > -                       idstr, vmsd->name, ret);
> > +            error_prepend(errp,
> > +                          "Loading VM subsection '%s' in '%s' failed: %d:
> > ",
> > +                          idstr, vmsd->name, ret);
> >              return ret;
> >          }
> >      }
> > diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c
> > index
> > 63f28f26f45691a70936d33e7341d16477a3471f..807deb3531f3079864ac99567d4dece7122571dd
> > 100644
> > --- a/tests/unit/test-vmstate.c
> > +++ b/tests/unit/test-vmstate.c
> > @@ -30,6 +30,7 @@
> >  #include "../migration/savevm.h"
> >  #include "qemu/module.h"
> >  #include "io/channel-file.h"
> > +#include "qapi/error.h"
> >
> >  static int temp_fd;
> >
> > @@ -114,7 +115,7 @@ static int load_vmstate_one(const VMStateDescription
> > *desc, void *obj,
> >      qemu_fclose(f);
> >
> >      f = open_test_file(false);
> > -    ret = vmstate_load_state(f, desc, obj, version);
> > +    ret = vmstate_load_state(f, desc, obj, version, &error_warn);
> >      if (ret) {
> >          g_assert(qemu_file_get_error(f));
> >      } else{
> > @@ -365,7 +366,7 @@ static void test_load_v1(void)
> >
> >      QEMUFile *loading = open_test_file(false);
> >      TestStruct obj = { .b = 200, .e = 500, .f = 600 };
> > -    vmstate_load_state(loading, &vmstate_versioned, &obj, 1);
> > +    vmstate_load_state(loading, &vmstate_versioned, &obj, 1, &error_warn);
> >      g_assert(!qemu_file_get_error(loading));
> >      g_assert_cmpint(obj.a, ==, 10);
> >      g_assert_cmpint(obj.b, ==, 200);
> > @@ -391,7 +392,7 @@ static void test_load_v2(void)
> >
> >      QEMUFile *loading = open_test_file(false);
> >      TestStruct obj;
> > -    vmstate_load_state(loading, &vmstate_versioned, &obj, 2);
> > +    vmstate_load_state(loading, &vmstate_versioned, &obj, 2, &error_warn);
> >      g_assert_cmpint(obj.a, ==, 10);
> >      g_assert_cmpint(obj.b, ==, 20);
> >      g_assert_cmpint(obj.c, ==, 30);
> > @@ -480,7 +481,7 @@ static void test_load_noskip(void)
> >
> >      QEMUFile *loading = open_test_file(false);
> >      TestStruct obj = { .skip_c_e = false };
> > -    vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
> > +    vmstate_load_state(loading, &vmstate_skipping, &obj, 2, &error_warn);
> >      g_assert(!qemu_file_get_error(loading));
> >      g_assert_cmpint(obj.a, ==, 10);
> >      g_assert_cmpint(obj.b, ==, 20);
> > @@ -504,7 +505,7 @@ static void test_load_skip(void)
> >
> >      QEMUFile *loading = open_test_file(false);
> >      TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 };
> > -    vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
> > +    vmstate_load_state(loading, &vmstate_skipping, &obj, 2, &error_warn);
> >      g_assert(!qemu_file_get_error(loading));
> >      g_assert_cmpint(obj.a, ==, 10);
> >      g_assert_cmpint(obj.b, ==, 20);
> > @@ -773,7 +774,7 @@ static void test_load_q(void)
> >      TestQtailq tgt;
> >
> >      QTAILQ_INIT(&tgt.q);
> > -    vmstate_load_state(fload, &vmstate_q, &tgt, 1);
> > +    vmstate_load_state(fload, &vmstate_q, &tgt, 1, &error_warn);
> >      char eof = qemu_get_byte(fload);
> >      g_assert(!qemu_file_get_error(fload));
> >      g_assert_cmpint(tgt.i16, ==, obj_q.i16);
> > @@ -1127,7 +1128,7 @@ static void test_gtree_load_domain(void)
> >
> >      fload = open_test_file(false);
> >
> > -    vmstate_load_state(fload, &vmstate_domain, dest_domain, 1);
> > +    vmstate_load_state(fload, &vmstate_domain, dest_domain, 1,
> > &error_warn);
> >      eof = qemu_get_byte(fload);
> >      g_assert(!qemu_file_get_error(fload));
> >      g_assert_cmpint(orig_domain->id, ==, dest_domain->id);
> > @@ -1241,7 +1242,7 @@ static void test_gtree_load_iommu(void)
> >      qemu_fclose(fsave);
> >
> >      fload = open_test_file(false);
> > -    vmstate_load_state(fload, &vmstate_iommu, dest_iommu, 1);
> > +    vmstate_load_state(fload, &vmstate_iommu, dest_iommu, 1, &error_warn);
> >      eof = qemu_get_byte(fload);
> >      g_assert(!qemu_file_get_error(fload));
> >      g_assert_cmpint(orig_iommu->id, ==, dest_iommu->id);
> > @@ -1376,7 +1377,8 @@ static void test_load_qlist(void)
> >      qemu_fclose(fsave);
> >
> >      fload = open_test_file(false);
> > -    vmstate_load_state(fload, &vmstate_container, dest_container, 1);
> > +    vmstate_load_state(fload, &vmstate_container, dest_container, 1,
> > +                       &error_warn);
> >      eof = qemu_get_byte(fload);
> >      g_assert(!qemu_file_get_error(fload));
> >      g_assert_cmpint(eof, ==, QEMU_VM_EOF);
> > diff --git a/ui/vdagent.c b/ui/vdagent.c
> > index
> > c0746fe5b168fdc7aeb4866de2ba0c3387566649..b9a22a689d9acfeafb862ae73308db6fbd52331e
> > 100644
> > --- a/ui/vdagent.c
> > +++ b/ui/vdagent.c
> > @@ -1008,7 +1008,7 @@ static int get_cbinfo(QEMUFile *f, void *pv, size_t
> > size,
> >
> >      vdagent_clipboard_peer_register(vd);
> >
> > -    ret = vmstate_load_state(f, &vmstate_cbinfo_array, &cbinfo, 0);
> > +    ret = vmstate_load_state(f, &vmstate_cbinfo_array, &cbinfo, 0,
> > &error_warn);
> >      if (ret) {
> >          return ret;
> >      }
> >
> > --
> > 2.50.1
> >
> >

Regards,
Arun



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

* Re: [PATCH v9 20/27] migration: push Error **errp into loadvm_process_enable_colo()
  2025-08-06  8:07   ` Marc-André Lureau
@ 2025-08-06  9:56     ` Arun Menon
  2025-08-06 10:04       ` Marc-André Lureau
  0 siblings, 1 reply; 63+ messages in thread
From: Arun Menon @ 2025-08-06  9:56 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

Hi Marc-André,
Thanks for the review and the Reviewed-by tag.

On Wed, Aug 06, 2025 at 12:07:25PM +0400, Marc-André Lureau wrote:
> Hi
> 
> On Tue, Aug 5, 2025 at 10:31 PM Arun Menon <armenon@redhat.com> wrote:
> 
> > This is an incremental step in converting vmstate loading
> > code to report error via Error objects instead of directly
> > printing it to console/monitor.
> > It is ensured that loadvm_process_enable_colo() must report an error
> > in errp, in case of failure.
> >
> > Signed-off-by: Arun Menon <armenon@redhat.com>
> >
> 
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> 
> > ---
> >  include/migration/colo.h |  2 +-
> >  migration/migration.c    | 12 ++++++------
> >  migration/ram.c          |  8 ++++----
> >  migration/ram.h          |  2 +-
> >  migration/savevm.c       | 26 ++++++++++++++------------
> >  5 files changed, 26 insertions(+), 24 deletions(-)
> >
> > diff --git a/include/migration/colo.h b/include/migration/colo.h
> > index
> > 43222ef5ae6adc3f7d8aa6a48bef79af33d09208..d4fe422e4d335d3bef4f860f56400fcd73287a0e
> > 100644
> > --- a/include/migration/colo.h
> > +++ b/include/migration/colo.h
> > @@ -25,7 +25,7 @@ void migrate_start_colo_process(MigrationState *s);
> >  bool migration_in_colo_state(void);
> >
> >  /* loadvm */
> > -int migration_incoming_enable_colo(void);
> > +int migration_incoming_enable_colo(Error **errp);
> >  void migration_incoming_disable_colo(void);
> >  bool migration_incoming_colo_enabled(void);
> >  bool migration_incoming_in_colo_state(void);
> > diff --git a/migration/migration.c b/migration/migration.c
> > index
> > 6962dc7d7f3e0121d28994c98f12f9f2258343d7..4a76d7ed730589bae87115368b0bf4819f8b161e
> > 100644
> > --- a/migration/migration.c
> > +++ b/migration/migration.c
> > @@ -623,22 +623,22 @@ void migration_incoming_disable_colo(void)
> >      migration_colo_enabled = false;
> >  }
> >
> > -int migration_incoming_enable_colo(void)
> > +int migration_incoming_enable_colo(Error **errp)
> >  {
> >  #ifndef CONFIG_REPLICATION
> > -    error_report("ENABLE_COLO command come in migration stream, but the "
> > -                 "replication module is not built in");
> > +    error_setg(errp, "ENABLE_COLO command come in migration stream, but
> > the "
> > +               "replication module is not built in");
> >      return -ENOTSUP;
> >  #endif
> >
> >      if (!migrate_colo()) {
> > -        error_report("ENABLE_COLO command come in migration stream, but
> > x-colo "
> > -                     "capability is not set");
> > +        error_setg(errp, "ENABLE_COLO command come in migration stream"
> > +                   ", but x-colo capability is not set");
> >          return -EINVAL;
> >      }
> >
> >      if (ram_block_discard_disable(true)) {
> > -        error_report("COLO: cannot disable RAM discard");
> > +        error_setg(errp, "COLO: cannot disable RAM discard");
> >          return -EBUSY;
> >      }
> >      migration_colo_enabled = true;
> > diff --git a/migration/ram.c b/migration/ram.c
> > index
> > 6a0dcc04f436524a37672c41c38f201f06773374..995431c9e320f443c385c29d664d62e18c1afd90
> > 100644
> > --- a/migration/ram.c
> > +++ b/migration/ram.c
> > @@ -3576,7 +3576,7 @@ static void colo_init_ram_state(void)
> >   * memory of the secondary VM, it is need to hold the global lock
> >   * to call this helper.
> >   */
> > -int colo_init_ram_cache(void)
> > +int colo_init_ram_cache(Error **errp)
> >  {
> >      RAMBlock *block;
> >
> > @@ -3585,9 +3585,9 @@ int colo_init_ram_cache(void)
> >              block->colo_cache = qemu_anon_ram_alloc(block->used_length,
> >                                                      NULL, false, false);
> >              if (!block->colo_cache) {
> > -                error_report("%s: Can't alloc memory for COLO cache of
> > block %s,"
> > -                             "size 0x" RAM_ADDR_FMT, __func__,
> > block->idstr,
> > -                             block->used_length);
> > +                error_setg(errp, "Can't alloc memory for COLO cache of "
> > +                           "block %s, size 0x" RAM_ADDR_FMT,
> > +                           block->idstr, block->used_length);
> >                  RAMBLOCK_FOREACH_NOT_IGNORED(block) {
> >                      if (block->colo_cache) {
> >                          qemu_anon_ram_free(block->colo_cache,
> > block->used_length);
> > diff --git a/migration/ram.h b/migration/ram.h
> > index
> > 275709a99187f9429ccb4111e05281ec268ba0db..24cd0bf585762cfa1e86834dc03c6baeea2f0627
> > 100644
> > --- a/migration/ram.h
> > +++ b/migration/ram.h
> > @@ -109,7 +109,7 @@ void ramblock_set_file_bmap_atomic(RAMBlock *block,
> > ram_addr_t offset,
> >                                     bool set);
> >
> >  /* ram cache */
> > -int colo_init_ram_cache(void);
> > +int colo_init_ram_cache(Error **errp);
> >  void colo_flush_ram_cache(void);
> >  void colo_release_ram_cache(void);
> >  void colo_incoming_start_dirty_log(void);
> > diff --git a/migration/savevm.c b/migration/savevm.c
> > index
> > 938adb20270adbf9546b0884d0877c25c3f0f816..a6b71a958aeda31e89043f8103bfe2fc89542fb5
> > 100644
> > --- a/migration/savevm.c
> > +++ b/migration/savevm.c
> > @@ -2519,15 +2519,21 @@ static int
> > loadvm_handle_recv_bitmap(MigrationIncomingState *mis,
> >      return 0;
> >  }
> >
> > -static int loadvm_process_enable_colo(MigrationIncomingState *mis)
> > +static int loadvm_process_enable_colo(MigrationIncomingState *mis,
> > +                                      Error **errp)
> >  {
> > -    int ret = migration_incoming_enable_colo();
> > +    ERRP_GUARD();
> > +    int ret;
> >
> > -    if (!ret) {
> > -        ret = colo_init_ram_cache();
> > -        if (ret) {
> > -            migration_incoming_disable_colo();
> > -        }
> > +    ret = migration_incoming_enable_colo(errp);
> > +    if (ret < 0) {
> > +        return ret;
> > +    }
> > +
> > +    ret = colo_init_ram_cache(errp);
> > +    if (ret) {
> >
> 
> Note: here you keep the "ret" error condition !=0, ok.
> 
> colo_init_ram_cache(), returns -errno on error. Although errno should
> remain unchanged on success (during qemu_anon_ram_free etc), I think it
> would be safer to convert the function to follow the recommended
> bool-valued function for true on success / false on failure instead.

There has been some discussion on this here:
https://lore.kernel.org/all/aH5AtUcjI3HYXdBe@redhat.com/
It has been advised not to use bool return.

> 
> 
> > +        error_prepend(errp, "failed to init colo RAM cache: %d: ", ret);
> > +        migration_incoming_disable_colo();
> >
>      }
> >      return ret;
> >  }
> > @@ -2646,11 +2652,7 @@ static int loadvm_process_command(QEMUFile *f,
> > Error **errp)
> >          return loadvm_handle_recv_bitmap(mis, len, errp);
> >
> >      case MIG_CMD_ENABLE_COLO:
> > -        ret = loadvm_process_enable_colo(mis);
> > -        if (ret < 0) {
> > -            error_setg(errp, "Failed to load device state command: %d",
> > ret);
> > -        }
> > -        return ret;
> > +        return loadvm_process_enable_colo(mis, errp);
> >
> >      case MIG_CMD_SWITCHOVER_START:
> >          ret = loadvm_postcopy_handle_switchover_start();
> >
> > --
> > 2.50.1
> >
> >
Regards,
Arun



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

* Re: [PATCH v9 25/27] migration: Remove error variant of vmstate_save_state() function
  2025-08-06  9:46     ` Arun Menon
@ 2025-08-06 10:01       ` Marc-André Lureau
  2025-08-06 10:30         ` Arun Menon
  0 siblings, 1 reply; 63+ messages in thread
From: Marc-André Lureau @ 2025-08-06 10:01 UTC (permalink / raw)
  To: Arun Menon
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

Hi

On Wed, Aug 6, 2025 at 1:47 PM Arun Menon <armenon@redhat.com> wrote:
>
> Hi Marc-André,
> Thanks for the review.
>
> On Wed, Aug 06, 2025 at 12:28:54PM +0400, Marc-André Lureau wrote:
> > Hi
> >
> > On Tue, Aug 5, 2025 at 10:31 PM Arun Menon <armenon@redhat.com> wrote:
> >
> > > This commit removes the redundant vmstate_save_state_with_err()
> > > function.
> > >
> > > Previously, commit 969298f9d7 introduced vmstate_save_state_with_err()
> > > to handle error propagation, while vmstate_save_state() existed for
> > > non-error scenarios.
> > > This is because there were code paths where vmstate_save_state_v()
> > > (called internally by vmstate_save_state) did not explicitly set
> > > errors on failure.
> > >
> > > This change unifies error handling by
> > >  - updating vmstate_save_state() to accept an Error **errp argument.
> > >  - vmstate_save_state_v() ensures errors are set directly within the errp
> > >    object, eliminating the need for two separate functions. Specifically
> > >    errors are handled by storing them in errp in vmstate_save_dispatch()
> > >
> > > All calls to vmstate_save_state_with_err() are replaced with
> > > vmstate_save_state(). This simplifies the API and improves code
> > > maintainability.
> > >
> > > Signed-off-by: Arun Menon <armenon@redhat.com>
> > >
> >
> > I realize that vmstate_save_state() callers progressively became silent on
> > error with this series (switching to error_report to error_set). I suggest
> > you make a preliminary patch to make vmstate_save_state() verbose by
> > passing &error_warn to vmstate_save_state_v. wdyt?
>
> I am sorry, I did not understand the suggestion completely.
> vmstate_save_state() callers have always been silent before. Even before this series.
> Because, by default NULL was passed in vmstate_save_state_v().

But inner functions were calling error_report()

>
> This commit in the series changes that by passing &error_warn to vmstate_save_state_v()
> inside vmstate_save_state() and removing the need for alternate vmstate_save_state_with_err().
>
> Do you mean that we need to pass &error_warn to vmstate_save_state_v() in vmstate_save_state()
> as a preliminary patch before this patch? Please correct me if I am wrong.

I think so, then errors are still reported until the parent handles
the error reporting itself.

> >
> > patch lgtm otherwise
> >
> >
> >
> >
> > > ---
> > >  hw/display/virtio-gpu.c     |  3 ++-
> > >  hw/pci/pci.c                |  2 +-
> > >  hw/s390x/virtio-ccw.c       |  2 +-
> > >  hw/scsi/spapr_vscsi.c       |  2 +-
> > >  hw/vfio/pci.c               |  4 ++--
> > >  hw/virtio/virtio-mmio.c     |  2 +-
> > >  hw/virtio/virtio-pci.c      |  2 +-
> > >  hw/virtio/virtio.c          |  4 ++--
> > >  include/migration/vmstate.h |  2 --
> > >  migration/cpr.c             |  5 +++--
> > >  migration/savevm.c          |  7 ++++---
> > >  migration/vmstate-types.c   | 12 +++++++-----
> > >  migration/vmstate.c         | 18 ++++++++----------
> > >  tests/unit/test-vmstate.c   |  8 +++++---
> > >  ui/vdagent.c                |  3 ++-
> > >  15 files changed, 40 insertions(+), 36 deletions(-)
> > >
> > > diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
> > > index
> > > 5ff9f5e6dc0b6b112e36170318c900f893a73c5e..bddb26fa21312e5d0b2b6412cdf2a6c28dd06eb3
> > > 100644
> > > --- a/hw/display/virtio-gpu.c
> > > +++ b/hw/display/virtio-gpu.c
> > > @@ -1246,7 +1246,8 @@ 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);
> > > +    return vmstate_save_state(f, &vmstate_virtio_gpu_scanouts, g, NULL,
> > > +                              &error_warn);
> > >  }
> > >
> > >  static bool virtio_gpu_load_restore_mapping(VirtIOGPU *g,
> > > diff --git a/hw/pci/pci.c b/hw/pci/pci.c
> > > index
> > > 80545189980f176ca6a3dc9abce7043c8bc2708c..f7fb3931ff190c25ffa665ba80abe7737dbefdb6
> > > 100644
> > > --- a/hw/pci/pci.c
> > > +++ b/hw/pci/pci.c
> > > @@ -926,7 +926,7 @@ void pci_device_save(PCIDevice *s, QEMUFile *f)
> > >       * 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);
> > > +    vmstate_save_state(f, &vmstate_pci_device, s, NULL, &error_warn);
> > >      /* Restore the interrupt status bit. */
> > >      pci_update_irq_status(s);
> > >  }
> > > diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
> > > index
> > > bafcb9b76d81749925e31b5a0a1320b3332ad2cf..14304fd3e8901d28a7788266df2c7ef7334b691c
> > > 100644
> > > --- a/hw/s390x/virtio-ccw.c
> > > +++ b/hw/s390x/virtio-ccw.c
> > > @@ -1130,7 +1130,7 @@ 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);
> > > +    vmstate_save_state(f, &vmstate_virtio_ccw_dev, dev, NULL,
> > > &error_warn);
> > >  }
> > >
> > >  static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
> > > diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c
> > > index
> > > a4812afd0cc7c495080ef03a531c7d279af29b33..2204beb40c7cdc827db2bf86fb8612d8c09999ed
> > > 100644
> > > --- a/hw/scsi/spapr_vscsi.c
> > > +++ b/hw/scsi/spapr_vscsi.c
> > > @@ -630,7 +630,7 @@ static void vscsi_save_request(QEMUFile *f,
> > > SCSIRequest *sreq)
> > >      vscsi_req *req = sreq->hba_private;
> > >      assert(req->active);
> > >
> > > -    vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL);
> > > +    vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL,
> > > &error_warn);
> > >
> > >      trace_spapr_vscsi_save_request(req->qtag, req->cur_desc_num,
> > >                                     req->cur_desc_offset);
> > > diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
> > > index
> > > 04d385d7674f444844beeee7364ee0424f762758..8e2b6d611de8efdf98235355226f5e5a6fd87b19
> > > 100644
> > > --- a/hw/vfio/pci.c
> > > +++ b/hw/vfio/pci.c
> > > @@ -2780,8 +2780,8 @@ static int vfio_pci_save_config(VFIODevice
> > > *vbasedev, QEMUFile *f, Error **errp)
> > >  {
> > >      VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev);
> > >
> > > -    return vmstate_save_state_with_err(f, &vmstate_vfio_pci_config, vdev,
> > > NULL,
> > > -                                       errp);
> > > +    return vmstate_save_state(f, &vmstate_vfio_pci_config, vdev, NULL,
> > > +                              errp);
> > >  }
> > >
> > >  static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f)
> > > diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c
> > > index
> > > f14f0487fec3ed30d2cc3aec96950c2121265716..58957990743f061e78d0eafcfcb6cc5989252edc
> > > 100644
> > > --- a/hw/virtio/virtio-mmio.c
> > > +++ b/hw/virtio/virtio-mmio.c
> > > @@ -613,7 +613,7 @@ static void virtio_mmio_save_extra_state(DeviceState
> > > *opaque, QEMUFile *f)
> > >  {
> > >      VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
> > >
> > > -    vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL);
> > > +    vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL, &error_warn);
> > >  }
> > >
> > >  static int virtio_mmio_load_extra_state(DeviceState *opaque, QEMUFile *f)
> > > diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
> > > index
> > > 07f144d6d1bfbb561b28015de71762601080dc23..0826399cdfbf09b5b3f9acd9967de3417858c9d2
> > > 100644
> > > --- a/hw/virtio/virtio-pci.c
> > > +++ b/hw/virtio/virtio-pci.c
> > > @@ -154,7 +154,7 @@ static void virtio_pci_save_extra_state(DeviceState
> > > *d, QEMUFile *f)
> > >  {
> > >      VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
> > >
> > > -    vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL);
> > > +    vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL, &error_warn);
> > >  }
> > >
> > >  static int virtio_pci_load_extra_state(DeviceState *d, QEMUFile *f)
> > > diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
> > > index
> > > aac317a93c3cd55ece9b0fd858c7b2459f8242f9..552e167529e9c22fb4cba2b445ffad85fef619f3
> > > 100644
> > > --- a/hw/virtio/virtio.c
> > > +++ b/hw/virtio/virtio.c
> > > @@ -3025,14 +3025,14 @@ int virtio_save(VirtIODevice *vdev, QEMUFile *f)
> > >      }
> > >
> > >      if (vdc->vmsd) {
> > > -        int ret = vmstate_save_state(f, vdc->vmsd, vdev, NULL);
> > > +        int ret = vmstate_save_state(f, vdc->vmsd, vdev, NULL,
> > > &error_warn);
> > >          if (ret) {
> > >              return ret;
> > >          }
> > >      }
> > >
> > >      /* Subsections */
> > > -    return vmstate_save_state(f, &vmstate_virtio, vdev, NULL);
> > > +    return vmstate_save_state(f, &vmstate_virtio, vdev, NULL,
> > > &error_warn);
> > >  }
> > >
> > >  /* A wrapper for use as a VMState .put function */
> > > diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
> > > index
> > > 056781b1c21e737583f081594d9f88b32adfd674..5fe9bbf39058d0cf97c1adab54cc516dbe8dc32a
> > > 100644
> > > --- a/include/migration/vmstate.h
> > > +++ b/include/migration/vmstate.h
> > > @@ -1198,8 +1198,6 @@ extern const VMStateInfo vmstate_info_qlist;
> > >  int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
> > >                         void *opaque, int version_id, Error **errp);
> > >  int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
> > > -                       void *opaque, JSONWriter *vmdesc);
> > > -int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription
> > > *vmsd,
> > >                         void *opaque, JSONWriter *vmdesc, Error **errp);
> > >  int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
> > >                           void *opaque, JSONWriter *vmdesc,
> > > diff --git a/migration/cpr.c b/migration/cpr.c
> > > index
> > > bdb24736f44e91ba59b6e622a315597c97e7f64d..d48f013dac21b531e41a26f4bb33eefe32097757
> > > 100644
> > > --- a/migration/cpr.c
> > > +++ b/migration/cpr.c
> > > @@ -163,6 +163,7 @@ bool cpr_is_incoming(void)
> > >
> > >  int cpr_state_save(MigrationChannel *channel, Error **errp)
> > >  {
> > > +    ERRP_GUARD();
> > >      int ret;
> > >      QEMUFile *f;
> > >      MigMode mode = migrate_mode();
> > > @@ -182,9 +183,9 @@ int cpr_state_save(MigrationChannel *channel, Error
> > > **errp)
> > >      qemu_put_be32(f, QEMU_CPR_FILE_MAGIC);
> > >      qemu_put_be32(f, QEMU_CPR_FILE_VERSION);
> > >
> > > -    ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0);
> > > +    ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0, errp);
> > >      if (ret) {
> > > -        error_setg(errp, "vmstate_save_state error %d", ret);
> > > +        error_prepend(errp, "vmstate_save_state error %d: ", ret);
> > >          qemu_fclose(f);
> > >          return ret;
> > >      }
> > > diff --git a/migration/savevm.c b/migration/savevm.c
> > > index
> > > 4df50fb5c674e56c0173c357c1e23367373e2114..3cb9c34efb22fdcb2b05cf1fefcb6fbd80cadca5
> > > 100644
> > > --- a/migration/savevm.c
> > > +++ b/migration/savevm.c
> > > @@ -1056,8 +1056,8 @@ static int vmstate_save(QEMUFile *f, SaveStateEntry
> > > *se, JSONWriter *vmdesc,
> > >      if (!se->vmsd) {
> > >          vmstate_save_old_style(f, se, vmdesc);
> > >      } else {
> > > -        ret = vmstate_save_state_with_err(f, se->vmsd, se->opaque, vmdesc,
> > > -                                          errp);
> > > +        ret = vmstate_save_state(f, se->vmsd, se->opaque, vmdesc,
> > > +                                 errp);
> > >          if (ret) {
> > >              return ret;
> > >          }
> > > @@ -1303,7 +1303,8 @@ void qemu_savevm_state_header(QEMUFile *f)
> > >              json_writer_start_object(vmdesc, "configuration");
> > >          }
> > >
> > > -        vmstate_save_state(f, &vmstate_configuration, &savevm_state,
> > > vmdesc);
> > > +        vmstate_save_state(f, &vmstate_configuration, &savevm_state,
> > > vmdesc,
> > > +                           &error_warn);
> > >
> > >          if (vmdesc) {
> > >              json_writer_end_object(vmdesc);
> > > diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c
> > > index
> > > e2020a733d0921d748b6f832a193e5de8d302d5f..2b88dc145ab32072f09845cee95b79aad7ac3cb2
> > > 100644
> > > --- a/migration/vmstate-types.c
> > > +++ b/migration/vmstate-types.c
> > > @@ -564,7 +564,7 @@ static int put_tmp(QEMUFile *f, void *pv, size_t size,
> > >
> > >      /* Writes the parent field which is at the start of the tmp */
> > >      *(void **)tmp = pv;
> > > -    ret = vmstate_save_state(f, vmsd, tmp, vmdesc);
> > > +    ret = vmstate_save_state(f, vmsd, tmp, vmdesc, &error_warn);
> > >      g_free(tmp);
> > >
> > >      return ret;
> > > @@ -675,7 +675,7 @@ static int put_qtailq(QEMUFile *f, void *pv, size_t
> > > unused_size,
> > >
> > >      QTAILQ_RAW_FOREACH(elm, pv, entry_offset) {
> > >          qemu_put_byte(f, true);
> > > -        ret = vmstate_save_state(f, vmsd, elm, vmdesc);
> > > +        ret = vmstate_save_state(f, vmsd, elm, vmdesc, &error_warn);
> > >          if (ret) {
> > >              return ret;
> > >          }
> > > @@ -712,7 +712,8 @@ static gboolean put_gtree_elem(gpointer key, gpointer
> > > value, gpointer data)
> > >      if (!capsule->key_vmsd) {
> > >          qemu_put_be64(f, (uint64_t)(uintptr_t)(key)); /* direct key */
> > >      } else {
> > > -        ret = vmstate_save_state(f, capsule->key_vmsd, key,
> > > capsule->vmdesc);
> > > +        ret = vmstate_save_state(f, capsule->key_vmsd, key,
> > > capsule->vmdesc,
> > > +                                 &error_warn);
> > >          if (ret) {
> > >              capsule->ret = ret;
> > >              return true;
> > > @@ -720,7 +721,8 @@ static gboolean put_gtree_elem(gpointer key, gpointer
> > > value, gpointer data)
> > >      }
> > >
> > >      /* put the data */
> > > -    ret = vmstate_save_state(f, capsule->val_vmsd, value,
> > > capsule->vmdesc);
> > > +    ret = vmstate_save_state(f, capsule->val_vmsd, value, capsule->vmdesc,
> > > +                             &error_warn);
> > >      if (ret) {
> > >          capsule->ret = ret;
> > >          return true;
> > > @@ -856,7 +858,7 @@ static int put_qlist(QEMUFile *f, void *pv, size_t
> > > unused_size,
> > >      trace_put_qlist(field->name, vmsd->name, vmsd->version_id);
> > >      QLIST_RAW_FOREACH(elm, pv, entry_offset) {
> > >          qemu_put_byte(f, true);
> > > -        ret = vmstate_save_state(f, vmsd, elm, vmdesc);
> > > +        ret = vmstate_save_state(f, vmsd, elm, vmdesc, &error_warn);
> > >          if (ret) {
> > >              error_report("%s: failed to save %s (%d)", field->name,
> > >                           vmsd->name, ret);
> > > diff --git a/migration/vmstate.c b/migration/vmstate.c
> > > index
> > > ef78a1e62ad92e9608de72d125da80ea496c8dd1..569e66ea983f833e6a0d651d2a751f34a64e8f5c
> > > 100644
> > > --- a/migration/vmstate.c
> > > +++ b/migration/vmstate.c
> > > @@ -403,12 +403,6 @@ bool vmstate_section_needed(const VMStateDescription
> > > *vmsd, void *opaque)
> > >
> > >
> > >  int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
> > > -                       void *opaque, JSONWriter *vmdesc_id)
> > > -{
> > > -    return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id,
> > > vmsd->version_id, NULL);
> > > -}
> > > -
> > > -int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription
> > > *vmsd,
> > >                         void *opaque, JSONWriter *vmdesc_id, Error **errp)
> > >  {
> > >      return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id,
> > > vmsd->version_id, errp);
> > > @@ -530,7 +524,7 @@ static int vmstate_save_dispatch(QEMUFile *f,
> > >
> > >                  if (inner_field->flags & VMS_STRUCT) {
> > >                      ret = vmstate_save_state(f, inner_field->vmsd,
> > > -                                             curr_elem, vmdesc_loop);
> > > +                                             curr_elem, vmdesc_loop,
> > > errp);
> > >                  } else if (inner_field->flags & VMS_VSTRUCT) {
> > >                      ret = vmstate_save_state_v(f, inner_field->vmsd,
> > >                                                 curr_elem, vmdesc_loop,
> > > @@ -539,6 +533,10 @@ static int vmstate_save_dispatch(QEMUFile *f,
> > >                  } else {
> > >                      ret = inner_field->info->put(f, curr_elem, size,
> > >                                                   inner_field,
> > > vmdesc_loop);
> > > +                    if (ret) {
> > > +                        error_setg(errp, "Save of field %s failed",
> > > +                                   inner_field->name);
> > > +                    }
> > >                  }
> > >
> > >                  written_bytes = qemu_file_transferred(f) - old_offset;
> > > @@ -551,8 +549,8 @@ static int vmstate_save_dispatch(QEMUFile *f,
> > >                  }
> > >
> > >                  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);
> > >                      ps_ret = post_save_dispatch(vmsd, opaque, &local_err);
> > >                      if (ps_ret) {
> > >                          ret = ps_ret;
> > > @@ -723,7 +721,7 @@ static int vmstate_subsection_save(QEMUFile *f, const
> > > VMStateDescription *vmsd,
> > >              qemu_put_byte(f, len);
> > >              qemu_put_buffer(f, (uint8_t *)vmsdsub->name, len);
> > >              qemu_put_be32(f, vmsdsub->version_id);
> > > -            ret = vmstate_save_state_with_err(f, vmsdsub, opaque, vmdesc,
> > > errp);
> > > +            ret = vmstate_save_state(f, vmsdsub, opaque, vmdesc, errp);
> > >              if (ret) {
> > >                  return ret;
> > >              }
> > > diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c
> > > index
> > > 807deb3531f3079864ac99567d4dece7122571dd..5f31ed4b6969c4ebf72b298f965bcbdc752ff87f
> > > 100644
> > > --- a/tests/unit/test-vmstate.c
> > > +++ b/tests/unit/test-vmstate.c
> > > @@ -69,7 +69,7 @@ static void save_vmstate(const VMStateDescription *desc,
> > > void *obj)
> > >      QEMUFile *f = open_test_file(true);
> > >
> > >      /* Save file with vmstate */
> > > -    int ret = vmstate_save_state(f, desc, obj, NULL);
> > > +    int ret = vmstate_save_state(f, desc, obj, NULL, &error_warn);
> > >      g_assert(!ret);
> > >      qemu_put_byte(f, QEMU_VM_EOF);
> > >      g_assert(!qemu_file_get_error(f));
> > > @@ -429,7 +429,8 @@ static void test_save_noskip(void)
> > >      QEMUFile *fsave = open_test_file(true);
> > >      TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
> > >                         .skip_c_e = false };
> > > -    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
> > > +    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL,
> > > +                                 &error_warn);
> > >      g_assert(!ret);
> > >      g_assert(!qemu_file_get_error(fsave));
> > >
> > > @@ -451,7 +452,8 @@ static void test_save_skip(void)
> > >      QEMUFile *fsave = open_test_file(true);
> > >      TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
> > >                         .skip_c_e = true };
> > > -    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
> > > +    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL,
> > > +                                 &error_warn);
> > >      g_assert(!ret);
> > >      g_assert(!qemu_file_get_error(fsave));
> > >
> > > diff --git a/ui/vdagent.c b/ui/vdagent.c
> > > index
> > > b9a22a689d9acfeafb862ae73308db6fbd52331e..e0fc7ff71468bfe66761385387653c742c375f05
> > > 100644
> > > --- a/ui/vdagent.c
> > > +++ b/ui/vdagent.c
> > > @@ -992,7 +992,8 @@ static int put_cbinfo(QEMUFile *f, void *pv, size_t
> > > size,
> > >          }
> > >      }
> > >
> > > -    return vmstate_save_state(f, &vmstate_cbinfo_array, &cbinfo, vmdesc);
> > > +    return vmstate_save_state(f, &vmstate_cbinfo_array, &cbinfo, vmdesc,
> > > +                              &error_warn);
> > >  }
> > >
> > >  static int get_cbinfo(QEMUFile *f, void *pv, size_t size,
> > >
> > > --
> > > 2.50.1
> > >
> > >
>
>
> Regards,
> Arun
>
>


-- 
Marc-André Lureau


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

* Re: [PATCH v9 20/27] migration: push Error **errp into loadvm_process_enable_colo()
  2025-08-06  9:56     ` Arun Menon
@ 2025-08-06 10:04       ` Marc-André Lureau
  2025-08-06 10:19         ` Arun Menon
  0 siblings, 1 reply; 63+ messages in thread
From: Marc-André Lureau @ 2025-08-06 10:04 UTC (permalink / raw)
  To: Arun Menon
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

Hi

On Wed, Aug 6, 2025 at 1:58 PM Arun Menon <armenon@redhat.com> wrote:
>
> Hi Marc-André,
> Thanks for the review and the Reviewed-by tag.
>
> On Wed, Aug 06, 2025 at 12:07:25PM +0400, Marc-André Lureau wrote:
> > Hi
> >
> > On Tue, Aug 5, 2025 at 10:31 PM Arun Menon <armenon@redhat.com> wrote:
> >
> > > This is an incremental step in converting vmstate loading
> > > code to report error via Error objects instead of directly
> > > printing it to console/monitor.
> > > It is ensured that loadvm_process_enable_colo() must report an error
> > > in errp, in case of failure.
> > >
> > > Signed-off-by: Arun Menon <armenon@redhat.com>
> > >
> >
> > Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> >
> >
> > > ---
> > >  include/migration/colo.h |  2 +-
> > >  migration/migration.c    | 12 ++++++------
> > >  migration/ram.c          |  8 ++++----
> > >  migration/ram.h          |  2 +-
> > >  migration/savevm.c       | 26 ++++++++++++++------------
> > >  5 files changed, 26 insertions(+), 24 deletions(-)
> > >
> > > diff --git a/include/migration/colo.h b/include/migration/colo.h
> > > index
> > > 43222ef5ae6adc3f7d8aa6a48bef79af33d09208..d4fe422e4d335d3bef4f860f56400fcd73287a0e
> > > 100644
> > > --- a/include/migration/colo.h
> > > +++ b/include/migration/colo.h
> > > @@ -25,7 +25,7 @@ void migrate_start_colo_process(MigrationState *s);
> > >  bool migration_in_colo_state(void);
> > >
> > >  /* loadvm */
> > > -int migration_incoming_enable_colo(void);
> > > +int migration_incoming_enable_colo(Error **errp);
> > >  void migration_incoming_disable_colo(void);
> > >  bool migration_incoming_colo_enabled(void);
> > >  bool migration_incoming_in_colo_state(void);
> > > diff --git a/migration/migration.c b/migration/migration.c
> > > index
> > > 6962dc7d7f3e0121d28994c98f12f9f2258343d7..4a76d7ed730589bae87115368b0bf4819f8b161e
> > > 100644
> > > --- a/migration/migration.c
> > > +++ b/migration/migration.c
> > > @@ -623,22 +623,22 @@ void migration_incoming_disable_colo(void)
> > >      migration_colo_enabled = false;
> > >  }
> > >
> > > -int migration_incoming_enable_colo(void)
> > > +int migration_incoming_enable_colo(Error **errp)
> > >  {
> > >  #ifndef CONFIG_REPLICATION
> > > -    error_report("ENABLE_COLO command come in migration stream, but the "
> > > -                 "replication module is not built in");
> > > +    error_setg(errp, "ENABLE_COLO command come in migration stream, but
> > > the "
> > > +               "replication module is not built in");
> > >      return -ENOTSUP;
> > >  #endif
> > >
> > >      if (!migrate_colo()) {
> > > -        error_report("ENABLE_COLO command come in migration stream, but
> > > x-colo "
> > > -                     "capability is not set");
> > > +        error_setg(errp, "ENABLE_COLO command come in migration stream"
> > > +                   ", but x-colo capability is not set");
> > >          return -EINVAL;
> > >      }
> > >
> > >      if (ram_block_discard_disable(true)) {
> > > -        error_report("COLO: cannot disable RAM discard");
> > > +        error_setg(errp, "COLO: cannot disable RAM discard");
> > >          return -EBUSY;
> > >      }
> > >      migration_colo_enabled = true;
> > > diff --git a/migration/ram.c b/migration/ram.c
> > > index
> > > 6a0dcc04f436524a37672c41c38f201f06773374..995431c9e320f443c385c29d664d62e18c1afd90
> > > 100644
> > > --- a/migration/ram.c
> > > +++ b/migration/ram.c
> > > @@ -3576,7 +3576,7 @@ static void colo_init_ram_state(void)
> > >   * memory of the secondary VM, it is need to hold the global lock
> > >   * to call this helper.
> > >   */
> > > -int colo_init_ram_cache(void)
> > > +int colo_init_ram_cache(Error **errp)
> > >  {
> > >      RAMBlock *block;
> > >
> > > @@ -3585,9 +3585,9 @@ int colo_init_ram_cache(void)
> > >              block->colo_cache = qemu_anon_ram_alloc(block->used_length,
> > >                                                      NULL, false, false);
> > >              if (!block->colo_cache) {
> > > -                error_report("%s: Can't alloc memory for COLO cache of
> > > block %s,"
> > > -                             "size 0x" RAM_ADDR_FMT, __func__,
> > > block->idstr,
> > > -                             block->used_length);
> > > +                error_setg(errp, "Can't alloc memory for COLO cache of "
> > > +                           "block %s, size 0x" RAM_ADDR_FMT,
> > > +                           block->idstr, block->used_length);
> > >                  RAMBLOCK_FOREACH_NOT_IGNORED(block) {
> > >                      if (block->colo_cache) {
> > >                          qemu_anon_ram_free(block->colo_cache,
> > > block->used_length);
> > > diff --git a/migration/ram.h b/migration/ram.h
> > > index
> > > 275709a99187f9429ccb4111e05281ec268ba0db..24cd0bf585762cfa1e86834dc03c6baeea2f0627
> > > 100644
> > > --- a/migration/ram.h
> > > +++ b/migration/ram.h
> > > @@ -109,7 +109,7 @@ void ramblock_set_file_bmap_atomic(RAMBlock *block,
> > > ram_addr_t offset,
> > >                                     bool set);
> > >
> > >  /* ram cache */
> > > -int colo_init_ram_cache(void);
> > > +int colo_init_ram_cache(Error **errp);
> > >  void colo_flush_ram_cache(void);
> > >  void colo_release_ram_cache(void);
> > >  void colo_incoming_start_dirty_log(void);
> > > diff --git a/migration/savevm.c b/migration/savevm.c
> > > index
> > > 938adb20270adbf9546b0884d0877c25c3f0f816..a6b71a958aeda31e89043f8103bfe2fc89542fb5
> > > 100644
> > > --- a/migration/savevm.c
> > > +++ b/migration/savevm.c
> > > @@ -2519,15 +2519,21 @@ static int
> > > loadvm_handle_recv_bitmap(MigrationIncomingState *mis,
> > >      return 0;
> > >  }
> > >
> > > -static int loadvm_process_enable_colo(MigrationIncomingState *mis)
> > > +static int loadvm_process_enable_colo(MigrationIncomingState *mis,
> > > +                                      Error **errp)
> > >  {
> > > -    int ret = migration_incoming_enable_colo();
> > > +    ERRP_GUARD();
> > > +    int ret;
> > >
> > > -    if (!ret) {
> > > -        ret = colo_init_ram_cache();
> > > -        if (ret) {
> > > -            migration_incoming_disable_colo();
> > > -        }
> > > +    ret = migration_incoming_enable_colo(errp);
> > > +    if (ret < 0) {
> > > +        return ret;
> > > +    }
> > > +
> > > +    ret = colo_init_ram_cache(errp);
> > > +    if (ret) {
> > >
> >
> > Note: here you keep the "ret" error condition !=0, ok.
> >
> > colo_init_ram_cache(), returns -errno on error. Although errno should
> > remain unchanged on success (during qemu_anon_ram_free etc), I think it
> > would be safer to convert the function to follow the recommended
> > bool-valued function for true on success / false on failure instead.
>
> There has been some discussion on this here:
> https://lore.kernel.org/all/aH5AtUcjI3HYXdBe@redhat.com/
> It has been advised not to use bool return.

Ok, but the usage of errno in that function seems fragile as other
functions could clear it before it is returned. Can you make another
patch to change the return value to -1 and document it?

thanks

>
> >
> >
> > > +        error_prepend(errp, "failed to init colo RAM cache: %d: ", ret);
> > > +        migration_incoming_disable_colo();
> > >
> >      }
> > >      return ret;
> > >  }
> > > @@ -2646,11 +2652,7 @@ static int loadvm_process_command(QEMUFile *f,
> > > Error **errp)
> > >          return loadvm_handle_recv_bitmap(mis, len, errp);
> > >
> > >      case MIG_CMD_ENABLE_COLO:
> > > -        ret = loadvm_process_enable_colo(mis);
> > > -        if (ret < 0) {
> > > -            error_setg(errp, "Failed to load device state command: %d",
> > > ret);
> > > -        }
> > > -        return ret;
> > > +        return loadvm_process_enable_colo(mis, errp);
> > >
> > >      case MIG_CMD_SWITCHOVER_START:
> > >          ret = loadvm_postcopy_handle_switchover_start();
> > >
> > > --
> > > 2.50.1
> > >
> > >
> Regards,
> Arun
>
>


-- 
Marc-André Lureau


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

* Re: [PATCH v9 20/27] migration: push Error **errp into loadvm_process_enable_colo()
  2025-08-06 10:04       ` Marc-André Lureau
@ 2025-08-06 10:19         ` Arun Menon
  0 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-06 10:19 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

Hi Marc-André,

On Wed, Aug 06, 2025 at 02:04:25PM +0400, Marc-André Lureau wrote:
> Hi
> 
> On Wed, Aug 6, 2025 at 1:58 PM Arun Menon <armenon@redhat.com> wrote:
> >
> > Hi Marc-André,
> > Thanks for the review and the Reviewed-by tag.
> >
> > On Wed, Aug 06, 2025 at 12:07:25PM +0400, Marc-André Lureau wrote:
> > > Hi
> > >
> > > On Tue, Aug 5, 2025 at 10:31 PM Arun Menon <armenon@redhat.com> wrote:
> > >
> > > > This is an incremental step in converting vmstate loading
> > > > code to report error via Error objects instead of directly
> > > > printing it to console/monitor.
> > > > It is ensured that loadvm_process_enable_colo() must report an error
> > > > in errp, in case of failure.
> > > >
> > > > Signed-off-by: Arun Menon <armenon@redhat.com>
> > > >
> > >
> > > Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > >
> > >
> > > > ---
> > > >  include/migration/colo.h |  2 +-
> > > >  migration/migration.c    | 12 ++++++------
> > > >  migration/ram.c          |  8 ++++----
> > > >  migration/ram.h          |  2 +-
> > > >  migration/savevm.c       | 26 ++++++++++++++------------
> > > >  5 files changed, 26 insertions(+), 24 deletions(-)
> > > >
> > > > diff --git a/include/migration/colo.h b/include/migration/colo.h
> > > > index
> > > > 43222ef5ae6adc3f7d8aa6a48bef79af33d09208..d4fe422e4d335d3bef4f860f56400fcd73287a0e
> > > > 100644
> > > > --- a/include/migration/colo.h
> > > > +++ b/include/migration/colo.h
> > > > @@ -25,7 +25,7 @@ void migrate_start_colo_process(MigrationState *s);
> > > >  bool migration_in_colo_state(void);
> > > >
> > > >  /* loadvm */
> > > > -int migration_incoming_enable_colo(void);
> > > > +int migration_incoming_enable_colo(Error **errp);
> > > >  void migration_incoming_disable_colo(void);
> > > >  bool migration_incoming_colo_enabled(void);
> > > >  bool migration_incoming_in_colo_state(void);
> > > > diff --git a/migration/migration.c b/migration/migration.c
> > > > index
> > > > 6962dc7d7f3e0121d28994c98f12f9f2258343d7..4a76d7ed730589bae87115368b0bf4819f8b161e
> > > > 100644
> > > > --- a/migration/migration.c
> > > > +++ b/migration/migration.c
> > > > @@ -623,22 +623,22 @@ void migration_incoming_disable_colo(void)
> > > >      migration_colo_enabled = false;
> > > >  }
> > > >
> > > > -int migration_incoming_enable_colo(void)
> > > > +int migration_incoming_enable_colo(Error **errp)
> > > >  {
> > > >  #ifndef CONFIG_REPLICATION
> > > > -    error_report("ENABLE_COLO command come in migration stream, but the "
> > > > -                 "replication module is not built in");
> > > > +    error_setg(errp, "ENABLE_COLO command come in migration stream, but
> > > > the "
> > > > +               "replication module is not built in");
> > > >      return -ENOTSUP;
> > > >  #endif
> > > >
> > > >      if (!migrate_colo()) {
> > > > -        error_report("ENABLE_COLO command come in migration stream, but
> > > > x-colo "
> > > > -                     "capability is not set");
> > > > +        error_setg(errp, "ENABLE_COLO command come in migration stream"
> > > > +                   ", but x-colo capability is not set");
> > > >          return -EINVAL;
> > > >      }
> > > >
> > > >      if (ram_block_discard_disable(true)) {
> > > > -        error_report("COLO: cannot disable RAM discard");
> > > > +        error_setg(errp, "COLO: cannot disable RAM discard");
> > > >          return -EBUSY;
> > > >      }
> > > >      migration_colo_enabled = true;
> > > > diff --git a/migration/ram.c b/migration/ram.c
> > > > index
> > > > 6a0dcc04f436524a37672c41c38f201f06773374..995431c9e320f443c385c29d664d62e18c1afd90
> > > > 100644
> > > > --- a/migration/ram.c
> > > > +++ b/migration/ram.c
> > > > @@ -3576,7 +3576,7 @@ static void colo_init_ram_state(void)
> > > >   * memory of the secondary VM, it is need to hold the global lock
> > > >   * to call this helper.
> > > >   */
> > > > -int colo_init_ram_cache(void)
> > > > +int colo_init_ram_cache(Error **errp)
> > > >  {
> > > >      RAMBlock *block;
> > > >
> > > > @@ -3585,9 +3585,9 @@ int colo_init_ram_cache(void)
> > > >              block->colo_cache = qemu_anon_ram_alloc(block->used_length,
> > > >                                                      NULL, false, false);
> > > >              if (!block->colo_cache) {
> > > > -                error_report("%s: Can't alloc memory for COLO cache of
> > > > block %s,"
> > > > -                             "size 0x" RAM_ADDR_FMT, __func__,
> > > > block->idstr,
> > > > -                             block->used_length);
> > > > +                error_setg(errp, "Can't alloc memory for COLO cache of "
> > > > +                           "block %s, size 0x" RAM_ADDR_FMT,
> > > > +                           block->idstr, block->used_length);
> > > >                  RAMBLOCK_FOREACH_NOT_IGNORED(block) {
> > > >                      if (block->colo_cache) {
> > > >                          qemu_anon_ram_free(block->colo_cache,
> > > > block->used_length);
> > > > diff --git a/migration/ram.h b/migration/ram.h
> > > > index
> > > > 275709a99187f9429ccb4111e05281ec268ba0db..24cd0bf585762cfa1e86834dc03c6baeea2f0627
> > > > 100644
> > > > --- a/migration/ram.h
> > > > +++ b/migration/ram.h
> > > > @@ -109,7 +109,7 @@ void ramblock_set_file_bmap_atomic(RAMBlock *block,
> > > > ram_addr_t offset,
> > > >                                     bool set);
> > > >
> > > >  /* ram cache */
> > > > -int colo_init_ram_cache(void);
> > > > +int colo_init_ram_cache(Error **errp);
> > > >  void colo_flush_ram_cache(void);
> > > >  void colo_release_ram_cache(void);
> > > >  void colo_incoming_start_dirty_log(void);
> > > > diff --git a/migration/savevm.c b/migration/savevm.c
> > > > index
> > > > 938adb20270adbf9546b0884d0877c25c3f0f816..a6b71a958aeda31e89043f8103bfe2fc89542fb5
> > > > 100644
> > > > --- a/migration/savevm.c
> > > > +++ b/migration/savevm.c
> > > > @@ -2519,15 +2519,21 @@ static int
> > > > loadvm_handle_recv_bitmap(MigrationIncomingState *mis,
> > > >      return 0;
> > > >  }
> > > >
> > > > -static int loadvm_process_enable_colo(MigrationIncomingState *mis)
> > > > +static int loadvm_process_enable_colo(MigrationIncomingState *mis,
> > > > +                                      Error **errp)
> > > >  {
> > > > -    int ret = migration_incoming_enable_colo();
> > > > +    ERRP_GUARD();
> > > > +    int ret;
> > > >
> > > > -    if (!ret) {
> > > > -        ret = colo_init_ram_cache();
> > > > -        if (ret) {
> > > > -            migration_incoming_disable_colo();
> > > > -        }
> > > > +    ret = migration_incoming_enable_colo(errp);
> > > > +    if (ret < 0) {
> > > > +        return ret;
> > > > +    }
> > > > +
> > > > +    ret = colo_init_ram_cache(errp);
> > > > +    if (ret) {
> > > >
> > >
> > > Note: here you keep the "ret" error condition !=0, ok.
> > >
> > > colo_init_ram_cache(), returns -errno on error. Although errno should
> > > remain unchanged on success (during qemu_anon_ram_free etc), I think it
> > > would be safer to convert the function to follow the recommended
> > > bool-valued function for true on success / false on failure instead.
> >
> > There has been some discussion on this here:
> > https://lore.kernel.org/all/aH5AtUcjI3HYXdBe@redhat.com/
> > It has been advised not to use bool return.
> 
> Ok, but the usage of errno in that function seems fragile as other
> functions could clear it before it is returned. Can you make another
> patch to change the return value to -1 and document it?
> 
Yes, shall do it. Thanks.

> thanks
> 
> >
> > >
> > >
> > > > +        error_prepend(errp, "failed to init colo RAM cache: %d: ", ret);
> > > > +        migration_incoming_disable_colo();
> > > >
> > >      }
> > > >      return ret;
> > > >  }
> > > > @@ -2646,11 +2652,7 @@ static int loadvm_process_command(QEMUFile *f,
> > > > Error **errp)
> > > >          return loadvm_handle_recv_bitmap(mis, len, errp);
> > > >
> > > >      case MIG_CMD_ENABLE_COLO:
> > > > -        ret = loadvm_process_enable_colo(mis);
> > > > -        if (ret < 0) {
> > > > -            error_setg(errp, "Failed to load device state command: %d",
> > > > ret);
> > > > -        }
> > > > -        return ret;
> > > > +        return loadvm_process_enable_colo(mis, errp);
> > > >
> > > >      case MIG_CMD_SWITCHOVER_START:
> > > >          ret = loadvm_postcopy_handle_switchover_start();
> > > >
> > > > --
> > > > 2.50.1
> > > >
> > > >
> > Regards,
> > Arun
> >
> >
> 
> 
> -- 
> Marc-André Lureau
> 

Regards,
Arun



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

* Re: [PATCH v9 25/27] migration: Remove error variant of vmstate_save_state() function
  2025-08-06 10:01       ` Marc-André Lureau
@ 2025-08-06 10:30         ` Arun Menon
  0 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-06 10:30 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

Hi Marc-André,

On Wed, Aug 06, 2025 at 02:01:18PM +0400, Marc-André Lureau wrote:
> Hi
> 
> On Wed, Aug 6, 2025 at 1:47 PM Arun Menon <armenon@redhat.com> wrote:
> >
> > Hi Marc-André,
> > Thanks for the review.
> >
> > On Wed, Aug 06, 2025 at 12:28:54PM +0400, Marc-André Lureau wrote:
> > > Hi
> > >
> > > On Tue, Aug 5, 2025 at 10:31 PM Arun Menon <armenon@redhat.com> wrote:
> > >
> > > > This commit removes the redundant vmstate_save_state_with_err()
> > > > function.
> > > >
> > > > Previously, commit 969298f9d7 introduced vmstate_save_state_with_err()
> > > > to handle error propagation, while vmstate_save_state() existed for
> > > > non-error scenarios.
> > > > This is because there were code paths where vmstate_save_state_v()
> > > > (called internally by vmstate_save_state) did not explicitly set
> > > > errors on failure.
> > > >
> > > > This change unifies error handling by
> > > >  - updating vmstate_save_state() to accept an Error **errp argument.
> > > >  - vmstate_save_state_v() ensures errors are set directly within the errp
> > > >    object, eliminating the need for two separate functions. Specifically
> > > >    errors are handled by storing them in errp in vmstate_save_dispatch()
> > > >
> > > > All calls to vmstate_save_state_with_err() are replaced with
> > > > vmstate_save_state(). This simplifies the API and improves code
> > > > maintainability.
> > > >
> > > > Signed-off-by: Arun Menon <armenon@redhat.com>
> > > >
> > >
> > > I realize that vmstate_save_state() callers progressively became silent on
> > > error with this series (switching to error_report to error_set). I suggest
> > > you make a preliminary patch to make vmstate_save_state() verbose by
> > > passing &error_warn to vmstate_save_state_v. wdyt?
> >
> > I am sorry, I did not understand the suggestion completely.
> > vmstate_save_state() callers have always been silent before. Even before this series.
> > Because, by default NULL was passed in vmstate_save_state_v().
> 
> But inner functions were calling error_report()
> 
> >
> > This commit in the series changes that by passing &error_warn to vmstate_save_state_v()
> > inside vmstate_save_state() and removing the need for alternate vmstate_save_state_with_err().
> >
> > Do you mean that we need to pass &error_warn to vmstate_save_state_v() in vmstate_save_state()
> > as a preliminary patch before this patch? Please correct me if I am wrong.
> 
> I think so, then errors are still reported until the parent handles
> the error reporting itself.
I see, shall do it. Thanks.
> 
> > >
> > > patch lgtm otherwise
> > >
> > >
> > >
> > >
> > > > ---
> > > >  hw/display/virtio-gpu.c     |  3 ++-
> > > >  hw/pci/pci.c                |  2 +-
> > > >  hw/s390x/virtio-ccw.c       |  2 +-
> > > >  hw/scsi/spapr_vscsi.c       |  2 +-
> > > >  hw/vfio/pci.c               |  4 ++--
> > > >  hw/virtio/virtio-mmio.c     |  2 +-
> > > >  hw/virtio/virtio-pci.c      |  2 +-
> > > >  hw/virtio/virtio.c          |  4 ++--
> > > >  include/migration/vmstate.h |  2 --
> > > >  migration/cpr.c             |  5 +++--
> > > >  migration/savevm.c          |  7 ++++---
> > > >  migration/vmstate-types.c   | 12 +++++++-----
> > > >  migration/vmstate.c         | 18 ++++++++----------
> > > >  tests/unit/test-vmstate.c   |  8 +++++---
> > > >  ui/vdagent.c                |  3 ++-
> > > >  15 files changed, 40 insertions(+), 36 deletions(-)
> > > >
> > > > diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
> > > > index
> > > > 5ff9f5e6dc0b6b112e36170318c900f893a73c5e..bddb26fa21312e5d0b2b6412cdf2a6c28dd06eb3
> > > > 100644
> > > > --- a/hw/display/virtio-gpu.c
> > > > +++ b/hw/display/virtio-gpu.c
> > > > @@ -1246,7 +1246,8 @@ 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);
> > > > +    return vmstate_save_state(f, &vmstate_virtio_gpu_scanouts, g, NULL,
> > > > +                              &error_warn);
> > > >  }
> > > >
> > > >  static bool virtio_gpu_load_restore_mapping(VirtIOGPU *g,
> > > > diff --git a/hw/pci/pci.c b/hw/pci/pci.c
> > > > index
> > > > 80545189980f176ca6a3dc9abce7043c8bc2708c..f7fb3931ff190c25ffa665ba80abe7737dbefdb6
> > > > 100644
> > > > --- a/hw/pci/pci.c
> > > > +++ b/hw/pci/pci.c
> > > > @@ -926,7 +926,7 @@ void pci_device_save(PCIDevice *s, QEMUFile *f)
> > > >       * 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);
> > > > +    vmstate_save_state(f, &vmstate_pci_device, s, NULL, &error_warn);
> > > >      /* Restore the interrupt status bit. */
> > > >      pci_update_irq_status(s);
> > > >  }
> > > > diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
> > > > index
> > > > bafcb9b76d81749925e31b5a0a1320b3332ad2cf..14304fd3e8901d28a7788266df2c7ef7334b691c
> > > > 100644
> > > > --- a/hw/s390x/virtio-ccw.c
> > > > +++ b/hw/s390x/virtio-ccw.c
> > > > @@ -1130,7 +1130,7 @@ 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);
> > > > +    vmstate_save_state(f, &vmstate_virtio_ccw_dev, dev, NULL,
> > > > &error_warn);
> > > >  }
> > > >
> > > >  static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
> > > > diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c
> > > > index
> > > > a4812afd0cc7c495080ef03a531c7d279af29b33..2204beb40c7cdc827db2bf86fb8612d8c09999ed
> > > > 100644
> > > > --- a/hw/scsi/spapr_vscsi.c
> > > > +++ b/hw/scsi/spapr_vscsi.c
> > > > @@ -630,7 +630,7 @@ static void vscsi_save_request(QEMUFile *f,
> > > > SCSIRequest *sreq)
> > > >      vscsi_req *req = sreq->hba_private;
> > > >      assert(req->active);
> > > >
> > > > -    vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL);
> > > > +    vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL,
> > > > &error_warn);
> > > >
> > > >      trace_spapr_vscsi_save_request(req->qtag, req->cur_desc_num,
> > > >                                     req->cur_desc_offset);
> > > > diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
> > > > index
> > > > 04d385d7674f444844beeee7364ee0424f762758..8e2b6d611de8efdf98235355226f5e5a6fd87b19
> > > > 100644
> > > > --- a/hw/vfio/pci.c
> > > > +++ b/hw/vfio/pci.c
> > > > @@ -2780,8 +2780,8 @@ static int vfio_pci_save_config(VFIODevice
> > > > *vbasedev, QEMUFile *f, Error **errp)
> > > >  {
> > > >      VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev);
> > > >
> > > > -    return vmstate_save_state_with_err(f, &vmstate_vfio_pci_config, vdev,
> > > > NULL,
> > > > -                                       errp);
> > > > +    return vmstate_save_state(f, &vmstate_vfio_pci_config, vdev, NULL,
> > > > +                              errp);
> > > >  }
> > > >
> > > >  static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f)
> > > > diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c
> > > > index
> > > > f14f0487fec3ed30d2cc3aec96950c2121265716..58957990743f061e78d0eafcfcb6cc5989252edc
> > > > 100644
> > > > --- a/hw/virtio/virtio-mmio.c
> > > > +++ b/hw/virtio/virtio-mmio.c
> > > > @@ -613,7 +613,7 @@ static void virtio_mmio_save_extra_state(DeviceState
> > > > *opaque, QEMUFile *f)
> > > >  {
> > > >      VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
> > > >
> > > > -    vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL);
> > > > +    vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL, &error_warn);
> > > >  }
> > > >
> > > >  static int virtio_mmio_load_extra_state(DeviceState *opaque, QEMUFile *f)
> > > > diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
> > > > index
> > > > 07f144d6d1bfbb561b28015de71762601080dc23..0826399cdfbf09b5b3f9acd9967de3417858c9d2
> > > > 100644
> > > > --- a/hw/virtio/virtio-pci.c
> > > > +++ b/hw/virtio/virtio-pci.c
> > > > @@ -154,7 +154,7 @@ static void virtio_pci_save_extra_state(DeviceState
> > > > *d, QEMUFile *f)
> > > >  {
> > > >      VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
> > > >
> > > > -    vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL);
> > > > +    vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL, &error_warn);
> > > >  }
> > > >
> > > >  static int virtio_pci_load_extra_state(DeviceState *d, QEMUFile *f)
> > > > diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
> > > > index
> > > > aac317a93c3cd55ece9b0fd858c7b2459f8242f9..552e167529e9c22fb4cba2b445ffad85fef619f3
> > > > 100644
> > > > --- a/hw/virtio/virtio.c
> > > > +++ b/hw/virtio/virtio.c
> > > > @@ -3025,14 +3025,14 @@ int virtio_save(VirtIODevice *vdev, QEMUFile *f)
> > > >      }
> > > >
> > > >      if (vdc->vmsd) {
> > > > -        int ret = vmstate_save_state(f, vdc->vmsd, vdev, NULL);
> > > > +        int ret = vmstate_save_state(f, vdc->vmsd, vdev, NULL,
> > > > &error_warn);
> > > >          if (ret) {
> > > >              return ret;
> > > >          }
> > > >      }
> > > >
> > > >      /* Subsections */
> > > > -    return vmstate_save_state(f, &vmstate_virtio, vdev, NULL);
> > > > +    return vmstate_save_state(f, &vmstate_virtio, vdev, NULL,
> > > > &error_warn);
> > > >  }
> > > >
> > > >  /* A wrapper for use as a VMState .put function */
> > > > diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
> > > > index
> > > > 056781b1c21e737583f081594d9f88b32adfd674..5fe9bbf39058d0cf97c1adab54cc516dbe8dc32a
> > > > 100644
> > > > --- a/include/migration/vmstate.h
> > > > +++ b/include/migration/vmstate.h
> > > > @@ -1198,8 +1198,6 @@ extern const VMStateInfo vmstate_info_qlist;
> > > >  int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
> > > >                         void *opaque, int version_id, Error **errp);
> > > >  int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
> > > > -                       void *opaque, JSONWriter *vmdesc);
> > > > -int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription
> > > > *vmsd,
> > > >                         void *opaque, JSONWriter *vmdesc, Error **errp);
> > > >  int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
> > > >                           void *opaque, JSONWriter *vmdesc,
> > > > diff --git a/migration/cpr.c b/migration/cpr.c
> > > > index
> > > > bdb24736f44e91ba59b6e622a315597c97e7f64d..d48f013dac21b531e41a26f4bb33eefe32097757
> > > > 100644
> > > > --- a/migration/cpr.c
> > > > +++ b/migration/cpr.c
> > > > @@ -163,6 +163,7 @@ bool cpr_is_incoming(void)
> > > >
> > > >  int cpr_state_save(MigrationChannel *channel, Error **errp)
> > > >  {
> > > > +    ERRP_GUARD();
> > > >      int ret;
> > > >      QEMUFile *f;
> > > >      MigMode mode = migrate_mode();
> > > > @@ -182,9 +183,9 @@ int cpr_state_save(MigrationChannel *channel, Error
> > > > **errp)
> > > >      qemu_put_be32(f, QEMU_CPR_FILE_MAGIC);
> > > >      qemu_put_be32(f, QEMU_CPR_FILE_VERSION);
> > > >
> > > > -    ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0);
> > > > +    ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0, errp);
> > > >      if (ret) {
> > > > -        error_setg(errp, "vmstate_save_state error %d", ret);
> > > > +        error_prepend(errp, "vmstate_save_state error %d: ", ret);
> > > >          qemu_fclose(f);
> > > >          return ret;
> > > >      }
> > > > diff --git a/migration/savevm.c b/migration/savevm.c
> > > > index
> > > > 4df50fb5c674e56c0173c357c1e23367373e2114..3cb9c34efb22fdcb2b05cf1fefcb6fbd80cadca5
> > > > 100644
> > > > --- a/migration/savevm.c
> > > > +++ b/migration/savevm.c
> > > > @@ -1056,8 +1056,8 @@ static int vmstate_save(QEMUFile *f, SaveStateEntry
> > > > *se, JSONWriter *vmdesc,
> > > >      if (!se->vmsd) {
> > > >          vmstate_save_old_style(f, se, vmdesc);
> > > >      } else {
> > > > -        ret = vmstate_save_state_with_err(f, se->vmsd, se->opaque, vmdesc,
> > > > -                                          errp);
> > > > +        ret = vmstate_save_state(f, se->vmsd, se->opaque, vmdesc,
> > > > +                                 errp);
> > > >          if (ret) {
> > > >              return ret;
> > > >          }
> > > > @@ -1303,7 +1303,8 @@ void qemu_savevm_state_header(QEMUFile *f)
> > > >              json_writer_start_object(vmdesc, "configuration");
> > > >          }
> > > >
> > > > -        vmstate_save_state(f, &vmstate_configuration, &savevm_state,
> > > > vmdesc);
> > > > +        vmstate_save_state(f, &vmstate_configuration, &savevm_state,
> > > > vmdesc,
> > > > +                           &error_warn);
> > > >
> > > >          if (vmdesc) {
> > > >              json_writer_end_object(vmdesc);
> > > > diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c
> > > > index
> > > > e2020a733d0921d748b6f832a193e5de8d302d5f..2b88dc145ab32072f09845cee95b79aad7ac3cb2
> > > > 100644
> > > > --- a/migration/vmstate-types.c
> > > > +++ b/migration/vmstate-types.c
> > > > @@ -564,7 +564,7 @@ static int put_tmp(QEMUFile *f, void *pv, size_t size,
> > > >
> > > >      /* Writes the parent field which is at the start of the tmp */
> > > >      *(void **)tmp = pv;
> > > > -    ret = vmstate_save_state(f, vmsd, tmp, vmdesc);
> > > > +    ret = vmstate_save_state(f, vmsd, tmp, vmdesc, &error_warn);
> > > >      g_free(tmp);
> > > >
> > > >      return ret;
> > > > @@ -675,7 +675,7 @@ static int put_qtailq(QEMUFile *f, void *pv, size_t
> > > > unused_size,
> > > >
> > > >      QTAILQ_RAW_FOREACH(elm, pv, entry_offset) {
> > > >          qemu_put_byte(f, true);
> > > > -        ret = vmstate_save_state(f, vmsd, elm, vmdesc);
> > > > +        ret = vmstate_save_state(f, vmsd, elm, vmdesc, &error_warn);
> > > >          if (ret) {
> > > >              return ret;
> > > >          }
> > > > @@ -712,7 +712,8 @@ static gboolean put_gtree_elem(gpointer key, gpointer
> > > > value, gpointer data)
> > > >      if (!capsule->key_vmsd) {
> > > >          qemu_put_be64(f, (uint64_t)(uintptr_t)(key)); /* direct key */
> > > >      } else {
> > > > -        ret = vmstate_save_state(f, capsule->key_vmsd, key,
> > > > capsule->vmdesc);
> > > > +        ret = vmstate_save_state(f, capsule->key_vmsd, key,
> > > > capsule->vmdesc,
> > > > +                                 &error_warn);
> > > >          if (ret) {
> > > >              capsule->ret = ret;
> > > >              return true;
> > > > @@ -720,7 +721,8 @@ static gboolean put_gtree_elem(gpointer key, gpointer
> > > > value, gpointer data)
> > > >      }
> > > >
> > > >      /* put the data */
> > > > -    ret = vmstate_save_state(f, capsule->val_vmsd, value,
> > > > capsule->vmdesc);
> > > > +    ret = vmstate_save_state(f, capsule->val_vmsd, value, capsule->vmdesc,
> > > > +                             &error_warn);
> > > >      if (ret) {
> > > >          capsule->ret = ret;
> > > >          return true;
> > > > @@ -856,7 +858,7 @@ static int put_qlist(QEMUFile *f, void *pv, size_t
> > > > unused_size,
> > > >      trace_put_qlist(field->name, vmsd->name, vmsd->version_id);
> > > >      QLIST_RAW_FOREACH(elm, pv, entry_offset) {
> > > >          qemu_put_byte(f, true);
> > > > -        ret = vmstate_save_state(f, vmsd, elm, vmdesc);
> > > > +        ret = vmstate_save_state(f, vmsd, elm, vmdesc, &error_warn);
> > > >          if (ret) {
> > > >              error_report("%s: failed to save %s (%d)", field->name,
> > > >                           vmsd->name, ret);
> > > > diff --git a/migration/vmstate.c b/migration/vmstate.c
> > > > index
> > > > ef78a1e62ad92e9608de72d125da80ea496c8dd1..569e66ea983f833e6a0d651d2a751f34a64e8f5c
> > > > 100644
> > > > --- a/migration/vmstate.c
> > > > +++ b/migration/vmstate.c
> > > > @@ -403,12 +403,6 @@ bool vmstate_section_needed(const VMStateDescription
> > > > *vmsd, void *opaque)
> > > >
> > > >
> > > >  int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
> > > > -                       void *opaque, JSONWriter *vmdesc_id)
> > > > -{
> > > > -    return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id,
> > > > vmsd->version_id, NULL);
> > > > -}
> > > > -
> > > > -int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription
> > > > *vmsd,
> > > >                         void *opaque, JSONWriter *vmdesc_id, Error **errp)
> > > >  {
> > > >      return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id,
> > > > vmsd->version_id, errp);
> > > > @@ -530,7 +524,7 @@ static int vmstate_save_dispatch(QEMUFile *f,
> > > >
> > > >                  if (inner_field->flags & VMS_STRUCT) {
> > > >                      ret = vmstate_save_state(f, inner_field->vmsd,
> > > > -                                             curr_elem, vmdesc_loop);
> > > > +                                             curr_elem, vmdesc_loop,
> > > > errp);
> > > >                  } else if (inner_field->flags & VMS_VSTRUCT) {
> > > >                      ret = vmstate_save_state_v(f, inner_field->vmsd,
> > > >                                                 curr_elem, vmdesc_loop,
> > > > @@ -539,6 +533,10 @@ static int vmstate_save_dispatch(QEMUFile *f,
> > > >                  } else {
> > > >                      ret = inner_field->info->put(f, curr_elem, size,
> > > >                                                   inner_field,
> > > > vmdesc_loop);
> > > > +                    if (ret) {
> > > > +                        error_setg(errp, "Save of field %s failed",
> > > > +                                   inner_field->name);
> > > > +                    }
> > > >                  }
> > > >
> > > >                  written_bytes = qemu_file_transferred(f) - old_offset;
> > > > @@ -551,8 +549,8 @@ static int vmstate_save_dispatch(QEMUFile *f,
> > > >                  }
> > > >
> > > >                  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);
> > > >                      ps_ret = post_save_dispatch(vmsd, opaque, &local_err);
> > > >                      if (ps_ret) {
> > > >                          ret = ps_ret;
> > > > @@ -723,7 +721,7 @@ static int vmstate_subsection_save(QEMUFile *f, const
> > > > VMStateDescription *vmsd,
> > > >              qemu_put_byte(f, len);
> > > >              qemu_put_buffer(f, (uint8_t *)vmsdsub->name, len);
> > > >              qemu_put_be32(f, vmsdsub->version_id);
> > > > -            ret = vmstate_save_state_with_err(f, vmsdsub, opaque, vmdesc,
> > > > errp);
> > > > +            ret = vmstate_save_state(f, vmsdsub, opaque, vmdesc, errp);
> > > >              if (ret) {
> > > >                  return ret;
> > > >              }
> > > > diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c
> > > > index
> > > > 807deb3531f3079864ac99567d4dece7122571dd..5f31ed4b6969c4ebf72b298f965bcbdc752ff87f
> > > > 100644
> > > > --- a/tests/unit/test-vmstate.c
> > > > +++ b/tests/unit/test-vmstate.c
> > > > @@ -69,7 +69,7 @@ static void save_vmstate(const VMStateDescription *desc,
> > > > void *obj)
> > > >      QEMUFile *f = open_test_file(true);
> > > >
> > > >      /* Save file with vmstate */
> > > > -    int ret = vmstate_save_state(f, desc, obj, NULL);
> > > > +    int ret = vmstate_save_state(f, desc, obj, NULL, &error_warn);
> > > >      g_assert(!ret);
> > > >      qemu_put_byte(f, QEMU_VM_EOF);
> > > >      g_assert(!qemu_file_get_error(f));
> > > > @@ -429,7 +429,8 @@ static void test_save_noskip(void)
> > > >      QEMUFile *fsave = open_test_file(true);
> > > >      TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
> > > >                         .skip_c_e = false };
> > > > -    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
> > > > +    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL,
> > > > +                                 &error_warn);
> > > >      g_assert(!ret);
> > > >      g_assert(!qemu_file_get_error(fsave));
> > > >
> > > > @@ -451,7 +452,8 @@ static void test_save_skip(void)
> > > >      QEMUFile *fsave = open_test_file(true);
> > > >      TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
> > > >                         .skip_c_e = true };
> > > > -    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
> > > > +    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL,
> > > > +                                 &error_warn);
> > > >      g_assert(!ret);
> > > >      g_assert(!qemu_file_get_error(fsave));
> > > >
> > > > diff --git a/ui/vdagent.c b/ui/vdagent.c
> > > > index
> > > > b9a22a689d9acfeafb862ae73308db6fbd52331e..e0fc7ff71468bfe66761385387653c742c375f05
> > > > 100644
> > > > --- a/ui/vdagent.c
> > > > +++ b/ui/vdagent.c
> > > > @@ -992,7 +992,8 @@ static int put_cbinfo(QEMUFile *f, void *pv, size_t
> > > > size,
> > > >          }
> > > >      }
> > > >
> > > > -    return vmstate_save_state(f, &vmstate_cbinfo_array, &cbinfo, vmdesc);
> > > > +    return vmstate_save_state(f, &vmstate_cbinfo_array, &cbinfo, vmdesc,
> > > > +                              &error_warn);
> > > >  }
> > > >
> > > >  static int get_cbinfo(QEMUFile *f, void *pv, size_t size,
> > > >
> > > > --
> > > > 2.50.1
> > > >
> > > >
> >
> >
> > Regards,
> > Arun
> >
> >
> 
> 
> -- 
> Marc-André Lureau
> 
Regards,
Arun Menon



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

* Re: [PATCH v9 25/27] migration: Remove error variant of vmstate_save_state() function
  2025-08-06  8:28   ` Marc-André Lureau
  2025-08-06  9:46     ` Arun Menon
@ 2025-08-06 11:16     ` Arun Menon
  1 sibling, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-06 11:16 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

Hi Marc-André,

On Wed, Aug 06, 2025 at 12:28:54PM +0400, Marc-André Lureau wrote:
> Hi
> 
> On Tue, Aug 5, 2025 at 10:31 PM Arun Menon <armenon@redhat.com> wrote:
> 
> > This commit removes the redundant vmstate_save_state_with_err()
> > function.
> >
> > Previously, commit 969298f9d7 introduced vmstate_save_state_with_err()
> > to handle error propagation, while vmstate_save_state() existed for
> > non-error scenarios.
> > This is because there were code paths where vmstate_save_state_v()
> > (called internally by vmstate_save_state) did not explicitly set
> > errors on failure.
> >
> > This change unifies error handling by
> >  - updating vmstate_save_state() to accept an Error **errp argument.
> >  - vmstate_save_state_v() ensures errors are set directly within the errp
> >    object, eliminating the need for two separate functions. Specifically
> >    errors are handled by storing them in errp in vmstate_save_dispatch()
> >
> > All calls to vmstate_save_state_with_err() are replaced with
> > vmstate_save_state(). This simplifies the API and improves code
> > maintainability.
> >
> > Signed-off-by: Arun Menon <armenon@redhat.com>
> >
> 
> I realize that vmstate_save_state() callers progressively became silent on
> error with this series (switching to error_report to error_set). I suggest
> you make a preliminary patch to make vmstate_save_state() verbose by
> passing &error_warn to vmstate_save_state_v. wdyt?

On further examining, I see that vmstate_save_state_v() was never changed
from error_report to error_setg. There is only one error_report() call in
vmstate_save_state_v(), see below,

} else {                                                         
            if (field->flags & VMS_MUST_EXIST) {                         
                error_report("Output state validation failed: %s/%s",    
                        vmsd->name, field->name);                        
                assert(!(field->flags & VMS_MUST_EXIST));    


So if a caller called vmstate_save_state(), inturn calling vmstate_save_state_v()
that passed NULL, error_report() within the function should report that error.
This series did not convert any error_report to error_setg in vmstate_save_state_v().

Sorry to bring this up again. I was wondering do we still need an extra patch
in this case.

> 
> patch lgtm otherwise
> 
> 
> 
> 
> > ---
> >  hw/display/virtio-gpu.c     |  3 ++-
> >  hw/pci/pci.c                |  2 +-
> >  hw/s390x/virtio-ccw.c       |  2 +-
> >  hw/scsi/spapr_vscsi.c       |  2 +-
> >  hw/vfio/pci.c               |  4 ++--
> >  hw/virtio/virtio-mmio.c     |  2 +-
> >  hw/virtio/virtio-pci.c      |  2 +-
> >  hw/virtio/virtio.c          |  4 ++--
> >  include/migration/vmstate.h |  2 --
> >  migration/cpr.c             |  5 +++--
> >  migration/savevm.c          |  7 ++++---
> >  migration/vmstate-types.c   | 12 +++++++-----
> >  migration/vmstate.c         | 18 ++++++++----------
> >  tests/unit/test-vmstate.c   |  8 +++++---
> >  ui/vdagent.c                |  3 ++-
> >  15 files changed, 40 insertions(+), 36 deletions(-)
> >
> > diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
> > index
> > 5ff9f5e6dc0b6b112e36170318c900f893a73c5e..bddb26fa21312e5d0b2b6412cdf2a6c28dd06eb3
> > 100644
> > --- a/hw/display/virtio-gpu.c
> > +++ b/hw/display/virtio-gpu.c
> > @@ -1246,7 +1246,8 @@ 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);
> > +    return vmstate_save_state(f, &vmstate_virtio_gpu_scanouts, g, NULL,
> > +                              &error_warn);
> >  }
> >
> >  static bool virtio_gpu_load_restore_mapping(VirtIOGPU *g,
> > diff --git a/hw/pci/pci.c b/hw/pci/pci.c
> > index
> > 80545189980f176ca6a3dc9abce7043c8bc2708c..f7fb3931ff190c25ffa665ba80abe7737dbefdb6
> > 100644
> > --- a/hw/pci/pci.c
> > +++ b/hw/pci/pci.c
> > @@ -926,7 +926,7 @@ void pci_device_save(PCIDevice *s, QEMUFile *f)
> >       * 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);
> > +    vmstate_save_state(f, &vmstate_pci_device, s, NULL, &error_warn);
> >      /* Restore the interrupt status bit. */
> >      pci_update_irq_status(s);
> >  }
> > diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
> > index
> > bafcb9b76d81749925e31b5a0a1320b3332ad2cf..14304fd3e8901d28a7788266df2c7ef7334b691c
> > 100644
> > --- a/hw/s390x/virtio-ccw.c
> > +++ b/hw/s390x/virtio-ccw.c
> > @@ -1130,7 +1130,7 @@ 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);
> > +    vmstate_save_state(f, &vmstate_virtio_ccw_dev, dev, NULL,
> > &error_warn);
> >  }
> >
> >  static int virtio_ccw_load_config(DeviceState *d, QEMUFile *f)
> > diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c
> > index
> > a4812afd0cc7c495080ef03a531c7d279af29b33..2204beb40c7cdc827db2bf86fb8612d8c09999ed
> > 100644
> > --- a/hw/scsi/spapr_vscsi.c
> > +++ b/hw/scsi/spapr_vscsi.c
> > @@ -630,7 +630,7 @@ static void vscsi_save_request(QEMUFile *f,
> > SCSIRequest *sreq)
> >      vscsi_req *req = sreq->hba_private;
> >      assert(req->active);
> >
> > -    vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL);
> > +    vmstate_save_state(f, &vmstate_spapr_vscsi_req, req, NULL,
> > &error_warn);
> >
> >      trace_spapr_vscsi_save_request(req->qtag, req->cur_desc_num,
> >                                     req->cur_desc_offset);
> > diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
> > index
> > 04d385d7674f444844beeee7364ee0424f762758..8e2b6d611de8efdf98235355226f5e5a6fd87b19
> > 100644
> > --- a/hw/vfio/pci.c
> > +++ b/hw/vfio/pci.c
> > @@ -2780,8 +2780,8 @@ static int vfio_pci_save_config(VFIODevice
> > *vbasedev, QEMUFile *f, Error **errp)
> >  {
> >      VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev);
> >
> > -    return vmstate_save_state_with_err(f, &vmstate_vfio_pci_config, vdev,
> > NULL,
> > -                                       errp);
> > +    return vmstate_save_state(f, &vmstate_vfio_pci_config, vdev, NULL,
> > +                              errp);
> >  }
> >
> >  static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f)
> > diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c
> > index
> > f14f0487fec3ed30d2cc3aec96950c2121265716..58957990743f061e78d0eafcfcb6cc5989252edc
> > 100644
> > --- a/hw/virtio/virtio-mmio.c
> > +++ b/hw/virtio/virtio-mmio.c
> > @@ -613,7 +613,7 @@ static void virtio_mmio_save_extra_state(DeviceState
> > *opaque, QEMUFile *f)
> >  {
> >      VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
> >
> > -    vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL);
> > +    vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL, &error_warn);
> >  }
> >
> >  static int virtio_mmio_load_extra_state(DeviceState *opaque, QEMUFile *f)
> > diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
> > index
> > 07f144d6d1bfbb561b28015de71762601080dc23..0826399cdfbf09b5b3f9acd9967de3417858c9d2
> > 100644
> > --- a/hw/virtio/virtio-pci.c
> > +++ b/hw/virtio/virtio-pci.c
> > @@ -154,7 +154,7 @@ static void virtio_pci_save_extra_state(DeviceState
> > *d, QEMUFile *f)
> >  {
> >      VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
> >
> > -    vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL);
> > +    vmstate_save_state(f, &vmstate_virtio_pci, proxy, NULL, &error_warn);
> >  }
> >
> >  static int virtio_pci_load_extra_state(DeviceState *d, QEMUFile *f)
> > diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
> > index
> > aac317a93c3cd55ece9b0fd858c7b2459f8242f9..552e167529e9c22fb4cba2b445ffad85fef619f3
> > 100644
> > --- a/hw/virtio/virtio.c
> > +++ b/hw/virtio/virtio.c
> > @@ -3025,14 +3025,14 @@ int virtio_save(VirtIODevice *vdev, QEMUFile *f)
> >      }
> >
> >      if (vdc->vmsd) {
> > -        int ret = vmstate_save_state(f, vdc->vmsd, vdev, NULL);
> > +        int ret = vmstate_save_state(f, vdc->vmsd, vdev, NULL,
> > &error_warn);
> >          if (ret) {
> >              return ret;
> >          }
> >      }
> >
> >      /* Subsections */
> > -    return vmstate_save_state(f, &vmstate_virtio, vdev, NULL);
> > +    return vmstate_save_state(f, &vmstate_virtio, vdev, NULL,
> > &error_warn);
> >  }
> >
> >  /* A wrapper for use as a VMState .put function */
> > diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
> > index
> > 056781b1c21e737583f081594d9f88b32adfd674..5fe9bbf39058d0cf97c1adab54cc516dbe8dc32a
> > 100644
> > --- a/include/migration/vmstate.h
> > +++ b/include/migration/vmstate.h
> > @@ -1198,8 +1198,6 @@ extern const VMStateInfo vmstate_info_qlist;
> >  int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
> >                         void *opaque, int version_id, Error **errp);
> >  int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
> > -                       void *opaque, JSONWriter *vmdesc);
> > -int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription
> > *vmsd,
> >                         void *opaque, JSONWriter *vmdesc, Error **errp);
> >  int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
> >                           void *opaque, JSONWriter *vmdesc,
> > diff --git a/migration/cpr.c b/migration/cpr.c
> > index
> > bdb24736f44e91ba59b6e622a315597c97e7f64d..d48f013dac21b531e41a26f4bb33eefe32097757
> > 100644
> > --- a/migration/cpr.c
> > +++ b/migration/cpr.c
> > @@ -163,6 +163,7 @@ bool cpr_is_incoming(void)
> >
> >  int cpr_state_save(MigrationChannel *channel, Error **errp)
> >  {
> > +    ERRP_GUARD();
> >      int ret;
> >      QEMUFile *f;
> >      MigMode mode = migrate_mode();
> > @@ -182,9 +183,9 @@ int cpr_state_save(MigrationChannel *channel, Error
> > **errp)
> >      qemu_put_be32(f, QEMU_CPR_FILE_MAGIC);
> >      qemu_put_be32(f, QEMU_CPR_FILE_VERSION);
> >
> > -    ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0);
> > +    ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0, errp);
> >      if (ret) {
> > -        error_setg(errp, "vmstate_save_state error %d", ret);
> > +        error_prepend(errp, "vmstate_save_state error %d: ", ret);
> >          qemu_fclose(f);
> >          return ret;
> >      }
> > diff --git a/migration/savevm.c b/migration/savevm.c
> > index
> > 4df50fb5c674e56c0173c357c1e23367373e2114..3cb9c34efb22fdcb2b05cf1fefcb6fbd80cadca5
> > 100644
> > --- a/migration/savevm.c
> > +++ b/migration/savevm.c
> > @@ -1056,8 +1056,8 @@ static int vmstate_save(QEMUFile *f, SaveStateEntry
> > *se, JSONWriter *vmdesc,
> >      if (!se->vmsd) {
> >          vmstate_save_old_style(f, se, vmdesc);
> >      } else {
> > -        ret = vmstate_save_state_with_err(f, se->vmsd, se->opaque, vmdesc,
> > -                                          errp);
> > +        ret = vmstate_save_state(f, se->vmsd, se->opaque, vmdesc,
> > +                                 errp);
> >          if (ret) {
> >              return ret;
> >          }
> > @@ -1303,7 +1303,8 @@ void qemu_savevm_state_header(QEMUFile *f)
> >              json_writer_start_object(vmdesc, "configuration");
> >          }
> >
> > -        vmstate_save_state(f, &vmstate_configuration, &savevm_state,
> > vmdesc);
> > +        vmstate_save_state(f, &vmstate_configuration, &savevm_state,
> > vmdesc,
> > +                           &error_warn);
> >
> >          if (vmdesc) {
> >              json_writer_end_object(vmdesc);
> > diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c
> > index
> > e2020a733d0921d748b6f832a193e5de8d302d5f..2b88dc145ab32072f09845cee95b79aad7ac3cb2
> > 100644
> > --- a/migration/vmstate-types.c
> > +++ b/migration/vmstate-types.c
> > @@ -564,7 +564,7 @@ static int put_tmp(QEMUFile *f, void *pv, size_t size,
> >
> >      /* Writes the parent field which is at the start of the tmp */
> >      *(void **)tmp = pv;
> > -    ret = vmstate_save_state(f, vmsd, tmp, vmdesc);
> > +    ret = vmstate_save_state(f, vmsd, tmp, vmdesc, &error_warn);
> >      g_free(tmp);
> >
> >      return ret;
> > @@ -675,7 +675,7 @@ static int put_qtailq(QEMUFile *f, void *pv, size_t
> > unused_size,
> >
> >      QTAILQ_RAW_FOREACH(elm, pv, entry_offset) {
> >          qemu_put_byte(f, true);
> > -        ret = vmstate_save_state(f, vmsd, elm, vmdesc);
> > +        ret = vmstate_save_state(f, vmsd, elm, vmdesc, &error_warn);
> >          if (ret) {
> >              return ret;
> >          }
> > @@ -712,7 +712,8 @@ static gboolean put_gtree_elem(gpointer key, gpointer
> > value, gpointer data)
> >      if (!capsule->key_vmsd) {
> >          qemu_put_be64(f, (uint64_t)(uintptr_t)(key)); /* direct key */
> >      } else {
> > -        ret = vmstate_save_state(f, capsule->key_vmsd, key,
> > capsule->vmdesc);
> > +        ret = vmstate_save_state(f, capsule->key_vmsd, key,
> > capsule->vmdesc,
> > +                                 &error_warn);
> >          if (ret) {
> >              capsule->ret = ret;
> >              return true;
> > @@ -720,7 +721,8 @@ static gboolean put_gtree_elem(gpointer key, gpointer
> > value, gpointer data)
> >      }
> >
> >      /* put the data */
> > -    ret = vmstate_save_state(f, capsule->val_vmsd, value,
> > capsule->vmdesc);
> > +    ret = vmstate_save_state(f, capsule->val_vmsd, value, capsule->vmdesc,
> > +                             &error_warn);
> >      if (ret) {
> >          capsule->ret = ret;
> >          return true;
> > @@ -856,7 +858,7 @@ static int put_qlist(QEMUFile *f, void *pv, size_t
> > unused_size,
> >      trace_put_qlist(field->name, vmsd->name, vmsd->version_id);
> >      QLIST_RAW_FOREACH(elm, pv, entry_offset) {
> >          qemu_put_byte(f, true);
> > -        ret = vmstate_save_state(f, vmsd, elm, vmdesc);
> > +        ret = vmstate_save_state(f, vmsd, elm, vmdesc, &error_warn);
> >          if (ret) {
> >              error_report("%s: failed to save %s (%d)", field->name,
> >                           vmsd->name, ret);
> > diff --git a/migration/vmstate.c b/migration/vmstate.c
> > index
> > ef78a1e62ad92e9608de72d125da80ea496c8dd1..569e66ea983f833e6a0d651d2a751f34a64e8f5c
> > 100644
> > --- a/migration/vmstate.c
> > +++ b/migration/vmstate.c
> > @@ -403,12 +403,6 @@ bool vmstate_section_needed(const VMStateDescription
> > *vmsd, void *opaque)
> >
> >
> >  int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
> > -                       void *opaque, JSONWriter *vmdesc_id)
> > -{
> > -    return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id,
> > vmsd->version_id, NULL);
> > -}
> > -
> > -int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription
> > *vmsd,
> >                         void *opaque, JSONWriter *vmdesc_id, Error **errp)
> >  {
> >      return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id,
> > vmsd->version_id, errp);
> > @@ -530,7 +524,7 @@ static int vmstate_save_dispatch(QEMUFile *f,
> >
> >                  if (inner_field->flags & VMS_STRUCT) {
> >                      ret = vmstate_save_state(f, inner_field->vmsd,
> > -                                             curr_elem, vmdesc_loop);
> > +                                             curr_elem, vmdesc_loop,
> > errp);
> >                  } else if (inner_field->flags & VMS_VSTRUCT) {
> >                      ret = vmstate_save_state_v(f, inner_field->vmsd,
> >                                                 curr_elem, vmdesc_loop,
> > @@ -539,6 +533,10 @@ static int vmstate_save_dispatch(QEMUFile *f,
> >                  } else {
> >                      ret = inner_field->info->put(f, curr_elem, size,
> >                                                   inner_field,
> > vmdesc_loop);
> > +                    if (ret) {
> > +                        error_setg(errp, "Save of field %s failed",
> > +                                   inner_field->name);
> > +                    }
> >                  }
> >
> >                  written_bytes = qemu_file_transferred(f) - old_offset;
> > @@ -551,8 +549,8 @@ static int vmstate_save_dispatch(QEMUFile *f,
> >                  }
> >
> >                  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);
> >                      ps_ret = post_save_dispatch(vmsd, opaque, &local_err);
> >                      if (ps_ret) {
> >                          ret = ps_ret;
> > @@ -723,7 +721,7 @@ static int vmstate_subsection_save(QEMUFile *f, const
> > VMStateDescription *vmsd,
> >              qemu_put_byte(f, len);
> >              qemu_put_buffer(f, (uint8_t *)vmsdsub->name, len);
> >              qemu_put_be32(f, vmsdsub->version_id);
> > -            ret = vmstate_save_state_with_err(f, vmsdsub, opaque, vmdesc,
> > errp);
> > +            ret = vmstate_save_state(f, vmsdsub, opaque, vmdesc, errp);
> >              if (ret) {
> >                  return ret;
> >              }
> > diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c
> > index
> > 807deb3531f3079864ac99567d4dece7122571dd..5f31ed4b6969c4ebf72b298f965bcbdc752ff87f
> > 100644
> > --- a/tests/unit/test-vmstate.c
> > +++ b/tests/unit/test-vmstate.c
> > @@ -69,7 +69,7 @@ static void save_vmstate(const VMStateDescription *desc,
> > void *obj)
> >      QEMUFile *f = open_test_file(true);
> >
> >      /* Save file with vmstate */
> > -    int ret = vmstate_save_state(f, desc, obj, NULL);
> > +    int ret = vmstate_save_state(f, desc, obj, NULL, &error_warn);
> >      g_assert(!ret);
> >      qemu_put_byte(f, QEMU_VM_EOF);
> >      g_assert(!qemu_file_get_error(f));
> > @@ -429,7 +429,8 @@ static void test_save_noskip(void)
> >      QEMUFile *fsave = open_test_file(true);
> >      TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
> >                         .skip_c_e = false };
> > -    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
> > +    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL,
> > +                                 &error_warn);
> >      g_assert(!ret);
> >      g_assert(!qemu_file_get_error(fsave));
> >
> > @@ -451,7 +452,8 @@ static void test_save_skip(void)
> >      QEMUFile *fsave = open_test_file(true);
> >      TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
> >                         .skip_c_e = true };
> > -    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL);
> > +    int ret = vmstate_save_state(fsave, &vmstate_skipping, &obj, NULL,
> > +                                 &error_warn);
> >      g_assert(!ret);
> >      g_assert(!qemu_file_get_error(fsave));
> >
> > diff --git a/ui/vdagent.c b/ui/vdagent.c
> > index
> > b9a22a689d9acfeafb862ae73308db6fbd52331e..e0fc7ff71468bfe66761385387653c742c375f05
> > 100644
> > --- a/ui/vdagent.c
> > +++ b/ui/vdagent.c
> > @@ -992,7 +992,8 @@ static int put_cbinfo(QEMUFile *f, void *pv, size_t
> > size,
> >          }
> >      }
> >
> > -    return vmstate_save_state(f, &vmstate_cbinfo_array, &cbinfo, vmdesc);
> > +    return vmstate_save_state(f, &vmstate_cbinfo_array, &cbinfo, vmdesc,
> > +                              &error_warn);
> >  }
> >
> >  static int get_cbinfo(QEMUFile *f, void *pv, size_t size,
> >
> > --
> > 2.50.1
> >
> >

Regards,
Arun Menon



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

* Re: [PATCH v9 18/27] migration: push Error **errp into loadvm_postcopy_ram_handle_discard()
  2025-08-06  7:54   ` Marc-André Lureau
@ 2025-08-07  6:06     ` Arun Menon
  0 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-07  6:06 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Akihiko Odaki, Dmitry Osipenko, Michael S. Tsirkin,
	Marcel Apfelbaum, Cornelia Huck, Halil Pasic, Eric Farman,
	Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare, qemu-s390x,
	qemu-ppc, Hailiang Zhang, Stefan Berger

Hi Marc-André,

Thank you for the review.

On Wed, Aug 06, 2025 at 11:54:36AM +0400, Marc-André Lureau wrote:
> Hi
> 
> On Tue, Aug 5, 2025 at 10:30 PM Arun Menon <armenon@redhat.com> wrote:
> 
> > This is an incremental step in converting vmstate loading
> > code to report error via Error objects instead of directly
> > printing it to console/monitor.
> > It is ensured that loadvm_postcopy_ram_handle_discard() must report an
> > error
> > in errp, in case of failure.
> >
> > Signed-off-by: Arun Menon <armenon@redhat.com>
> >
> 
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> 
> > ---
> >  migration/savevm.c | 26 +++++++++++++-------------
> >  1 file changed, 13 insertions(+), 13 deletions(-)
> >
> > diff --git a/migration/savevm.c b/migration/savevm.c
> > index
> > eb90873a750ded354b3db31cba40b44d1be79864..3abe4193e02aae9c813ff07fb388a7ee470c8a6a
> > 100644
> > --- a/migration/savevm.c
> > +++ b/migration/savevm.c
> > @@ -2004,7 +2004,7 @@ static int
> > loadvm_postcopy_handle_advise(MigrationIncomingState *mis,
> >   * There can be 0..many of these messages, each encoding multiple pages.
> >   */
> >  static int loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis,
> > -                                              uint16_t len)
> > +                                              uint16_t len, Error **errp)
> >  {
> >      int tmp;
> >      char ramid[256];
> > @@ -2017,6 +2017,7 @@ static int
> > loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis,
> >          /* 1st discard */
> >          tmp = postcopy_ram_prepare_discard(mis);
> >          if (tmp) {
> > +            error_setg(errp, "Failed to prepare for RAM discard: %d",
> > tmp);
> >              return tmp;
> >          }
> >          break;
> > @@ -2026,8 +2027,9 @@ static int
> > loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis,
> >          break;
> >
> >      default:
> > -        error_report("CMD_POSTCOPY_RAM_DISCARD in wrong postcopy state
> > (%d)",
> > -                     ps);
> > +        error_setg(errp,
> > +                   "CMD_POSTCOPY_RAM_DISCARD in wrong postcopy state
> > (%d)",
> > +                   ps);
> >          return -1;
> >      }
> >      /* We're expecting a
> > @@ -2036,29 +2038,30 @@ static int
> > loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis,
> >       *    then at least 1 16 byte chunk
> >      */
> >      if (len < (1 + 1 + 1 + 1 + 2 * 8)) {
> > -        error_report("CMD_POSTCOPY_RAM_DISCARD invalid length (%d)", len);
> > +        error_setg(errp, "CMD_POSTCOPY_RAM_DISCARD invalid length (%d)",
> > len);
> >          return -1;
> >      }
> >
> >      tmp = qemu_get_byte(mis->from_src_file);
> >      if (tmp != postcopy_ram_discard_version) {
> > -        error_report("CMD_POSTCOPY_RAM_DISCARD invalid version (%d)",
> > tmp);
> > +        error_setg(errp, "CMD_POSTCOPY_RAM_DISCARD invalid version (%d)",
> > tmp);
> >          return -1;
> >      }
> >
> >      if (!qemu_get_counted_string(mis->from_src_file, ramid)) {
> > -        error_report("CMD_POSTCOPY_RAM_DISCARD Failed to read RAMBlock
> > ID");
> > +        error_setg(errp,
> > +                   "CMD_POSTCOPY_RAM_DISCARD Failed to read RAMBlock ID");
> >          return -1;
> >      }
> >      tmp = qemu_get_byte(mis->from_src_file);
> >      if (tmp != 0) {
> > -        error_report("CMD_POSTCOPY_RAM_DISCARD missing nil (%d)", tmp);
> > +        error_setg(errp, "CMD_POSTCOPY_RAM_DISCARD missing nil (%d)",
> > tmp);
> >          return -1;
> >      }
> >
> >      len -= 3 + strlen(ramid);
> >      if (len % 16) {
> > -        error_report("CMD_POSTCOPY_RAM_DISCARD invalid length (%d)", len);
> > +        error_setg(errp, "CMD_POSTCOPY_RAM_DISCARD invalid length (%d)",
> > len);
> >          return -1;
> >      }
> >      trace_loadvm_postcopy_ram_handle_discard_header(ramid, len);
> > @@ -2070,6 +2073,7 @@ static int
> > loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis,
> >          len -= 16;
> >          int ret = ram_discard_range(ramid, start_addr, block_length);
> >          if (ret) {
> > +            error_setg(errp, "Failed to discard RAM range %s: %d", ramid,
> > ret);
> >
> 
> note: the ram_discard_range() and ram_block_discard_range() functions also
> calls error_report()  Maybe they can be converted too.., let's not do this
> in this already long series.

Agreed.

> 
> 
> 
> 
> >              return ret;
> >          }
> >      }
> > @@ -2629,11 +2633,7 @@ static int loadvm_process_command(QEMUFile *f,
> > Error **errp)
> >          return loadvm_postcopy_handle_run(mis, errp);
> >
> >      case MIG_CMD_POSTCOPY_RAM_DISCARD:
> > -        ret = loadvm_postcopy_ram_handle_discard(mis, len);
> > -        if (ret < 0) {
> > -            error_setg(errp, "Failed to load device state command: %d",
> > ret);
> > -        }
> > -        return ret;
> > +        return loadvm_postcopy_ram_handle_discard(mis, len, errp);
> >
> >      case MIG_CMD_POSTCOPY_RESUME:
> >          loadvm_postcopy_handle_resume(mis);
> >
> > --
> > 2.50.1
> >
> >

Regards,
Arun



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

* Re: [PATCH v9 07/27] migration: push Error **errp into qemu_loadvm_state()
  2025-08-06  5:17   ` Akihiko Odaki
@ 2025-08-07  6:07     ` Arun Menon
  0 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-07  6:07 UTC (permalink / raw)
  To: Akihiko Odaki
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger

Hi Akihiko,

Thanks for the review.

On Wed, Aug 06, 2025 at 02:17:02PM +0900, Akihiko Odaki wrote:
> On 2025/08/06 3:25, Arun Menon wrote:
> > This is an incremental step in converting vmstate loading
> > code to report error via Error objects instead of directly
> > printing it to console/monitor.
> > It is ensured that qemu_loadvm_state() must report an error
> > in errp, in case of failure.
> > 
> > When postcopy live migration runs, the device states are loaded by
> > both the qemu coroutine process_incoming_migration_co() and the
> > postcopy_ram_listen_thread(). Therefore, it is important that the
> > coroutine also reports the error in case of failure, with
> > error_report_err(). Otherwise, the source qemu will not display
> > any errors before going into the postcopy pause state.
> > 
> > Signed-off-by: Arun Menon <armenon@redhat.com>
> > ---
> >   migration/migration.c |  7 ++++---
> >   migration/savevm.c    | 31 +++++++++++++++++++------------
> >   migration/savevm.h    |  2 +-
> >   3 files changed, 24 insertions(+), 16 deletions(-)
> > 
> > diff --git a/migration/migration.c b/migration/migration.c
> > index 10c216d25dec01f206eacad2edd24d21f00e614c..bb7d5e1dee52692cbea1d2c8fdca541e6a75bedb 100644
> > --- a/migration/migration.c
> > +++ b/migration/migration.c
> > @@ -881,7 +881,7 @@ process_incoming_migration_co(void *opaque)
> >                         MIGRATION_STATUS_ACTIVE);
> >       mis->loadvm_co = qemu_coroutine_self();
> > -    ret = qemu_loadvm_state(mis->from_src_file);
> > +    ret = qemu_loadvm_state(mis->from_src_file, &local_err);
> >       mis->loadvm_co = NULL;
> >       trace_vmstate_downtime_checkpoint("dst-precopy-loadvm-completed");
> > @@ -908,7 +908,8 @@ process_incoming_migration_co(void *opaque)
> >       }
> >       if (ret < 0) {
> > -        error_setg(&local_err, "load of migration failed: %s", strerror(-ret));
> > +        error_prepend(&local_err, "load of migration failed: %s: ",
> > +                      strerror(-ret));
> >           goto fail;
> >       }
> > @@ -924,7 +925,7 @@ fail:
> >       migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
> >                         MIGRATION_STATUS_FAILED);
> >       migrate_set_error(s, local_err);
> > -    error_free(local_err);
> > +    error_report_err(local_err);
> 
> There is "error_report_err(s->error)" after this so this can result in
> duplicate error printing.

Yes, we can replace this with error_free(s->error).

> 
> >       migration_incoming_state_destroy();
> > diff --git a/migration/savevm.c b/migration/savevm.c
> > index 1bd27efe437d4d911728d776e995490d0a45dcf5..ca166ebd397ad80836ed2f9cb20a92f704fd4ed5 100644
> > --- a/migration/savevm.c
> > +++ b/migration/savevm.c
> > @@ -3149,28 +3149,24 @@ out:
> >       return ret;
> >   }
> > -int qemu_loadvm_state(QEMUFile *f)
> > +int qemu_loadvm_state(QEMUFile *f, Error **errp)
> >   {
> >       MigrationState *s = migrate_get_current();
> >       MigrationIncomingState *mis = migration_incoming_get_current();
> > -    Error *local_err = NULL;
> >       int ret;
> > -    if (qemu_savevm_state_blocked(&local_err)) {
> > -        error_report_err(local_err);
> > +    if (qemu_savevm_state_blocked(errp)) {
> >           return -EINVAL;
> >       }
> >       qemu_loadvm_thread_pool_create(mis);
> > -    ret = qemu_loadvm_state_header(f, &local_err);
> > +    ret = qemu_loadvm_state_header(f, errp);
> >       if (ret) {
> > -        error_report_err(local_err);
> >           return ret;
> >       }
> > -    if (qemu_loadvm_state_setup(f, &local_err) != 0) {
> > -        error_report_err(local_err);
> > +    if (qemu_loadvm_state_setup(f, errp) != 0) {
> >           return -EINVAL;
> >       }
> > @@ -3181,6 +3177,9 @@ int qemu_loadvm_state(QEMUFile *f)
> >       cpu_synchronize_all_pre_loadvm();
> >       ret = qemu_loadvm_state_main(f, mis);
> > +    if (ret < 0) {
> > +        error_setg(errp, "Load VM state failed: %d", ret);
> > +    }
> >       qemu_event_set(&mis->main_thread_load_event);
> >       trace_qemu_loadvm_state_post_main(ret);
> > @@ -3198,8 +3197,14 @@ int qemu_loadvm_state(QEMUFile *f)
> >           if (migrate_has_error(migrate_get_current()) ||
> >               !qemu_loadvm_thread_pool_wait(s, mis)) {
> >               ret = -EINVAL;
> > +            error_setg(errp,
> > +                       "Error while loading VM state: "
> > +                       "Migration stream has error");
> >           } else {
> >               ret = qemu_file_get_error(f);
> > +            if (ret < 0) {
> > +                error_setg(errp, "Error while loading vmstate : %d", ret);
> > +            }
> >           }
> >       }
> >       /*
> > @@ -3464,6 +3469,7 @@ void qmp_xen_save_devices_state(const char *filename, bool has_live, bool live,
> >   void qmp_xen_load_devices_state(const char *filename, Error **errp)
> >   {
> > +    ERRP_GUARD();
> >       QEMUFile *f;
> >       QIOChannelFile *ioc;
> >       int ret;
> > @@ -3485,10 +3491,10 @@ void qmp_xen_load_devices_state(const char *filename, Error **errp)
> >       f = qemu_file_new_input(QIO_CHANNEL(ioc));
> >       object_unref(OBJECT(ioc));
> > -    ret = qemu_loadvm_state(f);
> > +    ret = qemu_loadvm_state(f, errp);
> >       qemu_fclose(f);
> >       if (ret < 0) {
> > -        error_setg(errp, "loading Xen device state failed");
> > +        error_prepend(errp, "loading Xen device state failed: ");
> >       }
> >       migration_incoming_state_destroy();
> >   }
> > @@ -3496,6 +3502,7 @@ void qmp_xen_load_devices_state(const char *filename, Error **errp)
> >   bool load_snapshot(const char *name, const char *vmstate,
> >                      bool has_devices, strList *devices, Error **errp)
> >   {
> > +    ERRP_GUARD();
> >       BlockDriverState *bs_vm_state;
> >       QEMUSnapshotInfo sn;
> >       QEMUFile *f;
> > @@ -3559,13 +3566,13 @@ bool load_snapshot(const char *name, const char *vmstate,
> >           ret = -EINVAL;
> >           goto err_drain;
> >       }
> > -    ret = qemu_loadvm_state(f);
> > +    ret = qemu_loadvm_state(f, errp);
> >       migration_incoming_state_destroy();
> >       bdrv_drain_all_end();
> >       if (ret < 0) {
> > -        error_setg(errp, "Error %d while loading VM state", ret);
> > +        error_prepend(errp, "Error %d while loading VM state: ", ret);
> >           return false;
> >       }
> > diff --git a/migration/savevm.h b/migration/savevm.h
> > index 2d5e9c716686f06720325e82fe90c75335ced1de..b80770b7461a60e2ad6ba5e24a7baeae73d90955 100644
> > --- a/migration/savevm.h
> > +++ b/migration/savevm.h
> > @@ -64,7 +64,7 @@ void qemu_savevm_send_colo_enable(QEMUFile *f);
> >   void qemu_savevm_live_state(QEMUFile *f);
> >   int qemu_save_device_state(QEMUFile *f);
> > -int qemu_loadvm_state(QEMUFile *f);
> > +int qemu_loadvm_state(QEMUFile *f, Error **errp);
> >   void qemu_loadvm_state_cleanup(MigrationIncomingState *mis);
> >   int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis);
> >   int qemu_load_device_state(QEMUFile *f);
> > 
> 
Regards,
Arun Menon



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

* Re: [PATCH v9 24/27] migration: Propagate last encountered error in vmstate_save_state_v() function
  2025-08-06  5:24   ` Akihiko Odaki
@ 2025-08-07  6:07     ` Arun Menon
  0 siblings, 0 replies; 63+ messages in thread
From: Arun Menon @ 2025-08-07  6:07 UTC (permalink / raw)
  To: Akihiko Odaki
  Cc: qemu-devel, Peter Xu, Fabiano Rosas, Alex Bennée,
	Dmitry Osipenko, Michael S. Tsirkin, Marcel Apfelbaum,
	Cornelia Huck, Halil Pasic, Eric Farman, Thomas Huth,
	Christian Borntraeger, Matthew Rosato, Richard Henderson,
	David Hildenbrand, Ilya Leoshkevich, Nicholas Piggin,
	Harsh Prateek Bora, Paolo Bonzini, Fam Zheng, Alex Williamson,
	Cédric Le Goater, Steve Sistare, Marc-André Lureau,
	qemu-s390x, qemu-ppc, Hailiang Zhang, Stefan Berger

Hi Akihiko,

Thanks for the review.

On Wed, Aug 06, 2025 at 02:24:37PM +0900, Akihiko Odaki wrote:
> On 2025/08/06 3:25, Arun Menon wrote:
> > We consider,
> >    - the saving of the device state (save or subsection save) and
> >    - running the cleanup after (post_save)
> > as an atomic operation. And that is why, post_save() is called regardless
> > of whether there is a preceeding error. This means that, it is possible
> > that we have 2 distict errors, one from the preceeding function and the
> > other from the post_save() function.
> > 
> > This commit changes the error handling behavior when two errors occur during
> > a save operation.
> > 
> > 1) Preceding errors were propagated before, but they are now dismissed, if there
> >     is a new post_save() error.
> >      - A failure during the main save operation, means the system failed to
> >        reach a new desired state. A failure during the post-save cleanup,
> >        however, is a more critical issue as it can leave the system in an
> >        inconsistent or corrupted state. At present, all post_save() calls
> >        succeed. However, the introduction of error handling variants of these
> >        functions (post_save_errp) in the following commit, imply that we need
> >        to handle and propagate these errors. Therefore, we prioritize reporting
> >        the more severe error.
> 
> This explains why the post_save() error is propagated instead of propagating
> the preceding error. But the preceding errors still do not have to be
> dismissed if we report them with error_report_err() instead.

Yes, both can also be reported.

> 
> > 
> > 2) post_save() errors were dismissed before, but they are now propagated.
> >      - The original design implicitly assumed post-save hooks were infallible
> >        cleanup routines.
> >        This will not be the case after introducing the post/pre save/load errp
> >        variant functions. Dismissing these errors prevents users from
> >        identifying the specific operation that failed.
> 
> Re-iterating previous comments, if introducing post save errp variant
> functions break the assumption, we just don't have to introduce one. The
> reason to introduce one needs to be explained.

Sure, let's keep the original design and try the approach where we rename
post_save to cleanup_save and return void. This should make things clear.

That way, we can avoid introducing post_save_errp() and also we can discard
patches [PATCH v9 23/27] Refactor vmstate_save_state_v() function and this
one [PATCH v9 24/27] Propagate last encountered error in vmstate_save_state_v()
function.

I will post a new version with that patch.

> 
> >        The entire save-and-cleanup process is treated as a single
> >        logical operation, and all potential failures are communicated.
> > 
> > Signed-off-by: Arun Menon <armenon@redhat.com>
> > ---
> >   migration/vmstate.c | 14 ++++++++++++--
> >   1 file changed, 12 insertions(+), 2 deletions(-)
> > 
> > diff --git a/migration/vmstate.c b/migration/vmstate.c
> > index fbc59caadbbcc75fe6de27b459aa9aa25e76aa0a..ef78a1e62ad92e9608de72d125da80ea496c8dd1 100644
> > --- a/migration/vmstate.c
> > +++ b/migration/vmstate.c
> > @@ -554,6 +554,12 @@ static int vmstate_save_dispatch(QEMUFile *f,
> >                       error_setg(errp, "Save of field %s/%s failed",
> >                                   vmsd->name, field->name);
> >                       ps_ret = post_save_dispatch(vmsd, opaque, &local_err);
> > +                    if (ps_ret) {
> > +                        ret = ps_ret;
> > +                        error_free_or_abort(errp);
> > +                        error_setg(errp, "post-save for %s failed, ret: %d",
> > +                                   vmsd->name, ret);
> > +                    }
> >                       return ret;
> >                   }
> > @@ -603,10 +609,14 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
> >       }
> >       ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc, errp);
> > +
> >       ps_ret = post_save_dispatch(vmsd, opaque, &local_err);
> > -    if (!ret && ps_ret) {
> > +    if (ps_ret) {
> > +        if (ret) {
> > +            error_free_or_abort(errp);
> > +        }
> >           ret = ps_ret;
> > -        error_setg(errp, "post-save failed: %s", vmsd->name);
> > +        error_propagate(errp, local_err);
> >       }
> >       return ret;
> > 
> 
Regards,
Arun



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

* Re: [PATCH v9 12/27] migration: Update qemu_file_get_return_path() docs and remove dead checks
  2025-08-05 18:25 ` [PATCH v9 12/27] migration: Update qemu_file_get_return_path() docs and remove dead checks Arun Menon
@ 2025-08-08 13:07   ` Fabiano Rosas
  0 siblings, 0 replies; 63+ messages in thread
From: Fabiano Rosas @ 2025-08-08 13:07 UTC (permalink / raw)
  To: Arun Menon, qemu-devel
  Cc: Peter Xu, Alex Bennée, Akihiko Odaki, Dmitry Osipenko,
	Michael S. Tsirkin, Marcel Apfelbaum, Cornelia Huck, Halil Pasic,
	Eric Farman, Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare,
	Marc-André Lureau, qemu-s390x, qemu-ppc, Hailiang Zhang,
	Stefan Berger, Arun Menon, Daniel P. Berrangé

Arun Menon <armenon@redhat.com> writes:

> The documentation of qemu_file_get_return_path() states that it can
> return NULL on failure. However, a review of the current implementation
> reveals that it is guaranteed that it will always succeed and will never
> return NULL.
>
> As a result, the NULL checks post calling the function become redundant.
> This commit updates the documentation for the function and removes all
> NULL checks throughout the migration code.
>
> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Signed-off-by: Arun Menon <armenon@redhat.com>

Reviewed-by: Fabiano Rosas <farosas@suse.de>


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

* Re: [PATCH v9 13/27] migration: make loadvm_postcopy_handle_resume() void
  2025-08-05 18:25 ` [PATCH v9 13/27] migration: make loadvm_postcopy_handle_resume() void Arun Menon
@ 2025-08-08 13:08   ` Fabiano Rosas
  0 siblings, 0 replies; 63+ messages in thread
From: Fabiano Rosas @ 2025-08-08 13:08 UTC (permalink / raw)
  To: Arun Menon, qemu-devel
  Cc: Peter Xu, Alex Bennée, Akihiko Odaki, Dmitry Osipenko,
	Michael S. Tsirkin, Marcel Apfelbaum, Cornelia Huck, Halil Pasic,
	Eric Farman, Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare,
	Marc-André Lureau, qemu-s390x, qemu-ppc, Hailiang Zhang,
	Stefan Berger, Arun Menon, Daniel P. Berrangé

Arun Menon <armenon@redhat.com> writes:

> This is an incremental step in converting vmstate loading
> code to report error via Error objects instead of directly
> printing it to console/monitor.
>
> Use warn_report() instead of error_report(); it ensures that
> a resume command received while the migration is not
> in postcopy recover state is not fatal. It only informs that
> the command received is unusual, and therefore we should not set
> errp with the error string.
>
> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
> Signed-off-by: Arun Menon <armenon@redhat.com>

Reviewed-by: Fabiano Rosas <farosas@suse.de>


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

* Re: [PATCH v9 22/27] migration: Capture error in postcopy_ram_listen_thread()
  2025-08-05 18:25 ` [PATCH v9 22/27] migration: Capture error in postcopy_ram_listen_thread() Arun Menon
@ 2025-08-08 13:27   ` Fabiano Rosas
  0 siblings, 0 replies; 63+ messages in thread
From: Fabiano Rosas @ 2025-08-08 13:27 UTC (permalink / raw)
  To: Arun Menon, qemu-devel
  Cc: Peter Xu, Alex Bennée, Akihiko Odaki, Dmitry Osipenko,
	Michael S. Tsirkin, Marcel Apfelbaum, Cornelia Huck, Halil Pasic,
	Eric Farman, Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare,
	Marc-André Lureau, qemu-s390x, qemu-ppc, Hailiang Zhang,
	Stefan Berger, Arun Menon

Arun Menon <armenon@redhat.com> writes:

> This is an incremental step in converting vmstate loading
> code to report error via Error objects instead of directly
> printing it to console/monitor.
> postcopy_ram_listen_thread() calls qemu_loadvm_state_main()
> to load the vm, and in case of a failure, it should set the error
> in the migration object.
>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Signed-off-by: Arun Menon <armenon@redhat.com>
> ---
>  migration/savevm.c | 10 ++++++++--
>  1 file changed, 8 insertions(+), 2 deletions(-)
>
> diff --git a/migration/savevm.c b/migration/savevm.c
> index c89fa59161fd1c4f20d0c3f8f14e86f014223dd6..4df50fb5c674e56c0173c357c1e23367373e2114 100644
> --- a/migration/savevm.c
> +++ b/migration/savevm.c
> @@ -2095,6 +2095,7 @@ static void *postcopy_ram_listen_thread(void *opaque)
>      QEMUFile *f = mis->from_src_file;
>      int load_res;
>      MigrationState *migr = migrate_get_current();
> +    Error *local_err = NULL;
>  
>      object_ref(OBJECT(migr));
>  
> @@ -2111,7 +2112,7 @@ static void *postcopy_ram_listen_thread(void *opaque)
>      qemu_file_set_blocking(f, true);
>  
>      /* TODO: sanity check that only postcopiable data will be loaded here */
> -    load_res = qemu_loadvm_state_main(f, mis, NULL);
> +    load_res = qemu_loadvm_state_main(f, mis, &local_err);
>  
>      /*
>       * This is tricky, but, mis->from_src_file can change after it
> @@ -2137,7 +2138,12 @@ static void *postcopy_ram_listen_thread(void *opaque)
>                           __func__, load_res);
>              load_res = 0; /* prevent further exit() */
>          } else {
> -            error_report("%s: loadvm failed: %d", __func__, load_res);
> +            if (local_err != NULL) {

This should always be true. I presume the conditional goes away at the
end of the series. Could have been mentioned in the commit message.

> +                error_prepend(&local_err,
> +                              "loadvm failed during postcopy: %d: ", load_res);
> +                migrate_set_error(migr, local_err);
> +                error_report_err(local_err);
> +            }
>              migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
>                                             MIGRATION_STATUS_FAILED);
>          }


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

* Re: [PATCH v9 02/27] migration: push Error **errp into vmstate_load_state()
  2025-08-05 18:25 ` [PATCH v9 02/27] migration: push Error **errp into vmstate_load_state() Arun Menon
  2025-08-06  8:31   ` Marc-André Lureau
@ 2025-08-10  4:47   ` Akihiko Odaki
  1 sibling, 0 replies; 63+ messages in thread
From: Akihiko Odaki @ 2025-08-10  4:47 UTC (permalink / raw)
  To: Arun Menon, qemu-devel
  Cc: Peter Xu, Fabiano Rosas, Alex Bennée, Dmitry Osipenko,
	Michael S. Tsirkin, Marcel Apfelbaum, Cornelia Huck, Halil Pasic,
	Eric Farman, Thomas Huth, Christian Borntraeger, Matthew Rosato,
	Richard Henderson, David Hildenbrand, Ilya Leoshkevich,
	Nicholas Piggin, Harsh Prateek Bora, Paolo Bonzini, Fam Zheng,
	Alex Williamson, Cédric Le Goater, Steve Sistare,
	Marc-André Lureau, qemu-s390x, qemu-ppc, Hailiang Zhang,
	Stefan Berger

On 2025/08/06 3:25, Arun Menon wrote:
> This is an incremental step in converting vmstate loading
> code to report error via Error objects instead of directly
> printing it to console/monitor.
> It is ensured that vmstate_load_state() must report an error
> in errp, in case of failure.
> 
> In cases where we do not want to essentially set the error
> in errp object and also not abort/exit, the caller only
> passes &error_warn, to warn on error.
> 
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Signed-off-by: Arun Menon <armenon@redhat.com>
> ---
>   hw/display/virtio-gpu.c     |  2 +-
>   hw/pci/pci.c                |  3 ++-
>   hw/s390x/virtio-ccw.c       |  2 +-
>   hw/scsi/spapr_vscsi.c       |  2 +-
>   hw/vfio/pci.c               |  2 +-
>   hw/virtio/virtio-mmio.c     |  3 ++-
>   hw/virtio/virtio-pci.c      |  2 +-
>   hw/virtio/virtio.c          |  4 ++--
>   include/migration/vmstate.h |  2 +-
>   migration/cpr.c             |  5 ++--
>   migration/savevm.c          |  6 +++--
>   migration/vmstate-types.c   | 11 +++++----
>   migration/vmstate.c         | 56 ++++++++++++++++++++++++++++++---------------
>   tests/unit/test-vmstate.c   | 20 ++++++++--------
>   ui/vdagent.c                |  2 +-
>   15 files changed, 75 insertions(+), 47 deletions(-)
> 
> diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
> index 0a1a625b0ea6cf26cb0d799171a57ed3d3ab2442..5ff9f5e6dc0b6b112e36170318c900f893a73c5e 100644
> --- a/hw/display/virtio-gpu.c
> +++ b/hw/display/virtio-gpu.c
> @@ -1343,7 +1343,7 @@ 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);
> +    vmstate_load_state(f, &vmstate_virtio_gpu_scanouts, g, 1, &error_warn);

I realized error_warn should be avoided. ERRP_GUARD() is incompatible 
with &error_warn due to the bug that the following patch fixes:
https://lore.kernel.org/qemu-devel/20250803-errp-v1-1-a59a73f63289@rsg.ci.i.u-tokyo.ac.jp/
("[PATCH] error: Rewrite &error_warn with ERRP_GUARD()")

The fix is not going to be pulled because error_warn is going to be 
removed with the following series:
https://lore.kernel.org/qemu-devel/20250808080823.2638861-1-armbru@redhat.com/
("[PATCH 00/12] Error reporting cleanup, a fix, and &error_warn removal")

>   
>       return 0;
>   }
> diff --git a/hw/pci/pci.c b/hw/pci/pci.c
> index c70b5ceebaf1f2b10768bd030526cbb518da2b8d..80545189980f176ca6a3dc9abce7043c8bc2708c 100644
> --- a/hw/pci/pci.c
> +++ b/hw/pci/pci.c
> @@ -934,7 +934,8 @@ void pci_device_save(PCIDevice *s, QEMUFile *f)
>   int pci_device_load(PCIDevice *s, QEMUFile *f)
>   {
>       int ret;
> -    ret = vmstate_load_state(f, &vmstate_pci_device, s, s->version_id);
> +    ret = vmstate_load_state(f, &vmstate_pci_device, s, s->version_id,
> +                             &error_warn);
>       /* 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 d2f85b39f30f7fc82e0c600144c0a958e1269b2c..bafcb9b76d81749925e31b5a0a1320b3332ad2cf 100644
> --- a/hw/s390x/virtio-ccw.c
> +++ b/hw/s390x/virtio-ccw.c
> @@ -1136,7 +1136,7 @@ static void virtio_ccw_save_config(DeviceState *d, QEMUFile *f)
>   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);
> +    return vmstate_load_state(f, &vmstate_virtio_ccw_dev, dev, 1, &error_warn);
>   }
>   
>   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 20f70fb2729de78b9636a6b8c869695dab4f8902..a4812afd0cc7c495080ef03a531c7d279af29b33 100644
> --- a/hw/scsi/spapr_vscsi.c
> +++ b/hw/scsi/spapr_vscsi.c
> @@ -648,7 +648,7 @@ static void *vscsi_load_request(QEMUFile *f, SCSIRequest *sreq)
>       assert(!req->active);
>   
>       memset(req, 0, sizeof(*req));
> -    rc = vmstate_load_state(f, &vmstate_spapr_vscsi_req, req, 1);
> +    rc = vmstate_load_state(f, &vmstate_spapr_vscsi_req, req, 1, &error_warn);
>       if (rc) {
>           fprintf(stderr, "VSCSI: failed loading request tag#%u\n", sreq->tag);
>           return NULL;
> diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
> index 4fa692c1a32bcfa4e4939e5fcb64f2bf19905b3b..04d385d7674f444844beeee7364ee0424f762758 100644
> --- a/hw/vfio/pci.c
> +++ b/hw/vfio/pci.c
> @@ -2795,7 +2795,7 @@ static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f)
>           old_addr[bar] = pdev->io_regions[bar].addr;
>       }
>   
> -    ret = vmstate_load_state(f, &vmstate_vfio_pci_config, vdev, 1);
> +    ret = vmstate_load_state(f, &vmstate_vfio_pci_config, vdev, 1, &error_warn);
>       if (ret) {
>           return ret;
>       }
> diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c
> index 532c67107ba1d2978a76cf49f9cdc1de1dea3e11..f14f0487fec3ed30d2cc3aec96950c2121265716 100644
> --- a/hw/virtio/virtio-mmio.c
> +++ b/hw/virtio/virtio-mmio.c
> @@ -34,6 +34,7 @@
>   #include "qemu/error-report.h"
>   #include "qemu/log.h"
>   #include "trace.h"
> +#include "qapi/error.h"
>   
>   static bool virtio_mmio_ioeventfd_enabled(DeviceState *d)
>   {
> @@ -619,7 +620,7 @@ static int virtio_mmio_load_extra_state(DeviceState *opaque, QEMUFile *f)
>   {
>       VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
>   
> -    return vmstate_load_state(f, &vmstate_virtio_mmio, proxy, 1);
> +    return vmstate_load_state(f, &vmstate_virtio_mmio, proxy, 1, &error_warn);
>   }
>   
>   static bool virtio_mmio_has_extra_state(DeviceState *opaque)
> diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
> index 767216d795998708f5716a23ae16c79cd90ff489..07f144d6d1bfbb561b28015de71762601080dc23 100644
> --- a/hw/virtio/virtio-pci.c
> +++ b/hw/virtio/virtio-pci.c
> @@ -161,7 +161,7 @@ static int virtio_pci_load_extra_state(DeviceState *d, QEMUFile *f)
>   {
>       VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
>   
> -    return vmstate_load_state(f, &vmstate_virtio_pci, proxy, 1);
> +    return vmstate_load_state(f, &vmstate_virtio_pci, proxy, 1, &error_warn);
>   }
>   
>   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 2ab1d20769495ea39445b87e3673b076ad172510..aac317a93c3cd55ece9b0fd858c7b2459f8242f9 100644
> --- a/hw/virtio/virtio.c
> +++ b/hw/virtio/virtio.c
> @@ -3317,14 +3317,14 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
>       }
>   
>       if (vdc->vmsd) {
> -        ret = vmstate_load_state(f, vdc->vmsd, vdev, version_id);
> +        ret = vmstate_load_state(f, vdc->vmsd, vdev, version_id, &error_warn);
>           if (ret) {
>               return ret;
>           }
>       }
>   
>       /* Subsections */
> -    ret = vmstate_load_state(f, &vmstate_virtio, vdev, 1);
> +    ret = vmstate_load_state(f, &vmstate_virtio, vdev, 1, &error_warn);
>       if (ret) {
>           return ret;
>       }
> diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
> index 1ff7bd9ac425ba67cd5ca7ad97bcf570f9e19abe..056781b1c21e737583f081594d9f88b32adfd674 100644
> --- a/include/migration/vmstate.h
> +++ b/include/migration/vmstate.h
> @@ -1196,7 +1196,7 @@ extern const VMStateInfo vmstate_info_qlist;
>       }
>   
>   int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
> -                       void *opaque, int version_id);
> +                       void *opaque, int version_id, Error **errp);
>   int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
>                          void *opaque, JSONWriter *vmdesc);
>   int vmstate_save_state_with_err(QEMUFile *f, const VMStateDescription *vmsd,
> diff --git a/migration/cpr.c b/migration/cpr.c
> index 42ad0b0d500e5de57faf0c6517e216b2d1c0cacf..bdb24736f44e91ba59b6e622a315597c97e7f64d 100644
> --- a/migration/cpr.c
> +++ b/migration/cpr.c
> @@ -202,6 +202,7 @@ int cpr_state_save(MigrationChannel *channel, Error **errp)
>   
>   int cpr_state_load(MigrationChannel *channel, Error **errp)
>   {
> +    ERRP_GUARD();
>       int ret;
>       uint32_t v;
>       QEMUFile *f;
> @@ -233,9 +234,9 @@ int cpr_state_load(MigrationChannel *channel, Error **errp)
>           return -ENOTSUP;
>       }
>   
> -    ret = vmstate_load_state(f, &vmstate_cpr_state, &cpr_state, 1);
> +    ret = vmstate_load_state(f, &vmstate_cpr_state, &cpr_state, 1, errp);
>       if (ret) {
> -        error_setg(errp, "vmstate_load_state error %d", ret);
> +        error_prepend(errp, "vmstate_load_state error %d: ", ret);
>           qemu_fclose(f);
>           return ret;
>       }
> diff --git a/migration/savevm.c b/migration/savevm.c
> index fabbeb296ae987d0c06ba6dafda63720205fecfd..ab947620f724874f325fb9fb59bef50b7c16fb51 100644
> --- a/migration/savevm.c
> +++ b/migration/savevm.c
> @@ -969,7 +969,8 @@ static int vmstate_load(QEMUFile *f, SaveStateEntry *se)
>       if (!se->vmsd) {         /* Old style */
>           return se->ops->load_state(f, se->opaque, se->load_version_id);
>       }
> -    return vmstate_load_state(f, se->vmsd, se->opaque, se->load_version_id);
> +    return vmstate_load_state(f, se->vmsd, se->opaque, se->load_version_id,
> +                              NULL);
>   }
>   
>   static void vmstate_save_old_style(QEMUFile *f, SaveStateEntry *se,
> @@ -2839,7 +2840,8 @@ static int qemu_loadvm_state_header(QEMUFile *f)
>               error_report("Configuration section missing");
>               return -EINVAL;
>           }
> -        ret = vmstate_load_state(f, &vmstate_configuration, &savevm_state, 0);
> +        ret = vmstate_load_state(f, &vmstate_configuration, &savevm_state, 0,
> +                                 NULL);
>   
>           if (ret) {
>               return ret;
> diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c
> index 741a588b7e18c6d37724b08a0101edc8bc74a0a5..e2020a733d0921d748b6f832a193e5de8d302d5f 100644
> --- a/migration/vmstate-types.c
> +++ b/migration/vmstate-types.c
> @@ -19,6 +19,7 @@
>   #include "qemu/error-report.h"
>   #include "qemu/queue.h"
>   #include "trace.h"
> +#include "qapi/error.h"
>   
>   /* bool */
>   
> @@ -549,7 +550,7 @@ static int get_tmp(QEMUFile *f, void *pv, size_t size,
>   
>       /* Writes the parent field which is at the start of the tmp */
>       *(void **)tmp = pv;
> -    ret = vmstate_load_state(f, vmsd, tmp, version_id);
> +    ret = vmstate_load_state(f, vmsd, tmp, version_id, &error_warn);
>       g_free(tmp);
>       return ret;
>   }
> @@ -649,7 +650,7 @@ static int get_qtailq(QEMUFile *f, void *pv, size_t unused_size,
>   
>       while (qemu_get_byte(f)) {
>           elm = g_malloc(size);
> -        ret = vmstate_load_state(f, vmsd, elm, version_id);
> +        ret = vmstate_load_state(f, vmsd, elm, version_id, &error_warn);
>           if (ret) {
>               return ret;
>           }
> @@ -803,7 +804,7 @@ static int get_gtree(QEMUFile *f, void *pv, size_t unused_size,
>               key = (void *)(uintptr_t)qemu_get_be64(f);
>           } else {
>               key = g_malloc0(key_size);
> -            ret = vmstate_load_state(f, key_vmsd, key, version_id);
> +            ret = vmstate_load_state(f, key_vmsd, key, version_id, &error_warn);
>               if (ret) {
>                   error_report("%s : failed to load %s (%d)",
>                                field->name, key_vmsd->name, ret);
> @@ -811,7 +812,7 @@ static int get_gtree(QEMUFile *f, void *pv, size_t unused_size,
>               }
>           }
>           val = g_malloc0(val_size);
> -        ret = vmstate_load_state(f, val_vmsd, val, version_id);
> +        ret = vmstate_load_state(f, val_vmsd, val, version_id, &error_warn);
>           if (ret) {
>               error_report("%s : failed to load %s (%d)",
>                            field->name, val_vmsd->name, ret);
> @@ -892,7 +893,7 @@ static int get_qlist(QEMUFile *f, void *pv, size_t unused_size,
>   
>       while (qemu_get_byte(f)) {
>           elm = g_malloc(size);
> -        ret = vmstate_load_state(f, vmsd, elm, version_id);
> +        ret = vmstate_load_state(f, vmsd, elm, version_id, &error_warn);
>           if (ret) {
>               error_report("%s: failed to load %s (%d)", field->name,
>                            vmsd->name, ret);
> diff --git a/migration/vmstate.c b/migration/vmstate.c
> index 24451b054c11dfca2d76e24b053d604bb7184e1c..60ff38858cf54277992fa5eddeadb6f3d70edec3 100644
> --- a/migration/vmstate.c
> +++ b/migration/vmstate.c
> @@ -132,29 +132,33 @@ static void vmstate_handle_alloc(void *ptr, const VMStateField *field,
>   }
>   
>   int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
> -                       void *opaque, int version_id)
> +                       void *opaque, int version_id, Error **errp)
>   {
>       const VMStateField *field = vmsd->fields;
>       int ret = 0;
>   
>       trace_vmstate_load_state(vmsd->name, version_id);
>       if (version_id > vmsd->version_id) {
> -        error_report("%s: incoming version_id %d is too new "
> -                     "for local version_id %d",
> -                     vmsd->name, version_id, vmsd->version_id);
> +        error_setg(errp, "%s: incoming version_id %d is too new "
> +                   "for local version_id %d",
> +                   vmsd->name, version_id, vmsd->version_id);
>           trace_vmstate_load_state_end(vmsd->name, "too new", -EINVAL);
>           return -EINVAL;
>       }
>       if  (version_id < vmsd->minimum_version_id) {
> -        error_report("%s: incoming version_id %d is too old "
> -                     "for local minimum version_id  %d",
> -                     vmsd->name, version_id, vmsd->minimum_version_id);
> +        error_setg(errp, "%s: incoming version_id %d is too old "
> +                   "for local minimum version_id %d",
> +                   vmsd->name, version_id, vmsd->minimum_version_id);
>           trace_vmstate_load_state_end(vmsd->name, "too old", -EINVAL);
>           return -EINVAL;
>       }
>       if (vmsd->pre_load) {
>           ret = vmsd->pre_load(opaque);
>           if (ret) {
> +            error_setg(errp, "VM pre load failed for: '%s', "
> +                       "version_id: %d, minimum version_id: %d, ret: %d",
> +                       vmsd->name, vmsd->version_id, vmsd->minimum_version_id,
> +                       ret);
>               return ret;
>           }
>       }
> @@ -192,10 +196,12 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
>   
>                   if (inner_field->flags & VMS_STRUCT) {
>                       ret = vmstate_load_state(f, inner_field->vmsd, curr_elem,
> -                                             inner_field->vmsd->version_id);
> +                                             inner_field->vmsd->version_id,
> +                                             errp);
>                   } else if (inner_field->flags & VMS_VSTRUCT) {
>                       ret = vmstate_load_state(f, inner_field->vmsd, curr_elem,
> -                                             inner_field->struct_version_id);
> +                                             inner_field->struct_version_id,
> +                                             errp);
>                   } else {
>                       ret = inner_field->info->get(f, curr_elem, size,
>                                                    inner_field);
> @@ -208,30 +214,43 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
>   
>                   if (ret >= 0) {
>                       ret = qemu_file_get_error(f);
> +                    if (ret < 0) {
> +                        error_setg(errp, "Failed to load %s state: %d",
> +                                   vmsd->name, ret);
> +                        return ret;
> +                    }
>                   }
>                   if (ret < 0) {
>                       qemu_file_set_error(f, ret);
> -                    error_report("Failed to load %s:%s", vmsd->name,
> -                                 field->name);
> +                    error_setg(errp,
> +                               "Failed to load %s:%s version_id: %d: ",
> +                               vmsd->name, field->name, vmsd->version_id);
>                       trace_vmstate_load_field_error(field->name, ret);
>                       return ret;
>                   }
>               }
>           } else if (field->flags & VMS_MUST_EXIST) {
> -            error_report("Input validation failed: %s/%s",
> -                         vmsd->name, field->name);
> +            error_setg(errp, "Input validation failed: %s/%s version_id: %d",
> +                       vmsd->name, field->name, vmsd->version_id);
>               return -1;
>           }
>           field++;
>       }
>       assert(field->flags == VMS_END);
> -    ret = vmstate_subsection_load(f, vmsd, opaque, NULL);
> +    ret = vmstate_subsection_load(f, vmsd, opaque, errp);
>       if (ret != 0) {
>           qemu_file_set_error(f, ret);
>           return ret;
>       }
>       if (vmsd->post_load) {
>           ret = vmsd->post_load(opaque, version_id);
> +        if (ret < 0) {
> +            error_setg(errp,
> +                       "VM Post load failed for: %s, version_id: %d, "
> +                       "minimum_version: %d, ret: %d",
> +                       vmsd->name, vmsd->version_id, vmsd->minimum_version_id,
> +                       ret);
> +        }
>       }
>       trace_vmstate_load_state_end(vmsd->name, "end", ret);
>       return ret;
> @@ -568,6 +587,7 @@ vmstate_get_subsection(const VMStateDescription * const *sub,
>   static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
>                                      void *opaque, Error **errp)
>   {
> +    ERRP_GUARD();
>       trace_vmstate_subsection_load(vmsd->name);
>   
>       while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) {
> @@ -607,12 +627,12 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
>           qemu_file_skip(f, len); /* idstr */
>           version_id = qemu_get_be32(f);
>   
> -        ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
> +        ret = vmstate_load_state(f, sub_vmsd, opaque, version_id, errp);
>           if (ret) {
>               trace_vmstate_subsection_load_bad(vmsd->name, idstr, "(child)");
> -            error_setg(errp,
> -                       "Loading VM subsection '%s' in '%s' failed: %d",
> -                       idstr, vmsd->name, ret);
> +            error_prepend(errp,
> +                          "Loading VM subsection '%s' in '%s' failed: %d: ",
> +                          idstr, vmsd->name, ret);
>               return ret;
>           }
>       }
> diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c
> index 63f28f26f45691a70936d33e7341d16477a3471f..807deb3531f3079864ac99567d4dece7122571dd 100644
> --- a/tests/unit/test-vmstate.c
> +++ b/tests/unit/test-vmstate.c
> @@ -30,6 +30,7 @@
>   #include "../migration/savevm.h"
>   #include "qemu/module.h"
>   #include "io/channel-file.h"
> +#include "qapi/error.h"
>   
>   static int temp_fd;
>   
> @@ -114,7 +115,7 @@ static int load_vmstate_one(const VMStateDescription *desc, void *obj,
>       qemu_fclose(f);
>   
>       f = open_test_file(false);
> -    ret = vmstate_load_state(f, desc, obj, version);
> +    ret = vmstate_load_state(f, desc, obj, version, &error_warn);
>       if (ret) {
>           g_assert(qemu_file_get_error(f));
>       } else{
> @@ -365,7 +366,7 @@ static void test_load_v1(void)
>   
>       QEMUFile *loading = open_test_file(false);
>       TestStruct obj = { .b = 200, .e = 500, .f = 600 };
> -    vmstate_load_state(loading, &vmstate_versioned, &obj, 1);
> +    vmstate_load_state(loading, &vmstate_versioned, &obj, 1, &error_warn);
>       g_assert(!qemu_file_get_error(loading));
>       g_assert_cmpint(obj.a, ==, 10);
>       g_assert_cmpint(obj.b, ==, 200);
> @@ -391,7 +392,7 @@ static void test_load_v2(void)
>   
>       QEMUFile *loading = open_test_file(false);
>       TestStruct obj;
> -    vmstate_load_state(loading, &vmstate_versioned, &obj, 2);
> +    vmstate_load_state(loading, &vmstate_versioned, &obj, 2, &error_warn);
>       g_assert_cmpint(obj.a, ==, 10);
>       g_assert_cmpint(obj.b, ==, 20);
>       g_assert_cmpint(obj.c, ==, 30);
> @@ -480,7 +481,7 @@ static void test_load_noskip(void)
>   
>       QEMUFile *loading = open_test_file(false);
>       TestStruct obj = { .skip_c_e = false };
> -    vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
> +    vmstate_load_state(loading, &vmstate_skipping, &obj, 2, &error_warn);
>       g_assert(!qemu_file_get_error(loading));
>       g_assert_cmpint(obj.a, ==, 10);
>       g_assert_cmpint(obj.b, ==, 20);
> @@ -504,7 +505,7 @@ static void test_load_skip(void)
>   
>       QEMUFile *loading = open_test_file(false);
>       TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 };
> -    vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
> +    vmstate_load_state(loading, &vmstate_skipping, &obj, 2, &error_warn);
>       g_assert(!qemu_file_get_error(loading));
>       g_assert_cmpint(obj.a, ==, 10);
>       g_assert_cmpint(obj.b, ==, 20);
> @@ -773,7 +774,7 @@ static void test_load_q(void)
>       TestQtailq tgt;
>   
>       QTAILQ_INIT(&tgt.q);
> -    vmstate_load_state(fload, &vmstate_q, &tgt, 1);
> +    vmstate_load_state(fload, &vmstate_q, &tgt, 1, &error_warn);
>       char eof = qemu_get_byte(fload);
>       g_assert(!qemu_file_get_error(fload));
>       g_assert_cmpint(tgt.i16, ==, obj_q.i16);
> @@ -1127,7 +1128,7 @@ static void test_gtree_load_domain(void)
>   
>       fload = open_test_file(false);
>   
> -    vmstate_load_state(fload, &vmstate_domain, dest_domain, 1);
> +    vmstate_load_state(fload, &vmstate_domain, dest_domain, 1, &error_warn);
>       eof = qemu_get_byte(fload);
>       g_assert(!qemu_file_get_error(fload));
>       g_assert_cmpint(orig_domain->id, ==, dest_domain->id);
> @@ -1241,7 +1242,7 @@ static void test_gtree_load_iommu(void)
>       qemu_fclose(fsave);
>   
>       fload = open_test_file(false);
> -    vmstate_load_state(fload, &vmstate_iommu, dest_iommu, 1);
> +    vmstate_load_state(fload, &vmstate_iommu, dest_iommu, 1, &error_warn);
>       eof = qemu_get_byte(fload);
>       g_assert(!qemu_file_get_error(fload));
>       g_assert_cmpint(orig_iommu->id, ==, dest_iommu->id);
> @@ -1376,7 +1377,8 @@ static void test_load_qlist(void)
>       qemu_fclose(fsave);
>   
>       fload = open_test_file(false);
> -    vmstate_load_state(fload, &vmstate_container, dest_container, 1);
> +    vmstate_load_state(fload, &vmstate_container, dest_container, 1,
> +                       &error_warn);
>       eof = qemu_get_byte(fload);
>       g_assert(!qemu_file_get_error(fload));
>       g_assert_cmpint(eof, ==, QEMU_VM_EOF);
> diff --git a/ui/vdagent.c b/ui/vdagent.c
> index c0746fe5b168fdc7aeb4866de2ba0c3387566649..b9a22a689d9acfeafb862ae73308db6fbd52331e 100644
> --- a/ui/vdagent.c
> +++ b/ui/vdagent.c
> @@ -1008,7 +1008,7 @@ static int get_cbinfo(QEMUFile *f, void *pv, size_t size,
>   
>       vdagent_clipboard_peer_register(vd);
>   
> -    ret = vmstate_load_state(f, &vmstate_cbinfo_array, &cbinfo, 0);
> +    ret = vmstate_load_state(f, &vmstate_cbinfo_array, &cbinfo, 0, &error_warn);
>       if (ret) {
>           return ret;
>       }
> 



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

end of thread, other threads:[~2025-08-10  4:49 UTC | newest]

Thread overview: 63+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-05 18:25 [PATCH v9 00/27] migration: propagate vTPM errors using Error objects Arun Menon
2025-08-05 18:25 ` [PATCH v9 01/27] migration: push Error **errp into vmstate_subsection_load() Arun Menon
2025-08-05 18:25 ` [PATCH v9 02/27] migration: push Error **errp into vmstate_load_state() Arun Menon
2025-08-06  8:31   ` Marc-André Lureau
2025-08-06  9:47     ` Arun Menon
2025-08-10  4:47   ` Akihiko Odaki
2025-08-05 18:25 ` [PATCH v9 03/27] migration: push Error **errp into qemu_loadvm_state_header() Arun Menon
2025-08-05 18:25 ` [PATCH v9 04/27] migration: push Error **errp into vmstate_load() Arun Menon
2025-08-05 18:25 ` [PATCH v9 05/27] migration: push Error **errp into loadvm_process_command() Arun Menon
2025-08-06  8:31   ` Marc-André Lureau
2025-08-06  9:46     ` Arun Menon
2025-08-05 18:25 ` [PATCH v9 06/27] migration: push Error **errp into loadvm_handle_cmd_packaged() Arun Menon
2025-08-05 18:25 ` [PATCH v9 07/27] migration: push Error **errp into qemu_loadvm_state() Arun Menon
2025-08-06  5:17   ` Akihiko Odaki
2025-08-07  6:07     ` Arun Menon
2025-08-06  7:24   ` Marc-André Lureau
2025-08-05 18:25 ` [PATCH v9 08/27] migration: push Error **errp into qemu_load_device_state() Arun Menon
2025-08-06  7:27   ` Marc-André Lureau
2025-08-05 18:25 ` [PATCH v9 09/27] migration: push Error **errp into qemu_loadvm_state_main() Arun Menon
2025-08-06  5:14   ` Akihiko Odaki
2025-08-06  7:34   ` Marc-André Lureau
2025-08-06  7:43     ` Arun Menon
2025-08-05 18:25 ` [PATCH v9 10/27] migration: push Error **errp into qemu_loadvm_section_start_full() Arun Menon
2025-08-06  7:37   ` Marc-André Lureau
2025-08-05 18:25 ` [PATCH v9 11/27] migration: push Error **errp into qemu_loadvm_section_part_end() Arun Menon
2025-08-06  7:46   ` Marc-André Lureau
2025-08-05 18:25 ` [PATCH v9 12/27] migration: Update qemu_file_get_return_path() docs and remove dead checks Arun Menon
2025-08-08 13:07   ` Fabiano Rosas
2025-08-05 18:25 ` [PATCH v9 13/27] migration: make loadvm_postcopy_handle_resume() void Arun Menon
2025-08-08 13:08   ` Fabiano Rosas
2025-08-05 18:25 ` [PATCH v9 14/27] migration: push Error **errp into ram_postcopy_incoming_init() Arun Menon
2025-08-05 18:25 ` [PATCH v9 15/27] migration: push Error **errp into loadvm_postcopy_handle_advise() Arun Menon
2025-08-05 18:25 ` [PATCH v9 16/27] migration: push Error **errp into loadvm_postcopy_handle_listen() Arun Menon
2025-08-05 18:25 ` [PATCH v9 17/27] migration: push Error **errp into loadvm_postcopy_handle_run() Arun Menon
2025-08-05 18:25 ` [PATCH v9 18/27] migration: push Error **errp into loadvm_postcopy_ram_handle_discard() Arun Menon
2025-08-06  7:54   ` Marc-André Lureau
2025-08-07  6:06     ` Arun Menon
2025-08-05 18:25 ` [PATCH v9 19/27] migration: push Error **errp into loadvm_handle_recv_bitmap() Arun Menon
2025-08-05 18:25 ` [PATCH v9 20/27] migration: push Error **errp into loadvm_process_enable_colo() Arun Menon
2025-08-06  8:07   ` Marc-André Lureau
2025-08-06  9:56     ` Arun Menon
2025-08-06 10:04       ` Marc-André Lureau
2025-08-06 10:19         ` Arun Menon
2025-08-05 18:25 ` [PATCH v9 21/27] migration: push Error **errp into loadvm_postcopy_handle_switchover_start() Arun Menon
2025-08-05 18:25 ` [PATCH v9 22/27] migration: Capture error in postcopy_ram_listen_thread() Arun Menon
2025-08-08 13:27   ` Fabiano Rosas
2025-08-05 18:25 ` [PATCH v9 23/27] migration: Refactor vmstate_save_state_v() function Arun Menon
2025-08-06  5:10   ` Akihiko Odaki
2025-08-06  8:19   ` Marc-André Lureau
2025-08-06  9:45     ` Arun Menon
2025-08-05 18:25 ` [PATCH v9 24/27] migration: Propagate last encountered error in " Arun Menon
2025-08-06  5:24   ` Akihiko Odaki
2025-08-07  6:07     ` Arun Menon
2025-08-05 18:25 ` [PATCH v9 25/27] migration: Remove error variant of vmstate_save_state() function Arun Menon
2025-08-06  8:28   ` Marc-André Lureau
2025-08-06  9:46     ` Arun Menon
2025-08-06 10:01       ` Marc-André Lureau
2025-08-06 10:30         ` Arun Menon
2025-08-06 11:16     ` Arun Menon
2025-08-05 18:25 ` [PATCH v9 26/27] migration: Add error-parameterized function variants in VMSD struct Arun Menon
2025-08-06  5:45   ` Akihiko Odaki
2025-08-06  7:27     ` Arun Menon
2025-08-05 18:25 ` [PATCH v9 27/27] backends/tpm: Propagate vTPM error on migration failure Arun Menon

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).