On Tue, Jun 30, 2026 at 01:49:38PM +0530, Aadeshveer Singh wrote:
> Add pending_bmap field per RAMBlock which is a Bitmap to store
> internal state of which pages have been read by some thread to ensure
> coordination between fault thread and eager load thread.
>
> Modify parse_ramblock_mapped_ram(), to not load the actual RAMBlocks
> data in postcopy case as that will be loaded by fault thread and eager
> thread after the VM starts running.
>
> Change ram_load() to use new function ram_should_load_postcopy_pages()
> to decide how to load/read RAM.
>
> Signed-off-by: Aadeshveer Singh <[email protected]>
> ---
> include/system/ramblock.h | 6 ++++
> migration/ram.c | 59 +++++++++++++++++++++++++++++++++++----
> 2 files changed, 59 insertions(+), 6 deletions(-)
>
> diff --git a/include/system/ramblock.h b/include/system/ramblock.h
> index 4435f8d55f..83187bf44c 100644
> --- a/include/system/ramblock.h
> +++ b/include/system/ramblock.h
> @@ -60,6 +60,12 @@ struct RAMBlock {
>
> /* Bitmap of already received pages. Only used on destination side. */
> unsigned long *receivedmap;
> + /*
> + * Bitmap for pages that are yet to be read from disk. It is required for
> + * fault thread and eager thread to keep note of which pages are
> currently
> + * being read. Used by fast snapshot load.
> + */
> + unsigned long *pending_bmap;
>
> /*
> * bitmap to track already cleared dirty bitmap. When the bit is
> diff --git a/migration/ram.c b/migration/ram.c
> index 4728f14d73..ffd60b2ac8 100644
> --- a/migration/ram.c
> +++ b/migration/ram.c
> @@ -263,6 +263,18 @@ static void ramblock_file_bmap_init(void)
> }
> }
>
> +static void ramblock_pending_bmap_init(void)
> +{
> + RAMBlock *rb;
> +
> + RAMBLOCK_FOREACH_NOT_IGNORED(rb) {
> + assert(!rb->pending_bmap);
> + size_t size = rb->max_length >> qemu_target_page_bits();
IIUC we should always stick with rb->page_size (aka, host page size) here
rather than guest page size. It's because you want to use that one bit to
serialize the two threads (fault thread, eager thread) when do atomic
test_and_clear, and since UFFDIO_COPY needs to happen at host page size
granule, this needs to match that.
> + rb->pending_bmap = bitmap_new(size);
> + bitmap_set(rb->pending_bmap, 0, size);
> + }
> +}
> +
> static void ramblock_recv_map_init(void)
> {
> RAMBlock *rb;
> @@ -3762,6 +3774,10 @@ static int ram_load_setup(QEMUFile *f, void *opaque,
> Error **errp)
> ramblock_recv_map_init();
> if (migrate_mapped_ram()) {
> ramblock_file_bmap_init();
> + if (migrate_postcopy_ram()) {
> + /* fast snapshot load */
> + ramblock_pending_bmap_init();
> + }
> }
>
> return 0;
> @@ -3782,6 +3798,7 @@ static int ram_load_cleanup(void *opaque)
> RAMBLOCK_FOREACH_NOT_IGNORED(rb) {
> g_clear_pointer(&rb->receivedmap, g_free);
> g_clear_pointer(&rb->file_bmap, g_free);
> + g_clear_pointer(&rb->pending_bmap, g_free);
> }
>
> return 0;
> @@ -4201,9 +4218,12 @@ static void parse_ramblock_mapped_ram(QEMUFile *f,
> RAMBlock *block,
> return;
> }
>
> - if (!read_ramblock_mapped_ram(f, block, num_pages, block->file_bmap,
> - errp)) {
> - return;
> + if (!migrate_postcopy_ram()) {
> + /* Do not load RAM during setup for fast snapshot load */
> + if (!read_ramblock_mapped_ram(f, block, num_pages, block->file_bmap,
> + errp)) {
> + return;
> + }
> }
>
> /* Skip pages array */
> @@ -4475,15 +4495,42 @@ static int ram_load_precopy(QEMUFile *f)
> return ret;
> }
>
> +static bool ram_should_load_postcopy_pages(void)
> +{
> + /* This is pure precopy, we don't need to load pages in postcopy way */
> + if (!postcopy_is_running()) {
> + return false;
> + }
> +
> + /*
> + * This is postcopy, but when with mapped-ram, pages are not loaded in
> the
> + * migration stream here, but done separately in a thread eagerly reading
> + * pages from the snapshot. Here, we only need to read the ram headers,
> + * reusing the precopy code.
> + * TODO: when we have separate function to parse RAM headers we should
> + * switch to that.
> + */
> + if (migrate_mapped_ram()) {
> + return false;
> + }
> +
> + /*
> + * Genuine network postcopy, we will load pages in this current stream
> and
> + * they need to be done in postcopy way.
> + */
> + return true;
> +}
> +
> static int ram_load(QEMUFile *f, void *opaque, int version_id)
> {
> int ret = 0;
> static uint64_t seq_iter;
> /*
> * If system is running in postcopy mode, page inserts to host memory
> must
> - * be atomic
> + * be atomic. However, fast snapshot load uses the mapped ram precopy
> like
> + * path to read block headers and populating bitmaps.
> */
> - bool postcopy_running = postcopy_is_running();
> + bool load_postcopy_pages = ram_should_load_postcopy_pages();
>
> seq_iter++;
>
> @@ -4499,7 +4546,7 @@ static int ram_load(QEMUFile *f, void *opaque, int
> version_id)
> */
> trace_ram_load_start();
> WITH_RCU_READ_LOCK_GUARD() {
> - if (postcopy_running) {
> + if (load_postcopy_pages) {
> /*
> * Note! Here RAM_CHANNEL_PRECOPY is the precopy channel of
> * postcopy migration, we have another RAM_CHANNEL_POSTCOPY to
> --
> 2.54.0
>
--
Peter Xu