qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH for-11.0 0/6] migration: Error reporting cleanups
@ 2025-11-25 20:46 Peter Xu
  2025-11-25 20:46 ` [PATCH for-11.0 1/6] migration: Use explicit error_free() instead of g_autoptr Peter Xu
                   ` (5 more replies)
  0 siblings, 6 replies; 24+ messages in thread
From: Peter Xu @ 2025-11-25 20:46 UTC (permalink / raw)
  To: qemu-devel
  Cc: Markus Armbruster, Juraj Marcin, peterx, Marc-André Lureau,
	Fabiano Rosas, Daniel P . Berrangé,
	Vladimir Sementsov-Ogievskiy

[not -rc material; target QEMU 11.0 only]

Based-on: <20251125070554.2256181-1-armbru@redhat.com>

This series is based on Markus's recent fix:

[PATCH] migration: Fix double-free on error path
https://lore.kernel.org/r/20251125070554.2256181-1-armbru@redhat.com

This series should address the issues discussed in this thread here:

https://lore.kernel.org/r/871plmk1bc.fsf@pond.sub.org

The problem is Error is not a good candidate of g_autoptr, however the
cleanup function was accidentally merged.  Luckily, we only have two users
so far (after Markus's patch above).  This series removes the last two in
migration code and reverts the auto cleanup function for Error.

When at it, it'll also change migrate_set_error() to start taking ownership
of errors, just like what most error APIs do.  When at it, it is renamed to
migrate_error_propagate() to imply migration version of error_propagate().

Comments welcomed, thanks.

Peter Xu (6):
  migration: Use explicit error_free() instead of g_autoptr
  Revert "error: define g_autoptr() cleanup function for the Error type"
  migration: Make migration_connect_set_error() own the error
  migration: Make multifd_send_set_error() own the error
  migration: Make multifd_recv_terminate_threads() own the error
  migration: Replace migrate_set_error() with migrate_error_propagate()

 include/qapi/error.h             |  2 --
 migration/migration.h            |  2 +-
 migration/channel.c              |  1 -
 migration/cpr-exec.c             |  4 +--
 migration/migration.c            | 51 ++++++++++++++++----------------
 migration/multifd-device-state.c |  6 ++--
 migration/multifd.c              | 24 +++++++--------
 migration/postcopy-ram.c         |  5 ++--
 migration/ram.c                  |  4 +--
 migration/savevm.c               | 16 ++++------
 10 files changed, 49 insertions(+), 66 deletions(-)

-- 
2.50.1



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

* [PATCH for-11.0 1/6] migration: Use explicit error_free() instead of g_autoptr
  2025-11-25 20:46 [PATCH for-11.0 0/6] migration: Error reporting cleanups Peter Xu
@ 2025-11-25 20:46 ` Peter Xu
  2025-11-26  6:43   ` Markus Armbruster
  2025-11-25 20:46 ` [PATCH for-11.0 2/6] Revert "error: define g_autoptr() cleanup function for the Error type" Peter Xu
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 24+ messages in thread
From: Peter Xu @ 2025-11-25 20:46 UTC (permalink / raw)
  To: qemu-devel
  Cc: Markus Armbruster, Juraj Marcin, peterx, Marc-André Lureau,
	Fabiano Rosas, Daniel P . Berrangé,
	Vladimir Sementsov-Ogievskiy

There're only two use cases of g_autoptr to free Error objects in migration
code paths.

Due to the nature of how Error should be used (normally ownership will be
passed over to Error APIs, like error_report_err), auto-free functions may
be error prone on its own.  The auto cleanup function was accidentally
merged as pointed out by Dan and Markus:

https://lore.kernel.org/r/aSWSLMi6ZhTCS_p2@redhat.com

Remove the two use cases so that we can remove the auto cleanup function,
hence suggest to not use auto frees for Errors.

Suggested-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Peter Xu <peterx@redhat.com>
---
 migration/multifd-device-state.c | 3 ++-
 migration/savevm.c               | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/migration/multifd-device-state.c b/migration/multifd-device-state.c
index fce64f00b0..db3239fef5 100644
--- a/migration/multifd-device-state.c
+++ b/migration/multifd-device-state.c
@@ -140,7 +140,7 @@ static void multifd_device_state_save_thread_data_free(void *opaque)
 static int multifd_device_state_save_thread(void *opaque)
 {
     SaveCompletePrecopyThreadData *data = opaque;
-    g_autoptr(Error) local_err = NULL;
+    Error *local_err = NULL;
 
     if (!data->hdlr(data, &local_err)) {
         MigrationState *s = migrate_get_current();
@@ -159,6 +159,7 @@ static int multifd_device_state_save_thread(void *opaque)
          * return we end setting is purely arbitrary.
          */
         migrate_set_error(s, local_err);
+        error_free(local_err);
     }
 
     return 0;
diff --git a/migration/savevm.c b/migration/savevm.c
index 62cc2ce25c..638e9b364f 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -2823,7 +2823,7 @@ static int qemu_loadvm_load_thread(void *thread_opaque)
 {
     struct LoadThreadData *data = thread_opaque;
     MigrationIncomingState *mis = migration_incoming_get_current();
-    g_autoptr(Error) local_err = NULL;
+    Error *local_err = NULL;
 
     if (!data->function(data->opaque, &mis->load_threads_abort, &local_err)) {
         MigrationState *s = migrate_get_current();
@@ -2841,6 +2841,7 @@ static int qemu_loadvm_load_thread(void *thread_opaque)
          * return we end setting is purely arbitrary.
          */
         migrate_set_error(s, local_err);
+        error_free(local_err);
     }
 
     return 0;
-- 
2.50.1



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

* [PATCH for-11.0 2/6] Revert "error: define g_autoptr() cleanup function for the Error type"
  2025-11-25 20:46 [PATCH for-11.0 0/6] migration: Error reporting cleanups Peter Xu
  2025-11-25 20:46 ` [PATCH for-11.0 1/6] migration: Use explicit error_free() instead of g_autoptr Peter Xu
@ 2025-11-25 20:46 ` Peter Xu
  2025-11-26  6:45   ` Markus Armbruster
                     ` (4 more replies)
  2025-11-25 20:46 ` [PATCH for-11.0 3/6] migration: Make migration_connect_set_error() own the error Peter Xu
                   ` (3 subsequent siblings)
  5 siblings, 5 replies; 24+ messages in thread
From: Peter Xu @ 2025-11-25 20:46 UTC (permalink / raw)
  To: qemu-devel
  Cc: Markus Armbruster, Juraj Marcin, peterx, Marc-André Lureau,
	Fabiano Rosas, Daniel P . Berrangé,
	Vladimir Sementsov-Ogievskiy, Maciej S. Szmigiero,
	Cédric Le Goater

This reverts commit 18eb55546a54e443d94a4c49286348176ad4b00a.  Discussion
can be seen at:

https://lore.kernel.org/r/aSWSLMi6ZhTCS_p2@redhat.com

Cc: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
Cc: Cédric Le Goater <clg@redhat.com>
Signed-off-by: Peter Xu <peterx@redhat.com>
---
 include/qapi/error.h | 2 --
 1 file changed, 2 deletions(-)

diff --git a/include/qapi/error.h b/include/qapi/error.h
index b16c6303f8..f3ce4a4a2d 100644
--- a/include/qapi/error.h
+++ b/include/qapi/error.h
@@ -437,8 +437,6 @@ Error *error_copy(const Error *err);
  */
 void error_free(Error *err);
 
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(Error, error_free)
-
 /*
  * Convenience function to assert that *@errp is set, then silently free it.
  */
-- 
2.50.1



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

* [PATCH for-11.0 3/6] migration: Make migration_connect_set_error() own the error
  2025-11-25 20:46 [PATCH for-11.0 0/6] migration: Error reporting cleanups Peter Xu
  2025-11-25 20:46 ` [PATCH for-11.0 1/6] migration: Use explicit error_free() instead of g_autoptr Peter Xu
  2025-11-25 20:46 ` [PATCH for-11.0 2/6] Revert "error: define g_autoptr() cleanup function for the Error type" Peter Xu
@ 2025-11-25 20:46 ` Peter Xu
  2025-11-26  7:27   ` Markus Armbruster
  2025-11-25 20:46 ` [PATCH for-11.0 4/6] migration: Make multifd_send_set_error() " Peter Xu
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 24+ messages in thread
From: Peter Xu @ 2025-11-25 20:46 UTC (permalink / raw)
  To: qemu-devel
  Cc: Markus Armbruster, Juraj Marcin, peterx, Marc-André Lureau,
	Fabiano Rosas, Daniel P . Berrangé,
	Vladimir Sementsov-Ogievskiy

Make migration_connect_set_error() take ownership of the error always.
Paving way for making migrate_set_error() to take ownership.

Signed-off-by: Peter Xu <peterx@redhat.com>
---
 migration/channel.c   | 1 -
 migration/migration.c | 7 ++++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/migration/channel.c b/migration/channel.c
index 462cc183e1..92435fa7f7 100644
--- a/migration/channel.c
+++ b/migration/channel.c
@@ -95,7 +95,6 @@ void migration_channel_connect(MigrationState *s,
         }
     }
     migration_connect(s, error);
-    error_free(error);
 }
 
 
diff --git a/migration/migration.c b/migration/migration.c
index b316ee01ab..4fe69cc2ef 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -1575,7 +1575,7 @@ static void migrate_error_free(MigrationState *s)
     }
 }
 
-static void migration_connect_set_error(MigrationState *s, const Error *error)
+static void migration_connect_set_error(MigrationState *s, Error *error)
 {
     MigrationStatus current = s->state;
     MigrationStatus next;
@@ -1602,6 +1602,7 @@ static void migration_connect_set_error(MigrationState *s, const Error *error)
 
     migrate_set_state(&s->state, current, next);
     migrate_set_error(s, error);
+    error_free(error);
 }
 
 void migration_cancel(void)
@@ -2292,7 +2293,7 @@ void qmp_migrate(const char *uri, bool has_channels,
 
 out:
     if (local_err) {
-        migration_connect_set_error(s, local_err);
+        migration_connect_set_error(s, error_copy(local_err));
         error_propagate(errp, local_err);
     }
 }
@@ -2337,7 +2338,7 @@ static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested,
         if (!resume_requested) {
             yank_unregister_instance(MIGRATION_YANK_INSTANCE);
         }
-        migration_connect_set_error(s, local_err);
+        migration_connect_set_error(s, error_copy(local_err));
         error_propagate(errp, local_err);
         return;
     }
-- 
2.50.1



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

* [PATCH for-11.0 4/6] migration: Make multifd_send_set_error() own the error
  2025-11-25 20:46 [PATCH for-11.0 0/6] migration: Error reporting cleanups Peter Xu
                   ` (2 preceding siblings ...)
  2025-11-25 20:46 ` [PATCH for-11.0 3/6] migration: Make migration_connect_set_error() own the error Peter Xu
@ 2025-11-25 20:46 ` Peter Xu
  2025-11-26  7:30   ` Markus Armbruster
  2025-11-25 20:46 ` [PATCH for-11.0 5/6] migration: Make multifd_recv_terminate_threads() " Peter Xu
  2025-11-25 20:46 ` [PATCH for-11.0 6/6] migration: Replace migrate_set_error() with migrate_error_propagate() Peter Xu
  5 siblings, 1 reply; 24+ messages in thread
From: Peter Xu @ 2025-11-25 20:46 UTC (permalink / raw)
  To: qemu-devel
  Cc: Markus Armbruster, Juraj Marcin, peterx, Marc-André Lureau,
	Fabiano Rosas, Daniel P . Berrangé,
	Vladimir Sementsov-Ogievskiy

Make multifd_send_set_error() take ownership of the error always.  Paving
way for making migrate_set_error() to take ownership.

Signed-off-by: Peter Xu <peterx@redhat.com>
---
 migration/multifd.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/migration/multifd.c b/migration/multifd.c
