Commit 7bb7ac94ff for qemu.org
commit 7bb7ac94ff8a0458f5e80b74f6c0d99d592fb2e2
Author: Aadeshveer Singh <aadeshveer07@gmail.com>
Date: Sun Aug 16 23:16:26 2026 +0530
migration: add RAMBlock field and helper for fast snapshot load
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 <aadeshveer07@gmail.com>
Signed-off-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Fabiano Rosas <farosas@suse.de>
diff --git a/include/system/ramblock.h b/include/system/ramblock.h
index f0639287bf..84624aca2c 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 60ad435215..b7c406428e 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -263,6 +263,25 @@ 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);
+ /*
+ * The pending_bmap granularity must match the maximum of host and guest
+ * page sizes. This ensures that every load operation checks for one
+ * bit, allowing lockless thread coordination via a single-bit atomic
+ * test-and-clear.
+ */
+ size_t size = rb->max_length /
+ MAX(qemu_ram_pagesize(rb), qemu_target_page_size());
+ rb->pending_bmap = bitmap_new(size);
+ bitmap_set(rb->pending_bmap, 0, size);
+ }
+}
+
static void ramblock_recv_map_init(void)
{
RAMBlock *rb;
@@ -3768,6 +3787,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;
@@ -3788,6 +3811,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;
@@ -4207,9 +4231,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 */
@@ -4488,15 +4515,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++;
@@ -4512,7 +4566,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