From: Peter Xu <peterx@redhat.com>
To: qemu-devel@nongnu.org
Cc: Fabiano Rosas <farosas@suse.de>,
David Hildenbrand <david@redhat.com>,
peterx@redhat.com, Paolo Bonzini <pbonzini@redhat.com>,
Juraj Marcin <jmarcin@redhat.com>
Subject: [PULL 33/36] migration: Refactor all incoming cleanup info migration_incoming_destroy()
Date: Mon, 3 Nov 2025 16:06:22 -0500 [thread overview]
Message-ID: <20251103210625.3689448-34-peterx@redhat.com> (raw)
In-Reply-To: <20251103210625.3689448-1-peterx@redhat.com>
From: Juraj Marcin <jmarcin@redhat.com>
Currently, there are two functions that are responsible for calling the
cleanup of the incoming migration state. With successful precopy, it's
the incoming migration coroutine, and with successful postcopy it's the
postcopy listen thread. However, if postcopy fails during in the device
load, both functions will try to do the cleanup.
This patch refactors all cleanup that needs to be done on the incoming
side into a common function and defines a clear boundary, who is
responsible for the cleanup. The incoming migration coroutine is
responsible for calling the cleanup function, unless the listen thread
has been started, in which case the postcopy listen thread runs the
incoming migration cleanup in its BH.
Signed-off-by: Juraj Marcin <jmarcin@redhat.com>
Fixes: 9535435795 ("migration: push Error **errp into qemu_loadvm_state()")
Reviewed-by: Peter Xu <peterx@redhat.com>
Link: https://lore.kernel.org/r/20251103183301.3840862-6-jmarcin@redhat.com
Signed-off-by: Peter Xu <peterx@redhat.com>
---
migration/migration.h | 1 +
migration/migration.c | 44 +++++++++-------------------
migration/postcopy-ram.c | 63 +++++++++++++++++++++-------------------
migration/trace-events | 2 +-
4 files changed, 49 insertions(+), 61 deletions(-)
diff --git a/migration/migration.h b/migration/migration.h
index 01329bf824..4a37f7202c 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -254,6 +254,7 @@ struct MigrationIncomingState {
MigrationIncomingState *migration_incoming_get_current(void);
void migration_incoming_state_destroy(void);
void migration_incoming_transport_cleanup(MigrationIncomingState *mis);
+void migration_incoming_qemu_exit(void);
/*
* Functions to work with blocktime context
*/
diff --git a/migration/migration.c b/migration/migration.c
index 9a367f717e..637be71bfe 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -438,10 +438,15 @@ void migration_incoming_transport_cleanup(MigrationIncomingState *mis)
void migration_incoming_state_destroy(void)
{
- struct MigrationIncomingState *mis = migration_incoming_get_current();
+ MigrationIncomingState *mis = migration_incoming_get_current();
+ PostcopyState ps = postcopy_state_get();
multifd_recv_cleanup();
+ if (ps != POSTCOPY_INCOMING_NONE) {
+ postcopy_incoming_cleanup(mis);
+ }
+
/*
* RAM state cleanup needs to happen after multifd cleanup, because
* multifd threads can use some of its states (receivedmap).
@@ -866,7 +871,6 @@ process_incoming_migration_co(void *opaque)
{
MigrationState *s = migrate_get_current();
MigrationIncomingState *mis = migration_incoming_get_current();
- PostcopyState ps;
int ret;
Error *local_err = NULL;
@@ -883,25 +887,14 @@ process_incoming_migration_co(void *opaque)
trace_vmstate_downtime_checkpoint("dst-precopy-loadvm-completed");
- ps = postcopy_state_get();
- trace_process_incoming_migration_co_end(ret, ps);
- if (ps != POSTCOPY_INCOMING_NONE) {
- if (ps == POSTCOPY_INCOMING_ADVISE) {
- /*
- * Where a migration had postcopy enabled (and thus went to advise)
- * but managed to complete within the precopy period, we can use
- * the normal exit.
- */
- postcopy_incoming_cleanup(mis);
- } else if (ret >= 0) {
- /*
- * Postcopy was started, cleanup should happen at the end of the
- * postcopy thread.
- */
- trace_process_incoming_migration_co_postcopy_end_main();
- goto out;
- }
- /* Else if something went wrong then just fall out of the normal exit */
+ trace_process_incoming_migration_co_end(ret);
+ if (mis->have_listen_thread) {
+ /*
+ * Postcopy was started, cleanup should happen at the end of the
+ * postcopy listen thread.
+ */
+ trace_process_incoming_migration_co_postcopy_end_main();
+ goto out;
}
if (ret < 0) {
@@ -933,15 +926,6 @@ fail:
}
exit(EXIT_FAILURE);
- } else {
- /*
- * Report the error here in case that QEMU abruptly exits
- * when postcopy is enabled.
- */
- WITH_QEMU_LOCK_GUARD(&s->error_mutex) {
- error_report_err(s->error);
- s->error = NULL;
- }
}
out:
/* Pairs with the refcount taken in qmp_migrate_incoming() */
diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
index b47c955763..48cbb46c27 100644
--- a/migration/postcopy-ram.c
+++ b/migration/postcopy-ram.c
@@ -2078,6 +2078,24 @@ bool postcopy_is_paused(MigrationStatus status)
status == MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP;
}
+static void postcopy_listen_thread_bh(void *opaque)
+{
+ MigrationIncomingState *mis = migration_incoming_get_current();
+
+ migration_incoming_state_destroy();
+
+ if (mis->state == MIGRATION_STATUS_FAILED) {
+ /*
+ * If something went wrong then we have a bad state so exit;
+ * we only could have gotten here if something failed before
+ * POSTCOPY_INCOMING_RUNNING (for example device load), otherwise
+ * postcopy migration would pause inside qemu_loadvm_state_main().
+ * Failing dirty-bitmaps won't fail the whole migration.
+ */
+ exit(1);
+ }
+}
+
/*
* Triggered by a postcopy_listen command; this thread takes over reading
* the input stream, leaving the main thread free to carry on loading the rest
@@ -2131,53 +2149,38 @@ static void *postcopy_listen_thread(void *opaque)
"bitmaps are correctly migrated and valid.",
__func__, load_res, error_get_pretty(local_err));
g_clear_pointer(&local_err, error_free);
- load_res = 0; /* prevent further exit() */
} else {
+ /*
+ * Something went fatally wrong and we have a bad state, QEMU will
+ * exit depending on if postcopy-exit-on-error is true, but the
+ * migration cannot be recovered.
+ */
error_prepend(&local_err,
"loadvm failed during postcopy: %d: ", load_res);
migrate_set_error(migr, local_err);
g_clear_pointer(&local_err, error_report_err);
migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
MIGRATION_STATUS_FAILED);
+ goto out;
}
}
- if (load_res >= 0) {
- /*
- * This looks good, but it's possible that the device loading in the
- * main thread hasn't finished yet, and so we might not be in 'RUN'
- * state yet; wait for the end of the main thread.
- */
- qemu_event_wait(&mis->main_thread_load_event);
- }
- postcopy_incoming_cleanup(mis);
-
- if (load_res < 0) {
- /*
- * If something went wrong then we have a bad state so exit;
- * depending how far we got it might be possible at this point
- * to leave the guest running and fire MCEs for pages that never
- * arrived as a desperate recovery step.
- */
- rcu_unregister_thread();
- exit(EXIT_FAILURE);
- }
+ /*
+ * This looks good, but it's possible that the device loading in the
+ * main thread hasn't finished yet, and so we might not be in 'RUN'
+ * state yet; wait for the end of the main thread.
+ */
+ qemu_event_wait(&mis->main_thread_load_event);
migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
MIGRATION_STATUS_COMPLETED);
- /*
- * If everything has worked fine, then the main thread has waited
- * for us to start, and we're the last use of the mis.
- * (If something broke then qemu will have to exit anyway since it's
- * got a bad migration state).
- */
- bql_lock();
- migration_incoming_state_destroy();
- bql_unlock();
+out:
rcu_unregister_thread();
mis->have_listen_thread = false;
postcopy_state_set(POSTCOPY_INCOMING_END);
+ migration_bh_schedule(postcopy_listen_thread_bh, NULL);
+
object_unref(OBJECT(migr));
return NULL;
diff --git a/migration/trace-events b/migration/trace-events
index e8edd1fbba..772636f3ac 100644
--- a/migration/trace-events
+++ b/migration/trace-events
@@ -193,7 +193,7 @@ source_return_path_thread_resume_ack(uint32_t v) "%"PRIu32
source_return_path_thread_switchover_acked(void) ""
migration_thread_low_pending(uint64_t pending) "%" PRIu64
migrate_transferred(uint64_t transferred, uint64_t time_spent, uint64_t bandwidth, uint64_t avail_bw, uint64_t size) "transferred %" PRIu64 " time_spent %" PRIu64 " bandwidth %" PRIu64 " switchover_bw %" PRIu64 " max_size %" PRId64
-process_incoming_migration_co_end(int ret, int ps) "ret=%d postcopy-state=%d"
+process_incoming_migration_co_end(int ret) "ret=%d"
process_incoming_migration_co_postcopy_end_main(void) ""
postcopy_preempt_enabled(bool value) "%d"
migration_precopy_complete(void) ""
--
2.50.1
next prev parent reply other threads:[~2025-11-03 21:11 UTC|newest]
Thread overview: 39+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-03 21:05 [PULL 00/36] Staging patches Peter Xu
2025-11-03 21:05 ` [PULL 01/36] migration/savevm: Add a compatibility check for capabilities Peter Xu
2025-11-03 21:05 ` [PULL 02/36] MAINTAINERS: update cpr reviewers Peter Xu
2025-11-03 21:05 ` [PULL 03/36] migration/ram: fix docs of ram_handle_zero Peter Xu
2025-11-03 21:05 ` [PULL 04/36] migration: add FEATURE_SEEKABLE to QIOChannelBlock Peter Xu
2025-11-03 21:05 ` [PULL 05/36] migration: mapped-ram: handle zero pages Peter Xu
2025-11-03 21:05 ` [PULL 06/36] migration: Remove unused VMSTATE_UINTTL_EQUAL[_V]() macros Peter Xu
2025-11-03 21:05 ` [PULL 07/36] migration: Fix error leak in postcopy_ram_listen_thread() Peter Xu
2025-11-03 21:05 ` [PULL 08/36] migration/cpr: Fix coverity report in cpr_exec_persist_state() Peter Xu
2025-11-03 21:05 ` [PULL 09/36] migration/cpr: Fix UAF in cpr_exec_cb() when execvp() fails Peter Xu
2025-11-03 21:05 ` [PULL 10/36] migration/cpr: Avoid crashing QEMU when cpr-exec runs with no args Peter Xu
2025-11-03 21:06 ` [PULL 11/36] ram-block-attributes: fix interaction with hugetlb memory backends Peter Xu
2025-11-03 21:06 ` [PULL 12/36] ram-block-attributes: Unify the retrieval of the block size Peter Xu
2025-11-03 21:06 ` [PULL 13/36] migration/qmp: Update "resume" flag doc in "migrate" command Peter Xu
2025-11-05 12:27 ` Richard Henderson
2025-11-03 21:06 ` [PULL 14/36] migration/cpr: Document obscure usage of g_autofree when parse str Peter Xu
2025-11-03 21:06 ` [PULL 15/36] hostmem/shm: Allow shm memory backend serve as shared memory for coco-VMs Peter Xu
2025-11-03 21:06 ` [PULL 16/36] migration: Fix regression of passing error_fatal into vmstate_load_state() Peter Xu
2025-11-03 21:06 ` [PULL 17/36] migration: Don't free the reason after calling migrate_add_blocker Peter Xu
2025-11-03 21:06 ` [PULL 18/36] migration: Use unsigned instead of int for bit set of MigMode Peter Xu
2025-11-03 21:06 ` [PULL 19/36] migration: Use bitset of MigMode instead of variable arguments Peter Xu
2025-11-03 21:06 ` [PULL 20/36] migration: Put Error **errp parameter last Peter Xu
2025-11-03 21:06 ` [PULL 21/36] io: Add qio_channel_wait_cond() helper Peter Xu
2025-11-03 21:06 ` [PULL 22/36] migration: Properly wait on G_IO_IN when peeking messages Peter Xu
2025-11-03 21:06 ` [PULL 23/36] migration: vmstate_save_state_v(): fix error path Peter Xu
2025-11-03 21:06 ` [PULL 24/36] tmp_emulator: improve and fix use of errp Peter Xu
2025-11-03 21:06 ` [PULL 25/36] migration/vmstate: stop reporting error number for new _errp APIs Peter Xu
2025-11-03 21:06 ` [PULL 26/36] migration: vmsd errp handlers: return bool Peter Xu
2025-11-03 21:06 ` [PULL 27/36] scripts/vmstate-static-checker: Fix deprecation warnings with latest argparse Peter Xu
2025-11-03 21:06 ` [PULL 28/36] system/physmem: mark io_mem_unassigned lockless Peter Xu
2025-11-03 21:06 ` [PULL 29/36] migration: Flush migration channel after sending data of CMD_PACKAGED Peter Xu
2025-11-03 21:06 ` [PULL 30/36] migration: Do not try to start VM if disk activation fails Peter Xu
2025-11-03 21:06 ` [PULL 31/36] migration: Move postcopy_ram_listen_thread() to postcopy-ram.c Peter Xu
2025-11-03 21:06 ` [PULL 32/36] migration: Introduce postcopy incoming setup and cleanup functions Peter Xu
2025-11-03 21:06 ` Peter Xu [this message]
2025-11-03 21:06 ` [PULL 34/36] migration: Respect exit-on-error when migration fails before resuming Peter Xu
2025-11-03 21:06 ` [PULL 35/36] migration: Make postcopy listen thread joinable Peter Xu
2025-11-03 21:06 ` [PULL 36/36] migration: Introduce POSTCOPY_DEVICE state Peter Xu
2025-11-05 7:52 ` [PULL 00/36] Staging patches Richard Henderson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251103210625.3689448-34-peterx@redhat.com \
--to=peterx@redhat.com \
--cc=david@redhat.com \
--cc=farosas@suse.de \
--cc=jmarcin@redhat.com \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).