index 3203dc98e1..930eee9949 100644
--- a/migration/multifd.c
+++ b/migration/multifd.c
@@ -429,6 +429,7 @@ static void multifd_send_set_error(Error *err)
     if (err) {
         MigrationState *s = migrate_get_current();
         migrate_set_error(s, err);
+        error_free(err);
         if (s->state == MIGRATION_STATUS_SETUP ||
             s->state == MIGRATION_STATUS_PRE_SWITCHOVER ||
             s->state == MIGRATION_STATUS_DEVICE ||
@@ -779,7 +780,6 @@ out:
         trace_multifd_send_error(p->id);
         multifd_send_set_error(local_err);
         multifd_send_kick_main(p);
-        error_free(local_err);
     }
 
     rcu_unregister_thread();
@@ -908,7 +908,6 @@ out:
      * cleanup code doesn't even know its existence.
      */
     object_unref(OBJECT(ioc));
-    error_free(local_err);
 }
 
 static bool multifd_new_send_channel_create(gpointer opaque, Error **errp)
-- 
2.50.1



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

* [PATCH for-11.0 5/6] migration: Make multifd_recv_terminate_threads() own the error
  2025-11-25 20:46 [PATCH for-11.0 0/6] migration: Error reporting cleanups Peter Xu
                   ` (3 preceding siblings ...)
  2025-11-25 20:46 ` [PATCH for-11.0 4/6] migration: Make multifd_send_set_error() " Peter Xu
@ 2025-11-25 20:46 ` Peter Xu
  2025-11-25 20:46 ` [PATCH for-11.0 6/6] migration: Replace migrate_set_error() with migrate_error_propagate() Peter Xu
  5 siblings, 0 replies; 24+ messages in thread
From: Peter Xu @ 2025-11-25 20:46 UTC (permalink / raw)
  To: qemu-devel
  Cc: Markus Armbruster, Juraj Marcin, peterx, Marc-André Lureau,
	Fabiano Rosas, Daniel P . Berrangé,
	Vladimir Sementsov-Ogievskiy

Make multifd_recv_terminate_threads() take ownership of the error always.
Paving way for making migrate_set_error() to take ownership.

Signed-off-by: Peter Xu <peterx@redhat.com>
---
 migration/multifd.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/migration/multifd.c b/migration/multifd.c
