From: Peter Xu <peterx@redhat.com>
To: Aadeshveer Singh <aadeshveer07@gmail.com>
Cc: qemu-devel@nongnu.org, farosas@suse.de, pbonzini@redhat.com,
philmd@mailo.com, lvivier@redhat.com, ayoub@saferwall.com
Subject: Re: [RFC PATCH v2 7/7] migration: add eager load thread and setup for fast snapshot load
Date: Mon, 6 Jul 2026 17:07:59 -0400 [thread overview]
Message-ID: <akwZLy6EfzJNlTtY@x1.local> (raw)
In-Reply-To: <20260630081940.611092-8-aadeshveer07@gmail.com>
On Tue, Jun 30, 2026 at 01:49:40PM +0530, Aadeshveer Singh wrote:
> In fast snapshot load a thread is needed for actively loading in pages
> along with the fault path so that the guest is not dependent on fault
> thread indefinitely. Considering the difference from usual network
> postcopy where major chunk of RAM is already loaded here entire RAM
> needs to be loaded later. Existance of background pages which are not
> really accessed by the guest might never be loaded and system will be
> locked in migration for indefinite time. As there should be no
> assumption about how guest accesses memory, the load times can be
> indefinite.
>
> Add postcopy_ram_eager_load_thread(), for the eager thread which
> iterates over all non ignored blocks calling ram_block_load_eager()
> on each. ram_block_load_eager then iterates to load in all pages using
> postcopy_mapped_ram_load_page(), with a different channel, which takes
> care of not loading in pages already loaded by fault thread. On
> completion the thread schedules postcopy_incoming_complete_bh() to
> destroy the incoming migration state.
>
> Add postcopy_ram_eager_load_setup() to create the thread. Added joining
> logic in postcopy_incoming_cleanup().
>
> Add tracepoints for entry and exit to eager load thread.
>
> When both mapped-ram and postcopy-ram are set, divert from
> qemu_loadvm_state to run fast snapshot load
>
> Initialize postcopy RAM state and register RAM Blocks with userfaultfd
> via ram_postcopy_incoming_init() and postcopy_ram_incoming_setup() in
> process_incoming_migration_co(). Fault thread needs to be launched
> before VM to serve faults for some hardwares emulation that need to read
> RAM (like vapic devices). Populate bitmaps and offset tables while
> reading file in qemu_loadvm_state_main.
>
> Add function qemu_loadvm_run_fast_snapshot_load() which starts the VM
> using loadvm_postcopy_handle_run_bh() and launches eager load thread.
>
> Skip scheduling process_incoming_migration_bh() in
> process_incoming_migration_co(), for fast snapshot load as the state
> cleanup is managed by eager load thread on completion.
>
> Signed-off-by: Aadeshveer Singh <aadeshveer07@gmail.com>
> ---
> migration/migration.c | 33 ++++++++++++++++--
> migration/migration.h | 5 +++
> migration/postcopy-ram.c | 74 ++++++++++++++++++++++++++++++++++++++--
> migration/postcopy-ram.h | 2 ++
> migration/savevm.c | 26 ++++++++++++++
> migration/savevm.h | 2 ++
> migration/trace-events | 2 ++
> 7 files changed, 140 insertions(+), 4 deletions(-)
>
> diff --git a/migration/migration.c b/migration/migration.c
> index 074d3f2c69..9295794713 100644
> --- a/migration/migration.c
> +++ b/migration/migration.c
> @@ -730,6 +730,11 @@ static void process_incoming_migration_bh(void *opaque)
> migration_incoming_state_destroy();
> }
>
> +static bool migration_incoming_has_postcopy_thread(MigrationIncomingState *mis)
> +{
> + return mis->have_listen_thread || mis->have_eager_load_thread;
> +}
> +
> static void coroutine_fn
> process_incoming_migration_co(void *opaque)
> {
> @@ -759,17 +764,41 @@ process_incoming_migration_co(void *opaque)
> migrate_set_state(&mis->state, MIGRATION_STATUS_SETUP,
> MIGRATION_STATUS_ACTIVE);
>
Below is correct, said that, it would be nice to add a comment explaining
why setup postcopy needs to be done here, especially, why it needs to be
before qemu_loadvm_state(); it's very not obvious but critical..
Something like this?
/*
* When loading snapshot with postcopy enabled, setup the postcopy
* infrastructure before loading the major part of device states.
* It's required because qemu_loadvm_state() may access guest memory
* while loading device states, which can cause page faults already.
*/
> + if (migrate_postcopy_ram() && migrate_mapped_ram()) {
> + migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
> + MIGRATION_STATUS_POSTCOPY_DEVICE);
> +
> + if (ram_postcopy_incoming_init(mis, &local_err)) {
> + goto fail;
> + }
> +
> + postcopy_state_set(POSTCOPY_INCOMING_LISTENING);
> + if (postcopy_ram_incoming_setup(mis, &local_err)) {
> + goto fail;
> + }
> + }
> +
> mis->loadvm_co = qemu_coroutine_self();
> ret = qemu_loadvm_state(mis->from_src_file, &local_err);
> mis->loadvm_co = NULL;
> + if (ret < 0) {
> + goto fail;
> + }
> +
> + if (migrate_postcopy_ram() && migrate_mapped_ram()) {
> + if (qemu_loadvm_run_fast_snapshot_load(mis->from_src_file, mis,
> + &local_err)) {
> + goto fail;
> + }
> + }
>
> trace_vmstate_downtime_checkpoint("dst-precopy-loadvm-completed");
>
> trace_process_incoming_migration_co_end(ret);
> - if (mis->have_listen_thread) {
> + if (migration_incoming_has_postcopy_thread(mis)) {
> /*
> * Postcopy was started, cleanup should happen at the end of the
> - * postcopy listen thread.
> + * postcopy listen thread or eager load thread.
> */
> trace_process_incoming_migration_co_postcopy_end_main();
> goto out;
> diff --git a/migration/migration.h b/migration/migration.h
> index 841f49b215..540124bc27 100644
> --- a/migration/migration.h
> +++ b/migration/migration.h
> @@ -42,6 +42,7 @@
> #define MIGRATION_THREAD_DST_FAULT "mig/dst/fault"
> #define MIGRATION_THREAD_DST_LISTEN "mig/dst/listen"
> #define MIGRATION_THREAD_DST_PREEMPT "mig/dst/preempt"
> +#define MIGRATION_THREAD_DST_SNAPSHOT_LOAD "mig/dst/snapshot_load"
>
> struct PostcopyBlocktimeContext;
> typedef struct ThreadPool ThreadPool;
> @@ -120,6 +121,10 @@ struct MigrationIncomingState {
> bool have_listen_thread;
> QemuThread listen_thread;
>
> + /* Thread to load pages eagerly in fast snapshot load case */
> + bool have_eager_load_thread;
> + QemuThread eager_load_thread;
> +
> /* For the kernel to send us notifications */
> int userfault_fd;
> /* To notify the fault_thread to wake, e.g., when need to quit */
> diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
> index be9edac572..c31abd4339 100644
> --- a/migration/postcopy-ram.c
> +++ b/migration/postcopy-ram.c
> @@ -2159,7 +2159,7 @@ bool postcopy_is_paused(MigrationStatus status)
> status == MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP;
> }
>
> -static void postcopy_listen_thread_bh(void *opaque)
> +static void postcopy_incoming_complete_bh(void *opaque)
> {
> MigrationState *s = migrate_get_current();
> MigrationIncomingState *mis = migration_incoming_get_current();
> @@ -2267,7 +2267,7 @@ out:
> rcu_unregister_thread();
> postcopy_state_set(POSTCOPY_INCOMING_END);
>
> - migration_bh_schedule(postcopy_listen_thread_bh, NULL);
> + migration_bh_schedule(postcopy_incoming_complete_bh, NULL);
Nitpick: this rename change can be a tiny separate patch.
>
> object_unref(OBJECT(migr));
>
> @@ -2311,9 +2311,79 @@ int postcopy_incoming_cleanup(MigrationIncomingState *mis)
> mis->have_listen_thread = false;
> }
>
> + if (mis->have_eager_load_thread) {
> + qemu_thread_join(&mis->eager_load_thread);
> + mis->have_eager_load_thread = false;
> + }
> +
> if (migrate_postcopy_ram()) {
> rc = postcopy_ram_incoming_cleanup(mis);
> }
>
> return rc;
> }
> +
> +/*
> + * Called by postcopy_ram_eager_load_thread over all blocks to load in all the
> + * pending pages of given ram block
> + */
> +static int ram_block_load_eager(RAMBlock *rb, void *opaque)
> +{
> + MigrationIncomingState *mis = migration_incoming_get_current();
> + MigrationState *s = migrate_get_current();
> + Error *errp = NULL;
> + void *host = qemu_ram_get_host_addr(rb);
> + void *target;
> +
> + for (ram_addr_t page_loc = 0; page_loc < rb->used_length;
> + page_loc += qemu_ram_pagesize(rb)) {
> + target = (uint8_t *)host + page_loc;
> + if (!postcopy_mapped_ram_load_page(mis, rb, page_loc, (uint64_t)target,
> + RAM_CHANNEL_PRECOPY, &errp)) {
> + migrate_error_propagate(s, errp);
> + return 1;
Nit: return a negative (e.g. -1) might be clearer for an error.
> + }
> + }
> + return 0;
> +}
> +
> +/*
> + * Used by fast snapshot load to eagerly load in all pages of RAM and schedule
> + * cleanup after entire RAM is loaded
> + */
> +static void *postcopy_ram_eager_load_thread(void *opaque)
> +{
> + MigrationIncomingState *mis = opaque;
> +
> + trace_postcopy_ram_eager_load_thread_entry();
> + rcu_register_thread();
> + qemu_event_set(&mis->thread_sync_event);
> +
> + if (foreach_not_ignored_block(ram_block_load_eager, NULL)) {
> + migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
> + MIGRATION_STATUS_FAILED);
> + goto out;
> + }
> + migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE,
> + MIGRATION_STATUS_COMPLETED);
> +
> +out:
> + postcopy_state_set(POSTCOPY_INCOMING_END);
> + migration_bh_schedule(postcopy_incoming_complete_bh, mis);
> +
> + rcu_unregister_thread();
> + trace_postcopy_ram_eager_load_thread_exit();
> + return NULL;
> +}
> +
> +/*
> + * Create thread for eager loading in fast snapshot load case
> + */
> +int postcopy_ram_eager_load_setup(MigrationIncomingState *mis)
> +{
> + postcopy_thread_create(
> + mis, &mis->eager_load_thread, MIGRATION_THREAD_DST_SNAPSHOT_LOAD,
> + postcopy_ram_eager_load_thread, QEMU_THREAD_JOINABLE);
> + mis->have_eager_load_thread = true;
> + return 0;
> +}
> diff --git a/migration/postcopy-ram.h b/migration/postcopy-ram.h
> index 98d918713f..6d1a296795 100644
> --- a/migration/postcopy-ram.h
> +++ b/migration/postcopy-ram.h
> @@ -202,4 +202,6 @@ void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid,
> int postcopy_incoming_setup(MigrationIncomingState *mis, Error **errp);
> int postcopy_incoming_cleanup(MigrationIncomingState *mis);
>
> +int postcopy_ram_eager_load_setup(MigrationIncomingState *mis);
> +
> #endif
> diff --git a/migration/savevm.c b/migration/savevm.c
> index 23adaf9dd9..5857fb8ac2 100644
> --- a/migration/savevm.c
> +++ b/migration/savevm.c
> @@ -2959,6 +2959,32 @@ static bool postcopy_pause_incoming(MigrationIncomingState *mis)
> return true;
> }
>
> +/*
> + * Starts the VM and launches the eager thread for fast snapshot load
> + */
> +int qemu_loadvm_run_fast_snapshot_load(QEMUFile *f, MigrationIncomingState *mis,
> + Error **errp)
> +{
> + ERRP_GUARD();
> + int ret = 0;
> +
> + postcopy_state_set(POSTCOPY_INCOMING_RUNNING);
> +
> + migration_bh_schedule(loadvm_postcopy_handle_run_bh, mis);
> +
> + migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_DEVICE,
> + MIGRATION_STATUS_POSTCOPY_ACTIVE);
> +
> + ret = postcopy_ram_eager_load_setup(mis);
This function never fails, can make it return void and remove the err
handling.
> + if (ret) {
> + error_prepend(errp,
> + "Failed to setup eager load for fast snapshot load: ");
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis,
> Error **errp)
> {
> diff --git a/migration/savevm.h b/migration/savevm.h
> index 96fdf96d4e..f17d7057e1 100644
> --- a/migration/savevm.h
> +++ b/migration/savevm.h
> @@ -67,6 +67,8 @@ void qemu_savevm_send_postcopy_ram_discard(QEMUFile *f, const char *name,
> int qemu_save_device_state(QEMUFile *f, Error **errp);
> int qemu_loadvm_state(QEMUFile *f, Error **errp);
> void qemu_loadvm_state_cleanup(MigrationIncomingState *mis);
> +int qemu_loadvm_run_fast_snapshot_load(QEMUFile *f, MigrationIncomingState *mis,
> + Error **errp);
> int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis,
> Error **errp);
> int qemu_load_device_state(QEMUFile *f, Error **errp);
> diff --git a/migration/trace-events b/migration/trace-events
> index de99d976ab..38f11e1e9f 100644
> --- a/migration/trace-events
> +++ b/migration/trace-events
> @@ -314,6 +314,8 @@ postcopy_blocktime_tid_cpu_map(int cpu, uint32_t tid) "cpu: %d, tid: %u"
> postcopy_blocktime_begin(uint64_t addr, uint64_t time, int cpu, bool exists) "addr: 0x%" PRIx64 ", time: %" PRIu64 ", cpu: %d, exist: %d"
> postcopy_blocktime_end(uint64_t addr, uint64_t time, int affected_cpu, int affected_non_cpus) "addr: 0x%" PRIx64 ", time: %" PRIu64 ", affected_cpus: %d, affected_non_cpus: %d"
> postcopy_blocktime_end_one(int cpu, uint8_t left_faults) "cpu: %d, left_faults: %" PRIu8
> +postcopy_ram_eager_load_thread_entry(void) ""
> +postcopy_ram_eager_load_thread_exit(void) ""
>
> # exec.c
> migration_exec_outgoing(const char *cmd) "cmd=%s"
> --
> 2.54.0
>
--
Peter Xu
next prev parent reply other threads:[~2026-07-06 21:08 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-30 8:19 [RFC PATCH v2 0/7] migration: fast snapshot load Aadeshveer Singh
2026-06-30 8:19 ` [RFC PATCH v2 1/7] migration/tests: remove capability conflict test postcopy-ram+mapped-ram Aadeshveer Singh
2026-07-06 19:02 ` Peter Xu
2026-06-30 8:19 ` [RFC PATCH v2 2/7] migration: Propagate error in postcopy setup functions Aadeshveer Singh
2026-07-06 19:13 ` Peter Xu
2026-06-30 8:19 ` [RFC PATCH v2 3/7] migration: Use file_bmap for RAMBlock during incoming file load Aadeshveer Singh
2026-07-06 19:29 ` Peter Xu
2026-06-30 8:19 ` [RFC PATCH v2 4/7] migration: Make qemu_get_buffer_at() thread-safe Aadeshveer Singh
2026-07-06 19:34 ` Peter Xu
2026-06-30 8:19 ` [RFC PATCH v2 5/7] migration: add RAMBlock field and helper for fast snapshot load Aadeshveer Singh
2026-07-06 20:03 ` Peter Xu
2026-06-30 8:19 ` [RFC PATCH v2 6/7] migration: add support for fault thread to load pages from disk Aadeshveer Singh
2026-07-06 20:35 ` Peter Xu
2026-06-30 8:19 ` [RFC PATCH v2 7/7] migration: add eager load thread and setup for fast snapshot load Aadeshveer Singh
2026-07-06 21:07 ` Peter Xu [this message]
2026-07-06 19:16 ` [RFC PATCH v2 0/7] migration: " Peter Xu
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=akwZLy6EfzJNlTtY@x1.local \
--to=peterx@redhat.com \
--cc=aadeshveer07@gmail.com \
--cc=ayoub@saferwall.com \
--cc=farosas@suse.de \
--cc=lvivier@redhat.com \
--cc=pbonzini@redhat.com \
--cc=philmd@mailo.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.