index 930eee9949..c861b4b557 100644
--- a/migration/multifd.c
+++ b/migration/multifd.c
@@ -1068,6 +1068,7 @@ static void multifd_recv_terminate_threads(Error *err)
     if (err) {
         MigrationState *s = migrate_get_current();
         migrate_set_error(s, err);
+        error_free(err);
         if (s->state == MIGRATION_STATUS_SETUP ||
             s->state == MIGRATION_STATUS_ACTIVE) {
             migrate_set_state(&s->state, s->state,
@@ -1434,7 +1435,6 @@ static void *multifd_recv_thread(void *opaque)
 
     if (local_err) {
         multifd_recv_terminate_threads(local_err);
-        error_free(local_err);
     }
 
     rcu_unregister_thread();
@@ -1535,7 +1535,7 @@ void multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
     if (use_packets) {
         id = multifd_recv_initial_packet(ioc, &local_err);
         if (id < 0) {
-            multifd_recv_terminate_threads(local_err);
+            multifd_recv_terminate_threads(error_copy(local_err));
             error_propagate_prepend(errp, local_err,
                                     "failed to receive packet"
                                     " via multifd channel %d: ",
@@ -1551,7 +1551,7 @@ void multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
     if (p->c != NULL) {
         error_setg(&local_err, "multifd: received id '%d' already setup'",
                    id);
-        multifd_recv_terminate_threads(local_err);
+        multifd_recv_terminate_threads(error_copy(local_err));
         error_propagate(errp, local_err);
         return;
     }
-- 
2.50.1



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

* [PATCH for-11.0 6/6] migration: Replace migrate_set_error() with migrate_error_propagate()
  2025-11-25 20:46 [PATCH for-11.0 0/6] migration: Error reporting cleanups Peter Xu
                   ` (4 preceding siblings ...)
  2025-11-25 20:46 ` [PATCH for-11.0 5/6] migration: Make multifd_recv_terminate_threads() " Peter Xu
@ 2025-11-25 20:46 ` Peter Xu
  2025-11-26  7:57   ` Markus Armbruster
  5 siblings, 1 reply; 24+ messages in thread
From: Peter Xu @ 2025-11-25 20:46 UTC (permalink / raw)
  To: qemu-devel
  Cc: Markus Armbruster, Juraj Marcin, peterx, Marc-André Lureau,
	Fabiano Rosas, Daniel P . Berrangé,
	Vladimir Sementsov-Ogievskiy

migrate_set_error() currently doesn't take ownership of the error being
passed in.  It's not aligned with the error API and meanwhile it also
makes most of the caller free the error explicitly.

Change the API to take the ownership of the Error object instead.  When at
it, remove the first parameter so it's friendly to g_clear_pointer().  It
can be used whenever the caller wants to provide extra safety measure (or
reuse the pointer) to reset the Error* pointer after stolen.

Signed-off-by: Peter Xu <peterx@redhat.com>
---
 migration/migration.h            |  2 +-
 migration/cpr-exec.c             |  4 +--
 migration/migration.c            | 46 +++++++++++++++-----------------
 migration/multifd-device-state.c |  5 +---
 migration/multifd.c              | 19 +++++++------
 migration/postcopy-ram.c         |  5 ++--
 migration/ram.c                  |  4 +--
 migration/savevm.c               | 15 ++++-------
 8 files changed, 42 insertions(+), 58 deletions(-)

diff --git a/migration/migration.h b/migration/migration.h
index 213b33fe6e..df74f9b14f 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -525,7 +525,7 @@ void migration_incoming_process(void);
 
 bool  migration_has_all_channels(void);
 
-void migrate_set_error(MigrationState *s, const Error *error);
+void migrate_error_propagate(Error *error);
 bool migrate_has_error(MigrationState *s);
 
 void migration_connect(MigrationState *s, Error *error_in);
diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
index 0b8344a86f..13e6138f56 100644
--- a/migration/cpr-exec.c
+++ b/migration/cpr-exec.c
@@ -158,9 +158,7 @@ static void cpr_exec_cb(void *opaque)
 
     error_report_err(error_copy(err));
     migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
-    migrate_set_error(s, err);
-    error_free(err);
-    err = NULL;
+    g_clear_pointer(&err, migrate_error_propagate);
 
     /* Note, we can go from state COMPLETED to FAILED */
     migration_call_notifiers(s, MIG_EVENT_PRECOPY_FAILED, NULL);
diff --git a/migration/migration.c b/migration/migration.c
index 4fe69cc2ef..219d3129cb 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -914,9 +914,7 @@ process_incoming_migration_co(void *opaque)
 fail:
     migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
                       MIGRATION_STATUS_FAILED);
-    migrate_set_error(s, local_err);
-    error_free(local_err);
-
+    migrate_error_propagate(local_err);
     migration_incoming_state_destroy();
 
     if (mis->exit_on_error) {
@@ -1548,14 +1546,22 @@ static void migration_cleanup_bh(void *opaque)
     migration_cleanup(opaque);
 }
 
-void migrate_set_error(MigrationState *s, const Error *error)
+/*
+ * Propagate the Error* object to migration core.  The caller mustn't
+ * reference the error pointer after the function returned, because the
+ * Error* object might be freed.
+ */
+void migrate_error_propagate(Error *error)
 {
-    QEMU_LOCK_GUARD(&s->error_mutex);
+    MigrationState *s = migrate_get_current();
 
+    QEMU_LOCK_GUARD(&s->error_mutex);
     trace_migrate_error(error_get_pretty(error));
 
     if (!s->error) {
-        s->error = error_copy(error);
+        s->error = error;
+    } else {
+        error_free(error);
     }
 }
 
@@ -1601,8 +1607,7 @@ static void migration_connect_set_error(MigrationState *s, Error *error)
     }
 
     migrate_set_state(&s->state, current, next);
-    migrate_set_error(s, error);
-    error_free(error);
+    migrate_error_propagate(error);
 }
 
 void migration_cancel(void)
@@ -2014,8 +2019,7 @@ void qmp_migrate_pause(Error **errp)
 
         /* Tell the core migration that we're pausing */
         error_setg(&error, "Postcopy migration is paused by the user");
-        migrate_set_error(ms, error);
-        error_free(error);
+        migrate_error_propagate(error);
 
         qemu_mutex_lock(&ms->qemu_file_lock);
         if (ms->to_dst_file) {
@@ -2647,8 +2651,7 @@ static void *source_return_path_thread(void *opaque)
 
 out:
     if (err) {
-        migrate_set_error(ms, err);
-        error_free(err);
+        migrate_error_propagate(err);
         trace_source_return_path_thread_bad_end();
     }
 
@@ -3094,12 +3097,10 @@ static void migration_completion(MigrationState *s)
 
 fail:
     if (qemu_file_get_error_obj(s->to_dst_file, &local_err)) {
-        migrate_set_error(s, local_err);
-        error_free(local_err);
+        migrate_error_propagate(local_err);
     } else if (ret) {
         error_setg_errno(&local_err, -ret, "Error in migration completion");
-        migrate_set_error(s, local_err);
-        error_free(local_err);
+        migrate_error_propagate(local_err);
     }
 
     if (s->state != MIGRATION_STATUS_CANCELLING) {
@@ -3326,8 +3327,7 @@ static MigThrError migration_detect_error(MigrationState *s)
     }
 
     if (local_error) {
-        migrate_set_error(s, local_error);
-        error_free(local_error);
+        migrate_error_propagate(local_error);
     }
 
     if (state == MIGRATION_STATUS_POSTCOPY_ACTIVE && ret) {
@@ -3522,7 +3522,7 @@ static MigIterateState migration_iteration_run(MigrationState *s)
         if (must_precopy <= s->threshold_size &&
             can_switchover && qatomic_read(&s->start_postcopy)) {
             if (postcopy_start(s, &local_err)) {
-                migrate_set_error(s, local_err);
+                migrate_error_propagate(error_copy(local_err));
                 error_report_err(local_err);
             }
             return MIG_ITERATE_SKIP;
@@ -3819,8 +3819,7 @@ static void *migration_thread(void *opaque)
      * devices to unplug. This to preserve migration state transitions.
      */
     if (ret) {
-        migrate_set_error(s, local_err);
-        error_free(local_err);
+        migrate_error_propagate(local_err);
         migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE,
                           MIGRATION_STATUS_FAILED);
         goto out;
@@ -3944,8 +3943,7 @@ static void *bg_migration_thread(void *opaque)
      * devices to unplug. This to preserve migration state transitions.
      */
     if (ret) {
-        migrate_set_error(s, local_err);
-        error_free(local_err);
+        migrate_error_propagate(local_err);
         migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE,
                           MIGRATION_STATUS_FAILED);
         goto fail_setup;
@@ -4127,7 +4125,7 @@ void migration_connect(MigrationState *s, Error *error_in)
     return;
 
 fail:
-    migrate_set_error(s, local_err);
+    migrate_error_propagate(error_copy(local_err));
     if (s->state != MIGRATION_STATUS_CANCELLING) {
         migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
     }
diff --git a/migration/multifd-device-state.c b/migration/multifd-device-state.c
index db3239fef5..3040d70872 100644
--- a/migration/multifd-device-state.c
+++ b/migration/multifd-device-state.c
@@ -143,8 +143,6 @@ static int multifd_device_state_save_thread(void *opaque)
     Error *local_err = NULL;
 
     if (!data->hdlr(data, &local_err)) {
-        MigrationState *s = migrate_get_current();
-
         /*
          * Can't call abort_device_state_save_threads() here since new
          * save threads could still be in process of being launched
@@ -158,8 +156,7 @@ static int multifd_device_state_save_thread(void *opaque)
          * In case of multiple save threads failing which thread error
          * return we end setting is purely arbitrary.
          */
-        migrate_set_error(s, local_err);
-        error_free(local_err);
+        migrate_error_propagate(local_err);
     }
 
     return 0;
diff --git a/migration/multifd.c b/migration/multifd.c
index c861b4b557..99717b64e9 100644
--- a/migration/multifd.c
+++ b/migration/multifd.c
@@ -428,8 +428,9 @@ static void multifd_send_set_error(Error *err)
 
     if (err) {
         MigrationState *s = migrate_get_current();
-        migrate_set_error(s, err);
-        error_free(err);
+
+        migrate_error_propagate(err);
+
         if (s->state == MIGRATION_STATUS_SETUP ||
             s->state == MIGRATION_STATUS_PRE_SWITCHOVER ||
             s->state == MIGRATION_STATUS_DEVICE ||
@@ -588,8 +589,7 @@ void multifd_send_shutdown(void)
         Error *local_err = NULL;
 
         if (!multifd_send_cleanup_channel(p, &local_err)) {
-            migrate_set_error(migrate_get_current(), local_err);
-            error_free(local_err);
+            migrate_error_propagate(local_err);
         }
     }
 
@@ -962,8 +962,7 @@ bool multifd_send_setup(void)
         p->write_flags = 0;
 
         if (!multifd_new_send_channel_create(p, &local_err)) {
-            migrate_set_error(s, local_err);
-            error_free(local_err);
+            migrate_error_propagate(local_err);
             ret = -1;
         }
     }
@@ -987,8 +986,7 @@ bool multifd_send_setup(void)
 
         ret = multifd_send_state->ops->send_setup(p, &local_err);
         if (ret) {
-            migrate_set_error(s, local_err);
-            error_free(local_err);
+            migrate_error_propagate(local_err);
             goto err;
         }
         assert(p->iov);
@@ -1067,8 +1065,9 @@ static void multifd_recv_terminate_threads(Error *err)
 
     if (err) {
         MigrationState *s = migrate_get_current();
-        migrate_set_error(s, err);
-        error_free(err);
+
+        migrate_error_propagate(err);
+
         if (s->state == MIGRATION_STATUS_SETUP ||
             s->state == MIGRATION_STATUS_ACTIVE) {
             migrate_set_state(&s->state, s->state,
diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
index 7c9fe61041..856366072a 100644
--- a/migration/postcopy-ram.c
+++ b/migration/postcopy-ram.c
@@ -1927,8 +1927,7 @@ postcopy_preempt_send_channel_done(MigrationState *s,
                                    QIOChannel *ioc, Error *local_err)
 {
     if (local_err) {
-        migrate_set_error(s, local_err);
-        error_free(local_err);
+        migrate_error_propagate(local_err);
     } else {
         migration_ioc_register_yank(ioc);
         s->postcopy_qemufile_src = qemu_file_new_output(ioc);
@@ -2162,7 +2161,7 @@ static void *postcopy_listen_thread(void *opaque)
              * exit depending on if postcopy-exit-on-error is true, but the
              * migration cannot be recovered.
              */
-            migrate_set_error(migr, local_err);
+            migrate_error_propagate(error_copy(local_err));
             error_report_err(local_err);
             migrate_set_state(&mis->state, mis->state, MIGRATION_STATUS_FAILED);
             goto out;
diff --git a/migration/ram.c b/migration/ram.c
index 29f016cb25..1d2a47526e 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -4730,9 +4730,7 @@ static void ram_mig_ram_block_resized(RAMBlockNotifier *n, void *host,
          * Abort and indicate a proper reason.
          */
         error_setg(&err, "RAM block '%s' resized during precopy.", rb->idstr);
-        migrate_set_error(migrate_get_current(), err);
-        error_free(err);
-
+        migrate_error_propagate(err);
         migration_cancel();
     }
 
diff --git a/migration/savevm.c b/migration/savevm.c
index 638e9b364f..ab9d1e579e 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -1125,13 +1125,12 @@ void qemu_savevm_send_open_return_path(QEMUFile *f)
 int qemu_savevm_send_packaged(QEMUFile *f, const uint8_t *buf, size_t len)
 {
     uint32_t tmp;
-    MigrationState *ms = migrate_get_current();
     Error *local_err = NULL;
 
     if (len > MAX_VM_CMD_PACKAGED_SIZE) {
         error_setg(&local_err, "%s: Unreasonably large packaged state: %zu",
                      __func__, len);
-        migrate_set_error(ms, local_err);
+        migrate_error_propagate(error_copy(local_err));
         error_report_err(local_err);
         return -1;
     }
@@ -1373,7 +1372,7 @@ int qemu_savevm_state_setup(QEMUFile *f, Error **errp)
         if (se->vmsd && se->vmsd->early_setup) {
             ret = vmstate_save(f, se, vmdesc, errp);
             if (ret) {
-                migrate_set_error(ms, *errp);
+                migrate_error_propagate(error_copy(*errp));
                 qemu_file_set_error(f, ret);
                 break;
             }
@@ -1681,7 +1680,7 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f,
 
         ret = vmstate_save(f, se, vmdesc, &local_err);
         if (ret) {
-            migrate_set_error(ms, local_err);
+            migrate_error_propagate(error_copy(local_err));
             error_report_err(local_err);
             qemu_file_set_error(f, ret);
             return ret;
@@ -1858,7 +1857,6 @@ void qemu_savevm_live_state(QEMUFile *f)
 
 int qemu_save_device_state(QEMUFile *f)
 {
-    MigrationState *ms = migrate_get_current();
     Error *local_err = NULL;
     SaveStateEntry *se;
 
@@ -1876,7 +1874,7 @@ int qemu_save_device_state(QEMUFile *f)
         }
         ret = vmstate_save(f, se, NULL, &local_err);
         if (ret) {
-            migrate_set_error(ms, local_err);
+            migrate_error_propagate(error_copy(local_err));
             error_report_err(local_err);
             return ret;
         }
@@ -2826,8 +2824,6 @@ static int qemu_loadvm_load_thread(void *thread_opaque)
     Error *local_err = NULL;
 
     if (!data->function(data->opaque, &mis->load_threads_abort, &local_err)) {
-        MigrationState *s = migrate_get_current();
-
         /*
          * Can't set load_threads_abort here since processing of main migration
          * channel data could still be happening, resulting in launching of new
@@ -2840,8 +2836,7 @@ static int qemu_loadvm_load_thread(void *thread_opaque)
          * In case of multiple load threads failing which thread error
          * return we end setting is purely arbitrary.
          */
-        migrate_set_error(s, local_err);
-        error_free(local_err);
+        migrate_error_propagate(local_err);
     }
 
     return 0;
-- 
2.50.1



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

* Re: [PATCH for-11.0 1/6] migration: Use explicit error_free() instead of g_autoptr
  2025-11-25 20:46 ` [PATCH for-11.0 1/6] migration: Use explicit error_free() instead of g_autoptr Peter Xu
@ 2025-11-26  6:43   ` Markus Armbruster
  0 siblings, 0 replies; 24+ messages in thread
From: Markus Armbruster @ 2025-11-26  6:43 UTC (permalink / raw)
  To: Peter Xu
  Cc: qemu-devel, Juraj Marcin, Marc-André Lureau, Fabiano Rosas,
	Daniel P . Berrangé, Vladimir Sementsov-Ogievskiy

Peter Xu <peterx@redhat.com> writes:

> There're only two use cases of g_autoptr to free Error objects in migration
> code paths.
>
> Due to the nature of how Error should be used (normally ownership will be
> passed over to Error APIs, like error_report_err), auto-free functions may
> be error prone on its own.  The auto cleanup function was accidentally
> merged as pointed out by Dan and Markus:
>
> https://lore.kernel.org/r/aSWSLMi6ZhTCS_p2@redhat.com

Perhaps "merged without proper review" would be more accurate.

> Remove the two use cases so that we can remove the auto cleanup function,
> hence suggest to not use auto frees for Errors.
>
> Suggested-by: Markus Armbruster <armbru@redhat.com>
> Signed-off-by: Peter Xu <peterx@redhat.com>

Reviewed-by: Markus Armbruster <armbru@redhat.com>



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

* Re: [PATCH for-11.0 2/6] Revert "error: define g_autoptr() cleanup function for the Error type"
  2025-11-25 20:46 ` [PATCH for-11.0 2/6] Revert "error: define g_autoptr() cleanup function for the Error type" Peter Xu
@ 2025-11-26  6:45   ` Markus Armbruster
  2025-11-26  7:43   ` Cédric Le Goater
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 24+ messages in thread
From: Markus Armbruster @ 2025-11-26  6:45 UTC (permalink / raw)
  To: Peter Xu
  Cc: qemu-devel, Markus Armbruster, Juraj Marcin,
	Marc-André Lureau, Fabiano Rosas, Daniel P . Berrangé,
	Vladimir Sementsov-Ogievskiy, Maciej S. Szmigiero,
	Cédric Le Goater

Peter Xu <peterx@redhat.com> writes:

> This reverts commit 18eb55546a54e443d94a4c49286348176ad4b00a.  Discussion
> can be seen at:
>
> https://lore.kernel.org/r/aSWSLMi6ZhTCS_p2@redhat.com

Suggest to steal from the previous commit like this:

  Due to the nature of how Error should be used (normally ownership will be
  passed over to Error APIs, like error_report_err), auto-free functions may
  be error prone on its own.  The auto cleanup function was merged without 
  proper review as pointed out by Dan and Markus:

  https://lore.kernel.org/r/aSWSLMi6ZhTCS_p2@redhat.com

> Cc: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
> Cc: Cédric Le Goater <clg@redhat.com>
> Signed-off-by: Peter Xu <peterx@redhat.com>
> ---
>  include/qapi/error.h | 2 --
>  1 file changed, 2 deletions(-)
>
> diff --git a/include/qapi/error.h b/include/qapi/error.h
> index b16c6303f8..f3ce4a4a2d 100644
> --- a/include/qapi/error.h
> +++ b/include/qapi/error.h
> @@ -437,8 +437,6 @@ Error *error_copy(const Error *err);
>   */
>  void error_free(Error *err);
>  
> -G_DEFINE_AUTOPTR_CLEANUP_FUNC(Error, error_free)
> -
>  /*
>   * Convenience function to assert that *@errp is set, then silently free it.
>   */

Reviewed-by: Markus Armbruster <armbru@redhat.com>



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

* Re: [PATCH for-11.0 3/6] migration: Make migration_connect_set_error() own the error
  2025-11-25 20:46 ` [PATCH for-11.0 3/6] migration: Make migration_connect_set_error() own the error Peter Xu
@ 2025-11-26  7:27   ` Markus Armbruster
  2025-11-28 16:58     ` Peter Xu
  0 siblings, 1 reply; 24+ messages in thread
From: Markus Armbruster @ 2025-11-26  7:27 UTC (permalink / raw)
  To: Peter Xu
  Cc: qemu-devel, Juraj Marcin, Marc-André Lureau, Fabiano Rosas,
	Daniel P . Berrangé, Vladimir Sementsov-Ogievskiy

Peter Xu <peterx@redhat.com> writes:

> Make migration_connect_set_error() take ownership of the error always.
> Paving way for making migrate_set_error() to take ownership.
>
> Signed-off-by: Peter Xu <peterx@redhat.com>
> ---
>  migration/channel.c   | 1 -
>  migration/migration.c | 7 ++++---
>  2 files changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/migration/channel.c b/migration/channel.c
> index 462cc183e1..92435fa7f7 100644
> --- a/migration/channel.c
> +++ b/migration/channel.c
> @@ -95,7 +95,6 @@ void migration_channel_connect(MigrationState *s,
>          }
>      }
>      migration_connect(s, error);
> -    error_free(error);
>  }
>  
>  
> diff --git a/migration/migration.c b/migration/migration.c
> index b316ee01ab..4fe69cc2ef 100644
> --- a/migration/migration.c
> +++ b/migration/migration.c
> @@ -1575,7 +1575,7 @@ static void migrate_error_free(MigrationState *s)
>      }
>  }
>  
> -static void migration_connect_set_error(MigrationState *s, const Error *error)
> +static void migration_connect_set_error(MigrationState *s, Error *error)

Recommend to rename for the same reason you rename migrate_set_error()
in the last patch.

Bonus: all calls become visible in the patch, easing review.

>  {
>      MigrationStatus current = s->state;
>      MigrationStatus next;
> @@ -1602,6 +1602,7 @@ static void migration_connect_set_error(MigrationState *s, const Error *error)
>  
>      migrate_set_state(&s->state, current, next);
>      migrate_set_error(s, error);
> +    error_free(error);
>  }
>  
>  void migration_cancel(void)
> @@ -2292,7 +2293,7 @@ void qmp_migrate(const char *uri, bool has_channels,
>  
>  out:
>      if (local_err) {
> -        migration_connect_set_error(s, local_err);
> +        migration_connect_set_error(s, error_copy(local_err));
>          error_propagate(errp, local_err);
>      }
>  }
> @@ -2337,7 +2338,7 @@ static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested,
>          if (!resume_requested) {
>              yank_unregister_instance(MIGRATION_YANK_INSTANCE);
>          }
> -        migration_connect_set_error(s, local_err);
> +        migration_connect_set_error(s, error_copy(local_err));
>          error_propagate(errp, local_err);
>          return;
>      }

There's one more call in migration_connect().  Doesn't it need an
error_copy() now?  Oh, I see: it doesn't, because its only caller
migration_channel_connect() loses its error_free() in the first hunk.

So, migration_connect() *also* takes ownership now.  The commit message
should cover this.

Aside: I'd be tempted to lift the if (error_in) code from
migration_connect() to migration_channel_connect() and drop the
@error_in parameter.



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

* Re: [PATCH for-11.0 4/6] migration: Make multifd_send_set_error() own the error
  2025-11-25 20:46 ` [PATCH for-11.0 4/6] migration: Make multifd_send_set_error() " Peter Xu
@ 2025-11-26  7:30   ` Markus Armbruster
  2025-11-26  7:32     ` Markus Armbruster
  0 siblings, 1 reply; 24+ messages in thread
From: Markus Armbruster @ 2025-11-26  7:30 UTC (permalink / raw)
  To: Peter Xu
  Cc: qemu-devel, Juraj Marcin, Marc-André Lureau, Fabiano Rosas,
	Daniel P . Berrangé, Vladimir Sementsov-Ogievskiy

Peter Xu <peterx@redhat.com> writes:

> Make multifd_send_set_error() take ownership of the error always.  Paving
> way for making migrate_set_error() to take ownership.
>
> Signed-off-by: Peter Xu <peterx@redhat.com>

Reviewed-by: Markus Armbruster <armbru@redhat.com>



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

* Re: [PATCH for-11.0 4/6] migration: Make multifd_send_set_error() own the error
  2025-11-26  7:30   ` Markus Armbruster
@ 2025-11-26  7:32     ` Markus Armbruster
  0 siblings, 0 replies; 24+ messages in thread
From: Markus Armbruster @ 2025-11-26  7:32 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Peter Xu, qemu-devel, Juraj Marcin, Marc-André Lureau,
	Fabiano Rosas, Daniel P . Berrangé,
	Vladimir Sementsov-Ogievskiy

Markus Armbruster <armbru@redhat.com> writes:

> Peter Xu <peterx@redhat.com> writes:
>
>> Make multifd_send_set_error() take ownership of the error always.  Paving
>> way for making migrate_set_error() to take ownership.
>>
>> Signed-off-by: Peter Xu <peterx@redhat.com>
>
> Reviewed-by: Markus Armbruster <armbru@redhat.com>

Forgot to mention: recommend to rename for the same reason you rename
migrate_set_error() in the last patch.



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

* Re: [PATCH for-11.0 2/6] Revert "error: define g_autoptr() cleanup function for the Error type"
  2025-11-25 20:46 ` [PATCH for-11.0 2/6] Revert "error: define g_autoptr() cleanup function for the Error type" Peter Xu
  2025-11-26  6:45   ` Markus Armbruster
@ 2025-11-26  7:43   ` Cédric Le Goater
  2025-11-26  8:08     ` Markus Armbruster
  2025-11-26 14:34   ` [PATCH 2.5/6] error: Explain why we don't g_autoptr(Error) Markus Armbruster
                     ` (2 subsequent siblings)
  4 siblings, 1 reply; 24+ messages in thread
From: Cédric Le Goater @ 2025-11-26  7:43 UTC (permalink / raw)
  To: Peter Xu, qemu-devel
  Cc: Markus Armbruster, Juraj Marcin, Marc-André Lureau,
	Fabiano Rosas, Daniel P . Berrangé,
	Vladimir Sementsov-Ogievskiy, Maciej S. Szmigiero

On 11/25/25 21:46, Peter Xu wrote:
> This reverts commit 18eb55546a54e443d94a4c49286348176ad4b00a.  Discussion
> can be seen at:
> 
> https://lore.kernel.org/r/aSWSLMi6ZhTCS_p2@redhat.com
> 
> Cc: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
> Cc: Cédric Le Goater <clg@redhat.com>
> Signed-off-by: Peter Xu <peterx@redhat.com>
> ---
>   include/qapi/error.h | 2 --
>   1 file changed, 2 deletions(-)
> 
> diff --git a/include/qapi/error.h b/include/qapi/error.h
> index b16c6303f8..f3ce4a4a2d 100644
> --- a/include/qapi/error.h
> +++ b/include/qapi/error.h
> @@ -437,8 +437,6 @@ Error *error_copy(const Error *err);
>    */
>   void error_free(Error *err);
>   
> -G_DEFINE_AUTOPTR_CLEANUP_FUNC(Error, error_free)
> -
>   /*
>    * Convenience function to assert that *@errp is set, then silently free it.
>    */

Is that related to CID 1643463 issue ?

anyhow,


Reviewed-by: Cédric Le Goater <clg@redhat.com>

Thanks,

C.




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

* Re: [PATCH for-11.0 6/6] migration: Replace migrate_set_error() with migrate_error_propagate()
  2025-11-25 20:46 ` [PATCH for-11.0 6/6] migration: Replace migrate_set_error() with migrate_error_propagate() Peter Xu
@ 2025-11-26  7:57   ` Markus Armbruster
  2025-12-01 17:32     ` Peter Xu
  0 siblings, 1 reply; 24+ messages in thread
From: Markus Armbruster @ 2025-11-26  7:57 UTC (permalink / raw)
  To: Peter Xu
  Cc: qemu-devel, Juraj Marcin, Marc-André Lureau, Fabiano Rosas,
	Daniel P . Berrangé, Vladimir Sementsov-Ogievskiy

Peter Xu <peterx@redhat.com> writes:

> migrate_set_error() currently doesn't take ownership of the error being
> passed in.  It's not aligned with the error API and meanwhile it also
> makes most of the caller free the error explicitly.
>
> Change the API to take the ownership of the Error object instead.  When at
> it, remove the first parameter so it's friendly to g_clear_pointer().  It
> can be used whenever the caller wants to provide extra safety measure (or
> reuse the pointer) to reset the Error* pointer after stolen.
>
> Signed-off-by: Peter Xu <peterx@redhat.com>

Worth mentioning that this avoids Error object copies?

> ---
>  migration/migration.h            |  2 +-
>  migration/cpr-exec.c             |  4 +--
>  migration/migration.c            | 46 +++++++++++++++-----------------
>  migration/multifd-device-state.c |  5 +---
>  migration/multifd.c              | 19 +++++++------
>  migration/postcopy-ram.c         |  5 ++--
>  migration/ram.c                  |  4 +--
>  migration/savevm.c               | 15 ++++-------
>  8 files changed, 42 insertions(+), 58 deletions(-)
>
> diff --git a/migration/migration.h b/migration/migration.h
> index 213b33fe6e..df74f9b14f 100644
> --- a/migration/migration.h
> +++ b/migration/migration.h
> @@ -525,7 +525,7 @@ void migration_incoming_process(void);
>  
>  bool  migration_has_all_channels(void);
>  
> -void migrate_set_error(MigrationState *s, const Error *error);
> +void migrate_error_propagate(Error *error);
>  bool migrate_has_error(MigrationState *s);
>  
>  void migration_connect(MigrationState *s, Error *error_in);
> diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
> index 0b8344a86f..13e6138f56 100644
> --- a/migration/cpr-exec.c
> +++ b/migration/cpr-exec.c
> @@ -158,9 +158,7 @@ static void cpr_exec_cb(void *opaque)
>  
>      error_report_err(error_copy(err));
>      migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
> -    migrate_set_error(s, err);
> -    error_free(err);
> -    err = NULL;
> +    g_clear_pointer(&err, migrate_error_propagate);
>  
>      /* Note, we can go from state COMPLETED to FAILED */
>      migration_call_notifiers(s, MIG_EVENT_PRECOPY_FAILED, NULL);

I dislike g_clear_pointer(), and I dislike the change from taking the
migration state as argument to getting the global migration state with
migrate_get_current().  The loss of similarity to error_propagate() is
unfortunate, but tolerable.  This is not a demand.

For each hunk, we need to prove that the migrate_set_error()'s first
argument before the patch equals migrate_get_current() afterwards.

For this hunk, it's locally obvious: @s is initialized to
migrate_get_current() at the beginning of the funtion.

Where it's not locally obvious, I guess we could argue that just one
MigrationState object exists, so a MigrationState * can only point to
that one.

If locally non-obvious hunks exist, such an argument needs to be made in
the commit message.

I did *not* check this aspect of the patch.

> diff --git a/migration/migration.c b/migration/migration.c
> index 4fe69cc2ef..219d3129cb 100644
> --- a/migration/migration.c
> +++ b/migration/migration.c
> @@ -914,9 +914,7 @@ process_incoming_migration_co(void *opaque)
>  fail:
>      migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
>                        MIGRATION_STATUS_FAILED);
> -    migrate_set_error(s, local_err);
> -    error_free(local_err);
> -
> +    migrate_error_propagate(local_err);
>      migration_incoming_state_destroy();
>  
>      if (mis->exit_on_error) {
> @@ -1548,14 +1546,22 @@ static void migration_cleanup_bh(void *opaque)
>      migration_cleanup(opaque);
>  }
>  
> -void migrate_set_error(MigrationState *s, const Error *error)
> +/*
> + * Propagate the Error* object to migration core.  The caller mustn't
> + * reference the error pointer after the function returned, because the
> + * Error* object might be freed.
> + */
> +void migrate_error_propagate(Error *error)
>  {
> -    QEMU_LOCK_GUARD(&s->error_mutex);
> +    MigrationState *s = migrate_get_current();
>  
> +    QEMU_LOCK_GUARD(&s->error_mutex);
>      trace_migrate_error(error_get_pretty(error));
>  
>      if (!s->error) {
> -        s->error = error_copy(error);
> +        s->error = error;
> +    } else {
> +        error_free(error);
>      }
>  }
>  
> @@ -1601,8 +1607,7 @@ static void migration_connect_set_error(MigrationState *s, Error *error)
>      }
>  
>      migrate_set_state(&s->state, current, next);
> -    migrate_set_error(s, error);
> -    error_free(error);
> +    migrate_error_propagate(error);
>  }
>  
>  void migration_cancel(void)
> @@ -2014,8 +2019,7 @@ void qmp_migrate_pause(Error **errp)
>  
>          /* Tell the core migration that we're pausing */
>          error_setg(&error, "Postcopy migration is paused by the user");
> -        migrate_set_error(ms, error);
> -        error_free(error);
> +        migrate_error_propagate(error);
>  
>          qemu_mutex_lock(&ms->qemu_file_lock);
>          if (ms->to_dst_file) {
> @@ -2647,8 +2651,7 @@ static void *source_return_path_thread(void *opaque)
>  
>  out:
>      if (err) {
> -        migrate_set_error(ms, err);
> -        error_free(err);
> +        migrate_error_propagate(err);
>          trace_source_return_path_thread_bad_end();
>      }
>  
> @@ -3094,12 +3097,10 @@ static void migration_completion(MigrationState *s)
>  
>  fail:
>      if (qemu_file_get_error_obj(s->to_dst_file, &local_err)) {
> -        migrate_set_error(s, local_err);
> -        error_free(local_err);
> +        migrate_error_propagate(local_err);
>      } else if (ret) {
>          error_setg_errno(&local_err, -ret, "Error in migration completion");
> -        migrate_set_error(s, local_err);
> -        error_free(local_err);
> +        migrate_error_propagate(local_err);
>      }
>  
>      if (s->state != MIGRATION_STATUS_CANCELLING) {
> @@ -3326,8 +3327,7 @@ static MigThrError migration_detect_error(MigrationState *s)
>      }
>  
>      if (local_error) {
> -        migrate_set_error(s, local_error);
> -        error_free(local_error);
> +        migrate_error_propagate(local_error);
>      }
>  
>      if (state == MIGRATION_STATUS_POSTCOPY_ACTIVE && ret) {
> @@ -3522,7 +3522,7 @@ static MigIterateState migration_iteration_run(MigrationState *s)
>          if (must_precopy <= s->threshold_size &&
>              can_switchover && qatomic_read(&s->start_postcopy)) {
>              if (postcopy_start(s, &local_err)) {
> -                migrate_set_error(s, local_err);
> +                migrate_error_propagate(error_copy(local_err));

First hunk where we don't save a copy.  Reason: we report in addition to
propagate.  There are a several more below.

"Forking" the error like this gives me an uneasy feeling, as explained
in
    Subject: Re: [PATCH 0/3] migration: Error fixes and improvements
    Date: Tue, 18 Nov 2025 08:44:32 +0100
    Message-ID: <87a50jlr8f.fsf@pond.sub.org>

Clearly out of scope for this series.

>                  error_report_err(local_err);
>              }
>              return MIG_ITERATE_SKIP;
> @@ -3819,8 +3819,7 @@ static void *migration_thread(void *opaque)
>       * devices to unplug. This to preserve migration state transitions.
>       */
>      if (ret) {
> -        migrate_set_error(s, local_err);
> -        error_free(local_err);
> +        migrate_error_propagate(local_err);
>          migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE,
>                            MIGRATION_STATUS_FAILED);
>          goto out;
> @@ -3944,8 +3943,7 @@ static void *bg_migration_thread(void *opaque)
>       * devices to unplug. This to preserve migration state transitions.
>       */
>      if (ret) {
> -        migrate_set_error(s, local_err);
> -        error_free(local_err);
> +        migrate_error_propagate(local_err);
>          migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE,
>                            MIGRATION_STATUS_FAILED);
>          goto fail_setup;
> @@ -4127,7 +4125,7 @@ void migration_connect(MigrationState *s, Error *error_in)
>      return;
>  
>  fail:
> -    migrate_set_error(s, local_err);
> +    migrate_error_propagate(error_copy(local_err));
>      if (s->state != MIGRATION_STATUS_CANCELLING) {
>          migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
>      }
> diff --git a/migration/multifd-device-state.c b/migration/multifd-device-state.c
> index db3239fef5..3040d70872 100644
> --- a/migration/multifd-device-state.c
> +++ b/migration/multifd-device-state.c
> @@ -143,8 +143,6 @@ static int multifd_device_state_save_thread(void *opaque)
>      Error *local_err = NULL;
>  
>      if (!data->hdlr(data, &local_err)) {
> -        MigrationState *s = migrate_get_current();
> -
>          /*
>           * Can't call abort_device_state_save_threads() here since new
>           * save threads could still be in process of being launched
> @@ -158,8 +156,7 @@ static int multifd_device_state_save_thread(void *opaque)
>           * In case of multiple save threads failing which thread error
>           * return we end setting is purely arbitrary.
>           */
> -        migrate_set_error(s, local_err);
> -        error_free(local_err);
> +        migrate_error_propagate(local_err);
>      }
>  
>      return 0;
> diff --git a/migration/multifd.c b/migration/multifd.c
> index c861b4b557..99717b64e9 100644
> --- a/migration/multifd.c
> +++ b/migration/multifd.c
> @@ -428,8 +428,9 @@ static void multifd_send_set_error(Error *err)
>  
>      if (err) {
>          MigrationState *s = migrate_get_current();
> -        migrate_set_error(s, err);
> -        error_free(err);
> +
> +        migrate_error_propagate(err);
> +
>          if (s->state == MIGRATION_STATUS_SETUP ||
>              s->state == MIGRATION_STATUS_PRE_SWITCHOVER ||
>              s->state == MIGRATION_STATUS_DEVICE ||
> @@ -588,8 +589,7 @@ void multifd_send_shutdown(void)
>          Error *local_err = NULL;
>  
>          if (!multifd_send_cleanup_channel(p, &local_err)) {
> -            migrate_set_error(migrate_get_current(), local_err);
> -            error_free(local_err);
> +            migrate_error_propagate(local_err);
>          }
>      }
>  
> @@ -962,8 +962,7 @@ bool multifd_send_setup(void)
>          p->write_flags = 0;
>  
>          if (!multifd_new_send_channel_create(p, &local_err)) {
> -            migrate_set_error(s, local_err);
> -            error_free(local_err);
> +            migrate_error_propagate(local_err);
>              ret = -1;
>          }
>      }
> @@ -987,8 +986,7 @@ bool multifd_send_setup(void)
>  
>          ret = multifd_send_state->ops->send_setup(p, &local_err);
>          if (ret) {
> -            migrate_set_error(s, local_err);
> -            error_free(local_err);
> +            migrate_error_propagate(local_err);
>              goto err;
>          }
>          assert(p->iov);
> @@ -1067,8 +1065,9 @@ static void multifd_recv_terminate_threads(Error *err)
>  
>      if (err) {
>          MigrationState *s = migrate_get_current();
> -        migrate_set_error(s, err);
> -        error_free(err);
> +
> +        migrate_error_propagate(err);
> +
>          if (s->state == MIGRATION_STATUS_SETUP ||
>              s->state == MIGRATION_STATUS_ACTIVE) {
>              migrate_set_state(&s->state, s->state,
> diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
> index 7c9fe61041..856366072a 100644
> --- a/migration/postcopy-ram.c
> +++ b/migration/postcopy-ram.c
> @@ -1927,8 +1927,7 @@ postcopy_preempt_send_channel_done(MigrationState *s,
>                                     QIOChannel *ioc, Error *local_err)
>  {
>      if (local_err) {
> -        migrate_set_error(s, local_err);
> -        error_free(local_err);
> +        migrate_error_propagate(local_err);
>      } else {
>          migration_ioc_register_yank(ioc);
>          s->postcopy_qemufile_src = qemu_file_new_output(ioc);
> @@ -2162,7 +2161,7 @@ static void *postcopy_listen_thread(void *opaque)
>               * exit depending on if postcopy-exit-on-error is true, but the
>               * migration cannot be recovered.
>               */
> -            migrate_set_error(migr, local_err);
> +            migrate_error_propagate(error_copy(local_err));
>              error_report_err(local_err);
>              migrate_set_state(&mis->state, mis->state, MIGRATION_STATUS_FAILED);
>              goto out;
> diff --git a/migration/ram.c b/migration/ram.c
> index 29f016cb25..1d2a47526e 100644
> --- a/migration/ram.c
> +++ b/migration/ram.c
> @@ -4730,9 +4730,7 @@ static void ram_mig_ram_block_resized(RAMBlockNotifier *n, void *host,
>           * Abort and indicate a proper reason.
>           */
>          error_setg(&err, "RAM block '%s' resized during precopy.", rb->idstr);
> -        migrate_set_error(migrate_get_current(), err);
> -        error_free(err);
> -
> +        migrate_error_propagate(err);
>          migration_cancel();
>      }
>  
> diff --git a/migration/savevm.c b/migration/savevm.c
> index 638e9b364f..ab9d1e579e 100644
> --- a/migration/savevm.c
> +++ b/migration/savevm.c
> @@ -1125,13 +1125,12 @@ void qemu_savevm_send_open_return_path(QEMUFile *f)
>  int qemu_savevm_send_packaged(QEMUFile *f, const uint8_t *buf, size_t len)
>  {
>      uint32_t tmp;
> -    MigrationState *ms = migrate_get_current();
>      Error *local_err = NULL;
>  
>      if (len > MAX_VM_CMD_PACKAGED_SIZE) {
>          error_setg(&local_err, "%s: Unreasonably large packaged state: %zu",
>                       __func__, len);
> -        migrate_set_error(ms, local_err);
> +        migrate_error_propagate(error_copy(local_err));
>          error_report_err(local_err);
>          return -1;
>      }
> @@ -1373,7 +1372,7 @@ int qemu_savevm_state_setup(QEMUFile *f, Error **errp)
>          if (se->vmsd && se->vmsd->early_setup) {
>              ret = vmstate_save(f, se, vmdesc, errp);
>              if (ret) {
> -                migrate_set_error(ms, *errp);
> +                migrate_error_propagate(error_copy(*errp));

Here, we need to keep the copy because we additionally propagate to the
caller.

>                  qemu_file_set_error(f, ret);
>                  break;
>              }

[...]



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

* Re: [PATCH for-11.0 2/6] Revert "error: define g_autoptr() cleanup function for the Error type"
  2025-11-26  7:43   ` Cédric Le Goater
@ 2025-11-26  8:08     ` Markus Armbruster
  0 siblings, 0 replies; 24+ messages in thread
From: Markus Armbruster @ 2025-11-26  8:08 UTC (permalink / raw)
  To: Cédric Le Goater
  Cc: Peter Xu, qemu-devel, Juraj Marcin, Marc-André Lureau,
	Fabiano Rosas, Daniel P . Berrangé,
	Vladimir Sementsov-Ogievskiy, Maciej S. Szmigiero

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

> On 11/25/25 21:46, Peter Xu wrote:
>> This reverts commit 18eb55546a54e443d94a4c49286348176ad4b00a.  Discussion
>> can be seen at:
>> https://lore.kernel.org/r/aSWSLMi6ZhTCS_p2@redhat.com
>> Cc: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
>> Cc: Cédric Le Goater <clg@redhat.com>
>> Signed-off-by: Peter Xu <peterx@redhat.com>
>> ---
>>  include/qapi/error.h | 2 --
>>  1 file changed, 2 deletions(-)
>> diff --git a/include/qapi/error.h b/include/qapi/error.h
>> index b16c6303f8..f3ce4a4a2d 100644
>> --- a/include/qapi/error.h
>> +++ b/include/qapi/error.h
>> @@ -437,8 +437,6 @@ Error *error_copy(const Error *err);
>>   */
>>  void error_free(Error *err);
>>
>> -G_DEFINE_AUTOPTR_CLEANUP_FUNC(Error, error_free)
>> -
>>  /*
>>   * Convenience function to assert that *@errp is set, then silently free it.
>>   */
>
> Is that related to CID 1643463 issue ?

g_autoptr(Error) is a bad idea, as discussed in

    Subject: g_autoptr(Error) (was: [PATCH] migration: Fix double-free on error path) 
    Date: Tue, 25 Nov 2025 08:40:07 +0100
    Message-ID: <871plmk1bc.fsf@pond.sub.org>

We have three instances of g_autoptr(Error) in master.  CID 1643463 made
me see them.

One is removed by my fix to CID 1643463:

    Subject: [PATCH] migration: Fix double-free on error path
    Date: Tue, 25 Nov 2025 08:05:54 +0100
    Message-ID: <20251125070554.2256181-1-armbru@redhat.com>

The remaining two get removed in PATCH 1.  This patch deletes the code
that makes g_autoptr(Error) work.

> anyhow,
>
>
> Reviewed-by: Cédric Le Goater <clg@redhat.com>

Thank you!



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

* [PATCH 2.5/6] error: Explain why we don't g_autoptr(Error)
  2025-11-25 20:46 ` [PATCH for-11.0 2/6] Revert "error: define g_autoptr() cleanup function for the Error type" Peter Xu
  2025-11-26  6:45   ` Markus Armbruster
  2025-11-26  7:43   ` Cédric Le Goater
@ 2025-11-26 14:34   ` Markus Armbruster
  2025-11-26 15:14     ` Cédric Le Goater
  2025-11-26 20:21   ` [PATCH for-11.0 2/6] Revert "error: define g_autoptr() cleanup function for the Error type" Maciej S. Szmigiero
  2025-11-27  7:10   ` [PATCH 2.5/6] error: Poison g_autoptr(Error) to prevent its use Markus Armbruster
  4 siblings, 1 reply; 24+ messages in thread
From: Markus Armbruster @ 2025-11-26 14:34 UTC (permalink / raw)
  To: qemu-devel
  Cc: jmarcin, peterx, marcandre.lureau, farosas, berrange, vsementsov,
	mail, clg, peter.maydell

Suggested-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 include/qapi/error.h | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/include/qapi/error.h b/include/qapi/error.h
index f3ce4a4a2d..fc018b4c59 100644
--- a/include/qapi/error.h
+++ b/include/qapi/error.h
@@ -437,6 +437,23 @@ Error *error_copy(const Error *err);
  */
 void error_free(Error *err);
 
+/*
+ * Note: we intentionally do not enable g_autoptr(Error) with
+ * G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(Error, error_free).
+ *
+ * Functions that report or propagate an error take ownership of the
+ * Error object.  Explicit error_free() is needed when you handle an
+ * error in some other way.  This is rare.
+ *
+ * g_autoptr(Error) would call error_free() automatically on return.
+ * To avoid a double-free, we'd have to manually clear the pointer
+ * every time we propagate or report.
+ *
+ * Thus, g_autoptr(Error) would make the rare case easier to get right
+ * (less prone to leaks), and the common case easier to get wrong
+ * (more prone to double-free).
+ */
+
 /*
  * Convenience function to assert that *@errp is set, then silently free it.
  */
-- 
2.49.0



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

* Re: [PATCH 2.5/6] error: Explain why we don't g_autoptr(Error)
  2025-11-26 14:34   ` [PATCH 2.5/6] error: Explain why we don't g_autoptr(Error) Markus Armbruster
@ 2025-11-26 15:14     ` Cédric Le Goater
  2025-11-26 16:18       ` Peter Xu
  0 siblings, 1 reply; 24+ messages in thread
From: Cédric Le Goater @ 2025-11-26 15:14 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel
  Cc: jmarcin, peterx, marcandre.lureau, farosas, berrange, vsementsov,
	mail, peter.maydell

On 11/26/25 15:34, Markus Armbruster wrote:
> Suggested-by: Peter Maydell <peter.maydell@linaro.org>
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>   include/qapi/error.h | 17 +++++++++++++++++
>   1 file changed, 17 insertions(+)
> 
> diff --git a/include/qapi/error.h b/include/qapi/error.h
> index f3ce4a4a2d..fc018b4c59 100644
> --- a/include/qapi/error.h
> +++ b/include/qapi/error.h
> @@ -437,6 +437,23 @@ Error *error_copy(const Error *err);
>    */
>   void error_free(Error *err);
>   
> +/*
> + * Note: we intentionally do not enable g_autoptr(Error) with
> + * G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(Error, error_free).
> + *
> + * Functions that report or propagate an error take ownership of the
> + * Error object.  Explicit error_free() is needed when you handle an
> + * error in some other way.  This is rare.
> + *
> + * g_autoptr(Error) would call error_free() automatically on return.
> + * To avoid a double-free, we'd have to manually clear the pointer
> + * every time we propagate or report.
> + *
> + * Thus, g_autoptr(Error) would make the rare case easier to get right
> + * (less prone to leaks), and the common case easier to get wrong
> + * (more prone to double-free).
> + */
> +
>   /*
>    * Convenience function to assert that *@errp is set, then silently free it.
>    */


Reviewed-by: Cédric Le Goater <clg@redhat.com>

Thanks,

C.




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

* Re: [PATCH 2.5/6] error: Explain why we don't g_autoptr(Error)
  2025-11-26 15:14     ` Cédric Le Goater
@ 2025-11-26 16:18       ` Peter Xu
  2025-11-27  7:01         ` Markus Armbruster
  0 siblings, 1 reply; 24+ messages in thread
From: Peter Xu @ 2025-11-26 16:18 UTC (permalink / raw)
  To: Cédric Le Goater
  Cc: Markus Armbruster, qemu-devel, jmarcin, marcandre.lureau, farosas,
	berrange, vsementsov, mail, peter.maydell

On Wed, Nov 26, 2025 at 04:14:55PM +0100, Cédric Le Goater wrote:
> On 11/26/25 15:34, Markus Armbruster wrote:
> > Suggested-by: Peter Maydell <peter.maydell@linaro.org>
> > Signed-off-by: Markus Armbruster <armbru@redhat.com>
> > ---
> >   include/qapi/error.h | 17 +++++++++++++++++
> >   1 file changed, 17 insertions(+)
> > 
> > diff --git a/include/qapi/error.h b/include/qapi/error.h
> > index f3ce4a4a2d..fc018b4c59 100644
> > --- a/include/qapi/error.h
> > +++ b/include/qapi/error.h
> > @@ -437,6 +437,23 @@ Error *error_copy(const Error *err);
> >    */
> >   void error_free(Error *err);
> > +/*
> > + * Note: we intentionally do not enable g_autoptr(Error) with
> > + * G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(Error, error_free).
> > + *
> > + * Functions that report or propagate an error take ownership of the
> > + * Error object.  Explicit error_free() is needed when you handle an
> > + * error in some other way.  This is rare.
> > + *
> > + * g_autoptr(Error) would call error_free() automatically on return.
> > + * To avoid a double-free, we'd have to manually clear the pointer
> > + * every time we propagate or report.
> > + *
> > + * Thus, g_autoptr(Error) would make the rare case easier to get right
> > + * (less prone to leaks), and the common case easier to get wrong
> > + * (more prone to double-free).

How about we further poison the auto free altogether?

IIUC this should work:

+extern void
+__attribute__((error("Error should not be used with g_autoptr")))
+error_free_poisoned(Error *err);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(Error, error_free_poisoned)

> > + */
> > +
> >   /*
> >    * Convenience function to assert that *@errp is set, then silently free it.
> >    */
> 
> 
> Reviewed-by: Cédric Le Goater <clg@redhat.com>
> 
> Thanks,
> 
> C.
> 
> 

-- 
Peter Xu



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

* Re: [PATCH for-11.0 2/6] Revert "error: define g_autoptr() cleanup function for the Error type"
  2025-11-25 20:46 ` [PATCH for-11.0 2/6] Revert "error: define g_autoptr() cleanup function for the Error type" Peter Xu
                     ` (2 preceding siblings ...)
  2025-11-26 14:34   ` [PATCH 2.5/6] error: Explain why we don't g_autoptr(Error) Markus Armbruster
@ 2025-11-26 20:21   ` Maciej S. Szmigiero
  2025-11-27  7:10   ` [PATCH 2.5/6] error: Poison g_autoptr(Error) to prevent its use Markus Armbruster
  4 siblings, 0 replies; 24+ messages in thread
From: Maciej S. Szmigiero @ 2025-11-26 20:21 UTC (permalink / raw)
  To: Peter Xu
  Cc: Markus Armbruster, qemu-devel, Juraj Marcin,
	Marc-André Lureau, Fabiano Rosas, Daniel P . Berrangé,
	Vladimir Sementsov-Ogievskiy, Cédric Le Goater

On 25.11.2025 21:46, Peter Xu wrote:
> This reverts commit 18eb55546a54e443d94a4c49286348176ad4b00a.  Discussion
> can be seen at:
> 
> https://lore.kernel.org/r/aSWSLMi6ZhTCS_p2@redhat.com
> 
> Cc: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
> Cc: Cédric Le Goater <clg@redhat.com>
> Signed-off-by: Peter Xu <peterx@redhat.com>
> ---
>   include/qapi/error.h | 2 --
>   1 file changed, 2 deletions(-)
> 
> diff --git a/include/qapi/error.h b/include/qapi/error.h
> index b16c6303f8..f3ce4a4a2d 100644
> --- a/include/qapi/error.h
> +++ b/include/qapi/error.h
> @@ -437,8 +437,6 @@ Error *error_copy(const Error *err);
>    */
>   void error_free(Error *err);
>   
> -G_DEFINE_AUTOPTR_CLEANUP_FUNC(Error, error_free)
> -
>   /*
>    * Convenience function to assert that *@errp is set, then silently free it.
>    */

Acked-by: Maciej S. Szmigiero <maciej.szmigiero@oracle.com>

Thanks,
Maciej



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

* Re: [PATCH 2.5/6] error: Explain why we don't g_autoptr(Error)
  2025-11-26 16:18       ` Peter Xu
@ 2025-11-27  7:01         ` Markus Armbruster
  0 siblings, 0 replies; 24+ messages in thread
From: Markus Armbruster @ 2025-11-27  7:01 UTC (permalink / raw)
  To: Peter Xu
  Cc: Cédric Le Goater, qemu-devel, jmarcin, marcandre.lureau,
	farosas, berrange, vsementsov, mail, peter.maydell

Peter Xu <peterx@redhat.com> writes:

> On Wed, Nov 26, 2025 at 04:14:55PM +0100, Cédric Le Goater wrote:
>> On 11/26/25 15:34, Markus Armbruster wrote:
>> > Suggested-by: Peter Maydell <peter.maydell@linaro.org>
>> > Signed-off-by: Markus Armbruster <armbru@redhat.com>
>> > ---
>> >   include/qapi/error.h | 17 +++++++++++++++++
>> >   1 file changed, 17 insertions(+)
>> > 
>> > diff --git a/include/qapi/error.h b/include/qapi/error.h
>> > index f3ce4a4a2d..fc018b4c59 100644
>> > --- a/include/qapi/error.h
>> > +++ b/include/qapi/error.h
>> > @@ -437,6 +437,23 @@ Error *error_copy(const Error *err);
>> >    */
>> >   void error_free(Error *err);
>> > +/*
>> > + * Note: we intentionally do not enable g_autoptr(Error) with
>> > + * G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(Error, error_free).
>> > + *
>> > + * Functions that report or propagate an error take ownership of the
>> > + * Error object.  Explicit error_free() is needed when you handle an
>> > + * error in some other way.  This is rare.
>> > + *
>> > + * g_autoptr(Error) would call error_free() automatically on return.
>> > + * To avoid a double-free, we'd have to manually clear the pointer
>> > + * every time we propagate or report.
>> > + *
>> > + * Thus, g_autoptr(Error) would make the rare case easier to get right
>> > + * (less prone to leaks), and the common case easier to get wrong
>> > + * (more prone to double-free).
>
> How about we further poison the auto free altogether?
>
> IIUC this should work:
>
> +extern void
> +__attribute__((error("Error should not be used with g_autoptr")))
> +error_free_poisoned(Error *err);
> +
> +G_DEFINE_AUTOPTR_CLEANUP_FUNC(Error, error_free_poisoned)

Cute.  Why not.  I'll post a new patch.



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

* [PATCH 2.5/6] error: Poison g_autoptr(Error) to prevent its use
  2025-11-25 20:46 ` [PATCH for-11.0 2/6] Revert "error: define g_autoptr() cleanup function for the Error type" Peter Xu
                     ` (3 preceding siblings ...)
  2025-11-26 20:21   ` [PATCH for-11.0 2/6] Revert "error: define g_autoptr() cleanup function for the Error type" Maciej S. Szmigiero
@ 2025-11-27  7:10   ` Markus Armbruster
  2025-11-27 15:34     ` Cédric Le Goater
  4 siblings, 1 reply; 24+ messages in thread
From: Markus Armbruster @ 2025-11-27  7:10 UTC (permalink / raw)
  To: qemu-devel
  Cc: jmarcin, peterx, marcandre.lureau, farosas, berrange, vsementsov,
	mail, clg, peter.maydell

The previous commit reverted support for g_autoptr(Error).  This one
should stop it from coming back.

Suggested-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 include/qapi/error.h | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/include/qapi/error.h b/include/qapi/error.h
index f3ce4a4a2d..2356b84bb3 100644
--- a/include/qapi/error.h
+++ b/include/qapi/error.h
@@ -437,6 +437,26 @@ Error *error_copy(const Error *err);
  */
 void error_free(Error *err);
 
+/*
+ * Poison g_autoptr(Error) to prevent its use.
+ *
+ * Functions that report or propagate an error take ownership of the
+ * Error object.  Explicit error_free() is needed when you handle an
+ * error in some other way.  This is rare.
+ *
+ * g_autoptr(Error) would call error_free() automatically on return.
+ * To avoid a double-free, we'd have to manually clear the pointer
+ * every time we propagate or report.
+ *
+ * Thus, g_autoptr(Error) would make the rare case easier to get right
+ * (less prone to leaks), and the common case easier to get wrong
+ * (more prone to double-free).
+ */
+extern void
+__attribute__((error("Do not use g_autoptr() to declare Error * variables")))
+error_free_poisoned(Error *err);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(Error, error_free_poisoned)
+
 /*
  * Convenience function to assert that *@errp is set, then silently free it.
  */
-- 
2.49.0




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

* Re: [PATCH 2.5/6] error: Poison g_autoptr(Error) to prevent its use
  2025-11-27  7:10   ` [PATCH 2.5/6] error: Poison g_autoptr(Error) to prevent its use Markus Armbruster
@ 2025-11-27 15:34     ` Cédric Le Goater
  0 siblings, 0 replies; 24+ messages in thread
From: Cédric Le Goater @ 2025-11-27 15:34 UTC (permalink / raw)
  To: Markus Armbruster, qemu-devel
  Cc: jmarcin, peterx, marcandre.lureau, farosas, berrange, vsementsov,
	mail, peter.maydell

On 11/27/25 08:10, Markus Armbruster wrote:
> The previous commit reverted support for g_autoptr(Error).  This one
> should stop it from coming back.
> 
> Suggested-by: Peter Maydell <peter.maydell@linaro.org>
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
> ---
>   include/qapi/error.h | 20 ++++++++++++++++++++
>   1 file changed, 20 insertions(+)
> 
> diff --git a/include/qapi/error.h b/include/qapi/error.h
> index f3ce4a4a2d..2356b84bb3 100644
> --- a/include/qapi/error.h
> +++ b/include/qapi/error.h
> @@ -437,6 +437,26 @@ Error *error_copy(const Error *err);
>    */
>   void error_free(Error *err);
>   
> +/*
> + * Poison g_autoptr(Error) to prevent its use.
> + *
> + * Functions that report or propagate an error take ownership of the
> + * Error object.  Explicit error_free() is needed when you handle an
> + * error in some other way.  This is rare.
> + *
> + * g_autoptr(Error) would call error_free() automatically on return.
> + * To avoid a double-free, we'd have to manually clear the pointer
> + * every time we propagate or report.
> + *
> + * Thus, g_autoptr(Error) would make the rare case easier to get right
> + * (less prone to leaks), and the common case easier to get wrong
> + * (more prone to double-free).
> + */
> +extern void
> +__attribute__((error("Do not use g_autoptr() to declare Error * variables")))
> +error_free_poisoned(Error *err);
> +G_DEFINE_AUTOPTR_CLEANUP_FUNC(Error, error_free_poisoned)
> +
>   /*
>    * Convenience function to assert that *@errp is set, then silently free it.
>    */

broken build, even better.

Tested-by: Cédric Le Goater <clg@redhat.com>
Reviewed-by: Cédric Le Goater <clg@redhat.com>

Thanks,

C.




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

* Re: [PATCH for-11.0 3/6] migration: Make migration_connect_set_error() own the error
  2025-11-26  7:27   ` Markus Armbruster
@ 2025-11-28 16:58     ` Peter Xu
  0 siblings, 0 replies; 24+ messages in thread
From: Peter Xu @ 2025-11-28 16:58 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: qemu-devel, Juraj Marcin, Marc-André Lureau, Fabiano Rosas,
	Daniel P . Berrangé, Vladimir Sementsov-Ogievskiy

On Wed, Nov 26, 2025 at 08:27:48AM +0100, Markus Armbruster wrote:
> Peter Xu <peterx@redhat.com> writes:
> 
> > Make migration_connect_set_error() take ownership of the error always.
> > Paving way for making migrate_set_error() to take ownership.
> >
> > Signed-off-by: Peter Xu <peterx@redhat.com>
> > ---
> >  migration/channel.c   | 1 -
> >  migration/migration.c | 7 ++++---
> >  2 files changed, 4 insertions(+), 4 deletions(-)
> >
> > diff --git a/migration/channel.c b/migration/channel.c
> > index 462cc183e1..92435fa7f7 100644
> > --- a/migration/channel.c
> > +++ b/migration/channel.c
> > @@ -95,7 +95,6 @@ void migration_channel_connect(MigrationState *s,
> >          }
> >      }
> >      migration_connect(s, error);
> > -    error_free(error);
> >  }
> >  
> >  
> > diff --git a/migration/migration.c b/migration/migration.c
> > index b316ee01ab..4fe69cc2ef 100644
> > --- a/migration/migration.c
> > +++ b/migration/migration.c
> > @@ -1575,7 +1575,7 @@ static void migrate_error_free(MigrationState *s)
> >      }
> >  }
> >  
> > -static void migration_connect_set_error(MigrationState *s, const Error *error)
> > +static void migration_connect_set_error(MigrationState *s, Error *error)
> 
> Recommend to rename for the same reason you rename migrate_set_error()
> in the last patch.
> 
> Bonus: all calls become visible in the patch, easing review.

Makes sense, will do.

> 
> >  {
> >      MigrationStatus current = s->state;
> >      MigrationStatus next;
> > @@ -1602,6 +1602,7 @@ static void migration_connect_set_error(MigrationState *s, const Error *error)
> >  
> >      migrate_set_state(&s->state, current, next);
> >      migrate_set_error(s, error);
> > +    error_free(error);
> >  }
> >  
> >  void migration_cancel(void)
> > @@ -2292,7 +2293,7 @@ void qmp_migrate(const char *uri, bool has_channels,
> >  
> >  out:
> >      if (local_err) {
> > -        migration_connect_set_error(s, local_err);
> > +        migration_connect_set_error(s, error_copy(local_err));
> >          error_propagate(errp, local_err);
> >      }
> >  }
> > @@ -2337,7 +2338,7 @@ static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested,
> >          if (!resume_requested) {
> >              yank_unregister_instance(MIGRATION_YANK_INSTANCE);
> >          }
> > -        migration_connect_set_error(s, local_err);
> > +        migration_connect_set_error(s, error_copy(local_err));
> >          error_propagate(errp, local_err);
> >          return;
> >      }
> 
> There's one more call in migration_connect().  Doesn't it need an
> error_copy() now?  Oh, I see: it doesn't, because its only caller
> migration_channel_connect() loses its error_free() in the first hunk.
> 
> So, migration_connect() *also* takes ownership now.  The commit message
> should cover this.

Will add a note.

> 
> Aside: I'd be tempted to lift the if (error_in) code from
> migration_connect() to migration_channel_connect() and drop the
> @error_in parameter.

It was deliberately introduced in:

commit 688a3dcba980bf01344a1ae2bc37fea44c6014ac
Author: Dr. David Alan Gilbert <dgilbert@redhat.com>
Date:   Fri Dec 15 17:16:55 2017 +0000

    migration: Route errors down through migration_channel_connect

That change will need some justification and care taken on its own when
changing back to before.  I plan to stick with the current goal of this
series so far (which is to remove autoptr for Error and also make the main
migration error API to follow Error's) to land this first.

Thanks!

-- 
Peter Xu



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

* Re: [PATCH for-11.0 6/6] migration: Replace migrate_set_error() with migrate_error_propagate()
  2025-11-26  7:57   ` Markus Armbruster
@ 2025-12-01 17:32     ` Peter Xu
  0 siblings, 0 replies; 24+ messages in thread
From: Peter Xu @ 2025-12-01 17:32 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: qemu-devel, Juraj Marcin, Marc-André Lureau, Fabiano Rosas,
	Daniel P . Berrangé, Vladimir Sementsov-Ogievskiy

On Wed, Nov 26, 2025 at 08:57:43AM +0100, Markus Armbruster wrote:
> Peter Xu <peterx@redhat.com> writes:
> 
> > migrate_set_error() currently doesn't take ownership of the error being
> > passed in.  It's not aligned with the error API and meanwhile it also
> > makes most of the caller free the error explicitly.
> >
> > Change the API to take the ownership of the Error object instead.  When at
> > it, remove the first parameter so it's friendly to g_clear_pointer().  It
> > can be used whenever the caller wants to provide extra safety measure (or
> > reuse the pointer) to reset the Error* pointer after stolen.
> >
> > Signed-off-by: Peter Xu <peterx@redhat.com>
> 
> Worth mentioning that this avoids Error object copies?

Sure.

> 
> > ---
> >  migration/migration.h            |  2 +-
> >  migration/cpr-exec.c             |  4 +--
> >  migration/migration.c            | 46 +++++++++++++++-----------------
> >  migration/multifd-device-state.c |  5 +---
> >  migration/multifd.c              | 19 +++++++------
> >  migration/postcopy-ram.c         |  5 ++--
> >  migration/ram.c                  |  4 +--
> >  migration/savevm.c               | 15 ++++-------
> >  8 files changed, 42 insertions(+), 58 deletions(-)
> >
> > diff --git a/migration/migration.h b/migration/migration.h
> > index 213b33fe6e..df74f9b14f 100644
> > --- a/migration/migration.h
> > +++ b/migration/migration.h
> > @@ -525,7 +525,7 @@ void migration_incoming_process(void);
> >  
> >  bool  migration_has_all_channels(void);
> >  
> > -void migrate_set_error(MigrationState *s, const Error *error);
> > +void migrate_error_propagate(Error *error);
> >  bool migrate_has_error(MigrationState *s);
> >  
> >  void migration_connect(MigrationState *s, Error *error_in);
> > diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c
> > index 0b8344a86f..13e6138f56 100644
> > --- a/migration/cpr-exec.c
> > +++ b/migration/cpr-exec.c
> > @@ -158,9 +158,7 @@ static void cpr_exec_cb(void *opaque)
> >  
> >      error_report_err(error_copy(err));
> >      migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
> > -    migrate_set_error(s, err);
> > -    error_free(err);
> > -    err = NULL;
> > +    g_clear_pointer(&err, migrate_error_propagate);
> >  
> >      /* Note, we can go from state COMPLETED to FAILED */
> >      migration_call_notifiers(s, MIG_EVENT_PRECOPY_FAILED, NULL);
> 
> I dislike g_clear_pointer(), and I dislike the change from taking the
> migration state as argument to getting the global migration state with
> migrate_get_current().  The loss of similarity to error_propagate() is
> unfortunate, but tolerable.  This is not a demand.
> 
> For each hunk, we need to prove that the migrate_set_error()'s first
> argument before the patch equals migrate_get_current() afterwards.

It's guaranteed; the only point of set_error() is to persist the error to
the global MigrationState's error field that which will be queried later.

> 
> For this hunk, it's locally obvious: @s is initialized to
> migrate_get_current() at the beginning of the funtion.
> 
> Where it's not locally obvious, I guess we could argue that just one
> MigrationState object exists, so a MigrationState * can only point to
> that one.
> 
> If locally non-obvious hunks exist, such an argument needs to be made in
> the commit message.
> 
> I did *not* check this aspect of the patch.

Personally I liked the safety measure that g_clear_pointer() enforces, on
pointer reset together with object release.  migrate_error_propagate() is
almost a free function to me, except that it optionally remembers the error
if it's the first one for migration purpose.

But since you don't like it, and Cedric also similarly shared his opinion,
I can keep the MigrationState* arg, and remove this g_clear_pointer() for
now.

Maybe I'll try to "fight" more if we have more use cases of explicit
resetting Error* pointer for reuse like what cpr_exec_cb() does.  But that
seems the only use case anyway..  I think we can keep it open-coded.

> 
> > diff --git a/migration/migration.c b/migration/migration.c
> > index 4fe69cc2ef..219d3129cb 100644
> > --- a/migration/migration.c
> > +++ b/migration/migration.c
> > @@ -914,9 +914,7 @@ process_incoming_migration_co(void *opaque)
> >  fail:
> >      migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
> >                        MIGRATION_STATUS_FAILED);
> > -    migrate_set_error(s, local_err);
> > -    error_free(local_err);
> > -
> > +    migrate_error_propagate(local_err);
> >      migration_incoming_state_destroy();
> >  
> >      if (mis->exit_on_error) {
> > @@ -1548,14 +1546,22 @@ static void migration_cleanup_bh(void *opaque)
> >      migration_cleanup(opaque);
> >  }
> >  
> > -void migrate_set_error(MigrationState *s, const Error *error)
> > +/*
> > + * Propagate the Error* object to migration core.  The caller mustn't
> > + * reference the error pointer after the function returned, because the
> > + * Error* object might be freed.
> > + */
> > +void migrate_error_propagate(Error *error)
> >  {
> > -    QEMU_LOCK_GUARD(&s->error_mutex);
> > +    MigrationState *s = migrate_get_current();
> >  
> > +    QEMU_LOCK_GUARD(&s->error_mutex);
> >      trace_migrate_error(error_get_pretty(error));
> >  
> >      if (!s->error) {
> > -        s->error = error_copy(error);
> > +        s->error = error;
> > +    } else {
> > +        error_free(error);
> >      }
> >  }
> >  
> > @@ -1601,8 +1607,7 @@ static void migration_connect_set_error(MigrationState *s, Error *error)
> >      }
> >  
> >      migrate_set_state(&s->state, current, next);
> > -    migrate_set_error(s, error);
> > -    error_free(error);
> > +    migrate_error_propagate(error);
> >  }
> >  
> >  void migration_cancel(void)
> > @@ -2014,8 +2019,7 @@ void qmp_migrate_pause(Error **errp)
> >  
> >          /* Tell the core migration that we're pausing */
> >          error_setg(&error, "Postcopy migration is paused by the user");
> > -        migrate_set_error(ms, error);
> > -        error_free(error);
> > +        migrate_error_propagate(error);
> >  
> >          qemu_mutex_lock(&ms->qemu_file_lock);
> >          if (ms->to_dst_file) {
> > @@ -2647,8 +2651,7 @@ static void *source_return_path_thread(void *opaque)
> >  
> >  out:
> >      if (err) {
> > -        migrate_set_error(ms, err);
> > -        error_free(err);
> > +        migrate_error_propagate(err);
> >          trace_source_return_path_thread_bad_end();
> >      }
> >  
> > @@ -3094,12 +3097,10 @@ static void migration_completion(MigrationState *s)
> >  
> >  fail:
> >      if (qemu_file_get_error_obj(s->to_dst_file, &local_err)) {
> > -        migrate_set_error(s, local_err);
> > -        error_free(local_err);
> > +        migrate_error_propagate(local_err);
> >      } else if (ret) {
> >          error_setg_errno(&local_err, -ret, "Error in migration completion");
> > -        migrate_set_error(s, local_err);
> > -        error_free(local_err);
> > +        migrate_error_propagate(local_err);
> >      }
> >  
> >      if (s->state != MIGRATION_STATUS_CANCELLING) {
> > @@ -3326,8 +3327,7 @@ static MigThrError migration_detect_error(MigrationState *s)
> >      }
> >  
> >      if (local_error) {
> > -        migrate_set_error(s, local_error);
> > -        error_free(local_error);
> > +        migrate_error_propagate(local_error);
> >      }
> >  
> >      if (state == MIGRATION_STATUS_POSTCOPY_ACTIVE && ret) {
> > @@ -3522,7 +3522,7 @@ static MigIterateState migration_iteration_run(MigrationState *s)
> >          if (must_precopy <= s->threshold_size &&
> >              can_switchover && qatomic_read(&s->start_postcopy)) {
> >              if (postcopy_start(s, &local_err)) {
> > -                migrate_set_error(s, local_err);
> > +                migrate_error_propagate(error_copy(local_err));
> 
> First hunk where we don't save a copy.  Reason: we report in addition to
> propagate.  There are a several more below.
> 
> "Forking" the error like this gives me an uneasy feeling, as explained
> in
>     Subject: Re: [PATCH 0/3] migration: Error fixes and improvements
>     Date: Tue, 18 Nov 2025 08:44:32 +0100
>     Message-ID: <87a50jlr8f.fsf@pond.sub.org>
> 
> Clearly out of scope for this series.

Yes, and we discussed the need of doing that in the other thread as well.
Hope that justifies we should keep it until we want to change the behavior
of error reports.

Thanks,

> 
> >                  error_report_err(local_err);
> >              }
> >              return MIG_ITERATE_SKIP;
> > @@ -3819,8 +3819,7 @@ static void *migration_thread(void *opaque)
> >       * devices to unplug. This to preserve migration state transitions.
> >       */
> >      if (ret) {
> > -        migrate_set_error(s, local_err);
> > -        error_free(local_err);
> > +        migrate_error_propagate(local_err);
> >          migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE,
> >                            MIGRATION_STATUS_FAILED);
> >          goto out;
> > @@ -3944,8 +3943,7 @@ static void *bg_migration_thread(void *opaque)
> >       * devices to unplug. This to preserve migration state transitions.
> >       */
> >      if (ret) {
> > -        migrate_set_error(s, local_err);
> > -        error_free(local_err);
> > +        migrate_error_propagate(local_err);
> >          migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE,
> >                            MIGRATION_STATUS_FAILED);
> >          goto fail_setup;
> > @@ -4127,7 +4125,7 @@ void migration_connect(MigrationState *s, Error *error_in)
> >      return;
> >  
> >  fail:
> > -    migrate_set_error(s, local_err);
> > +    migrate_error_propagate(error_copy(local_err));
> >      if (s->state != MIGRATION_STATUS_CANCELLING) {
> >          migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED);
> >      }
> > diff --git a/migration/multifd-device-state.c b/migration/multifd-device-state.c
> > index db3239fef5..3040d70872 100644
> > --- a/migration/multifd-device-state.c
> > +++ b/migration/multifd-device-state.c
> > @@ -143,8 +143,6 @@ static int multifd_device_state_save_thread(void *opaque)
> >      Error *local_err = NULL;
> >  
> >      if (!data->hdlr(data, &local_err)) {
> > -        MigrationState *s = migrate_get_current();
> > -
> >          /*
> >           * Can't call abort_device_state_save_threads() here since new
> >           * save threads could still be in process of being launched
> > @@ -158,8 +156,7 @@ static int multifd_device_state_save_thread(void *opaque)
> >           * In case of multiple save threads failing which thread error
> >           * return we end setting is purely arbitrary.
> >           */
> > -        migrate_set_error(s, local_err);
> > -        error_free(local_err);
> > +        migrate_error_propagate(local_err);
> >      }
> >  
> >      return 0;
> > diff --git a/migration/multifd.c b/migration/multifd.c
> > index c861b4b557..99717b64e9 100644
> > --- a/migration/multifd.c
> > +++ b/migration/multifd.c
> > @@ -428,8 +428,9 @@ static void multifd_send_set_error(Error *err)
> >  
> >      if (err) {
> >          MigrationState *s = migrate_get_current();
> > -        migrate_set_error(s, err);
> > -        error_free(err);
> > +
> > +        migrate_error_propagate(err);
> > +
> >          if (s->state == MIGRATION_STATUS_SETUP ||
> >              s->state == MIGRATION_STATUS_PRE_SWITCHOVER ||
> >              s->state == MIGRATION_STATUS_DEVICE ||
> > @@ -588,8 +589,7 @@ void multifd_send_shutdown(void)
> >          Error *local_err = NULL;
> >  
> >          if (!multifd_send_cleanup_channel(p, &local_err)) {
> > -            migrate_set_error(migrate_get_current(), local_err);
> > -            error_free(local_err);
> > +            migrate_error_propagate(local_err);
> >          }
> >      }
> >  
> > @@ -962,8 +962,7 @@ bool multifd_send_setup(void)
> >          p->write_flags = 0;
> >  
> >          if (!multifd_new_send_channel_create(p, &local_err)) {
> > -            migrate_set_error(s, local_err);
> > -            error_free(local_err);
> > +            migrate_error_propagate(local_err);
> >              ret = -1;
> >          }
> >      }
> > @@ -987,8 +986,7 @@ bool multifd_send_setup(void)
> >  
> >          ret = multifd_send_state->ops->send_setup(p, &local_err);
> >          if (ret) {
> > -            migrate_set_error(s, local_err);
> > -            error_free(local_err);
> > +            migrate_error_propagate(local_err);
> >              goto err;
> >          }
> >          assert(p->iov);
> > @@ -1067,8 +1065,9 @@ static void multifd_recv_terminate_threads(Error *err)
> >  
> >      if (err) {
> >          MigrationState *s = migrate_get_current();
> > -        migrate_set_error(s, err);
> > -        error_free(err);
> > +
> > +        migrate_error_propagate(err);
> > +
> >          if (s->state == MIGRATION_STATUS_SETUP ||
> >              s->state == MIGRATION_STATUS_ACTIVE) {
> >              migrate_set_state(&s->state, s->state,
> > diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
> > index 7c9fe61041..856366072a 100644
> > --- a/migration/postcopy-ram.c
> > +++ b/migration/postcopy-ram.c
> > @@ -1927,8 +1927,7 @@ postcopy_preempt_send_channel_done(MigrationState *s,
> >                                     QIOChannel *ioc, Error *local_err)
> >  {
> >      if (local_err) {
> > -        migrate_set_error(s, local_err);
> > -        error_free(local_err);
> > +        migrate_error_propagate(local_err);
> >      } else {
> >          migration_ioc_register_yank(ioc);
> >          s->postcopy_qemufile_src = qemu_file_new_output(ioc);
> > @@ -2162,7 +2161,7 @@ static void *postcopy_listen_thread(void *opaque)
> >               * exit depending on if postcopy-exit-on-error is true, but the
> >               * migration cannot be recovered.
> >               */
> > -            migrate_set_error(migr, local_err);
> > +            migrate_error_propagate(error_copy(local_err));
> >              error_report_err(local_err);
> >              migrate_set_state(&mis->state, mis->state, MIGRATION_STATUS_FAILED);
> >              goto out;
> > diff --git a/migration/ram.c b/migration/ram.c
> > index 29f016cb25..1d2a47526e 100644
> > --- a/migration/ram.c
> > +++ b/migration/ram.c
> > @@ -4730,9 +4730,7 @@ static void ram_mig_ram_block_resized(RAMBlockNotifier *n, void *host,
> >           * Abort and indicate a proper reason.
> >           */
> >          error_setg(&err, "RAM block '%s' resized during precopy.", rb->idstr);
> > -        migrate_set_error(migrate_get_current(), err);
> > -        error_free(err);
> > -
> > +        migrate_error_propagate(err);
> >          migration_cancel();
> >      }
> >  
> > diff --git a/migration/savevm.c b/migration/savevm.c
> > index 638e9b364f..ab9d1e579e 100644
> > --- a/migration/savevm.c
> > +++ b/migration/savevm.c
> > @@ -1125,13 +1125,12 @@ void qemu_savevm_send_open_return_path(QEMUFile *f)
> >  int qemu_savevm_send_packaged(QEMUFile *f, const uint8_t *buf, size_t len)
> >  {
> >      uint32_t tmp;
> > -    MigrationState *ms = migrate_get_current();
> >      Error *local_err = NULL;
> >  
> >      if (len > MAX_VM_CMD_PACKAGED_SIZE) {
> >          error_setg(&local_err, "%s: Unreasonably large packaged state: %zu",
> >                       __func__, len);
> > -        migrate_set_error(ms, local_err);
> > +        migrate_error_propagate(error_copy(local_err));
> >          error_report_err(local_err);
> >          return -1;
> >      }
> > @@ -1373,7 +1372,7 @@ int qemu_savevm_state_setup(QEMUFile *f, Error **errp)
> >          if (se->vmsd && se->vmsd->early_setup) {
> >              ret = vmstate_save(f, se, vmdesc, errp);
> >              if (ret) {
> > -                migrate_set_error(ms, *errp);
> > +                migrate_error_propagate(error_copy(*errp));
> 
> Here, we need to keep the copy because we additionally propagate to the
> caller.
> 
> >                  qemu_file_set_error(f, ret);
> >                  break;
> >              }
> 
> [...]
> 

-- 
Peter Xu



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

end of thread, other threads:[~2025-12-01 17:33 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-25 20:46 [PATCH for-11.0 0/6] migration: Error reporting cleanups Peter Xu
2025-11-25 20:46 ` [PATCH for-11.0 1/6] migration: Use explicit error_free() instead of g_autoptr Peter Xu
2025-11-26  6:43   ` Markus Armbruster
2025-11-25 20:46 ` [PATCH for-11.0 2/6] Revert "error: define g_autoptr() cleanup function for the Error type" Peter Xu
2025-11-26  6:45   ` Markus Armbruster
2025-11-26  7:43   ` Cédric Le Goater
2025-11-26  8:08     ` Markus Armbruster
2025-11-26 14:34   ` [PATCH 2.5/6] error: Explain why we don't g_autoptr(Error) Markus Armbruster
2025-11-26 15:14     ` Cédric Le Goater
2025-11-26 16:18       ` Peter Xu
2025-11-27  7:01         ` Markus Armbruster
2025-11-26 20:21   ` [PATCH for-11.0 2/6] Revert "error: define g_autoptr() cleanup function for the Error type" Maciej S. Szmigiero
2025-11-27  7:10   ` [PATCH 2.5/6] error: Poison g_autoptr(Error) to prevent its use Markus Armbruster
2025-11-27 15:34     ` Cédric Le Goater
2025-11-25 20:46 ` [PATCH for-11.0 3/6] migration: Make migration_connect_set_error() own the error Peter Xu
2025-11-26  7:27   ` Markus Armbruster
2025-11-28 16:58     ` Peter Xu
2025-11-25 20:46 ` [PATCH for-11.0 4/6] migration: Make multifd_send_set_error() " Peter Xu
2025-11-26  7:30   ` Markus Armbruster
2025-11-26  7:32     ` Markus Armbruster
2025-11-25 20:46 ` [PATCH for-11.0 5/6] migration: Make multifd_recv_terminate_threads() " Peter Xu
2025-11-25 20:46 ` [PATCH for-11.0 6/6] migration: Replace migrate_set_error() with migrate_error_propagate() Peter Xu
2025-11-26  7:57   ` Markus Armbruster
2025-12-01 17:32     ` Peter Xu

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).