@samitouri / QOSamiQemu / commits / 7bb7ac94ff

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>

Aadeshveer Singh committed Aug 16, 2026 at 23:16 UTC 7bb7ac94ff8a0458f5e80b74f6c0d99d592fb2e2
2 files changed +66 -6
include/system/ramblock.h
+6
@@ -60,6 +60,12 @@ struct RAMBlock {
60
61 /* Bitmap of already received pages. Only used on destination side. */
62 unsigned long *receivedmap;
63 + /*
64 + * Bitmap for pages that are yet to be read from disk. It is required for
65 + * fault thread and eager thread to keep note of which pages are currently
66 + * being read. Used by fast snapshot load.
67 + */
68 + unsigned long *pending_bmap;
69
70 /*
71 * bitmap to track already cleared dirty bitmap. When the bit is
migration/ram.c
+60 -6
@@ -263,6 +263,25 @@ static void ramblock_file_bmap_init(void)
263 }
264 }
265
266 +static void ramblock_pending_bmap_init(void)
267 +{
268 + RAMBlock *rb;
269 +
270 + RAMBLOCK_FOREACH_NOT_IGNORED(rb) {
271 + assert(!rb->pending_bmap);
272 + /*
273 + * The pending_bmap granularity must match the maximum of host and guest
274 + * page sizes. This ensures that every load operation checks for one
275 + * bit, allowing lockless thread coordination via a single-bit atomic
276 + * test-and-clear.
277 + */
278 + size_t size = rb->max_length /
279 + MAX(qemu_ram_pagesize(rb), qemu_target_page_size());
280 + rb->pending_bmap = bitmap_new(size);
281 + bitmap_set(rb->pending_bmap, 0, size);
282 + }
283 +}
284 +
285 static void ramblock_recv_map_init(void)
286 {
287 RAMBlock *rb;
@@ -3768,6 +3787,10 @@ static int ram_load_setup(QEMUFile *f, void *opaque, Error **errp)
3787 ramblock_recv_map_init();
3788 if (migrate_mapped_ram()) {
3789 ramblock_file_bmap_init();
3790 + if (migrate_postcopy_ram()) {
3791 + /* fast snapshot load */
3792 + ramblock_pending_bmap_init();
3793 + }
3794 }
3795
3796 return 0;
@@ -3788,6 +3811,7 @@ static int ram_load_cleanup(void *opaque)
3811 RAMBLOCK_FOREACH_NOT_IGNORED(rb) {
3812 g_clear_pointer(&rb->receivedmap, g_free);
3813 g_clear_pointer(&rb->file_bmap, g_free);
3814 + g_clear_pointer(&rb->pending_bmap, g_free);
3815 }
3816
3817 return 0;
@@ -4207,9 +4231,12 @@ static void parse_ramblock_mapped_ram(QEMUFile *f, RAMBlock *block,
4231 return;
4232 }
4233
4210 - if (!read_ramblock_mapped_ram(f, block, num_pages, block->file_bmap,
4211 - errp)) {
4212 - return;
4234 + if (!migrate_postcopy_ram()) {
4235 + /* Do not load RAM during setup for fast snapshot load */
4236 + if (!read_ramblock_mapped_ram(f, block, num_pages, block->file_bmap,
4237 + errp)) {
4238 + return;
4239 + }
4240 }
4241
4242 /* Skip pages array */
@@ -4488,15 +4515,42 @@ static int ram_load_precopy(QEMUFile *f)
4515 return ret;
4516 }
4517
4518 +static bool ram_should_load_postcopy_pages(void)
4519 +{
4520 + /* This is pure precopy, we don't need to load pages in postcopy way */
4521 + if (!postcopy_is_running()) {
4522 + return false;
4523 + }
4524 +
4525 + /*
4526 + * This is postcopy, but when with mapped-ram, pages are not loaded in the
4527 + * migration stream here, but done separately in a thread eagerly reading
4528 + * pages from the snapshot. Here, we only need to read the ram headers,
4529 + * reusing the precopy code.
4530 + * TODO: when we have separate function to parse RAM headers we should
4531 + * switch to that.
4532 + */
4533 + if (migrate_mapped_ram()) {
4534 + return false;
4535 + }
4536 +
4537 + /*
4538 + * Genuine network postcopy, we will load pages in this current stream and
4539 + * they need to be done in postcopy way.
4540 + */
4541 + return true;
4542 +}
4543 +
4544 static int ram_load(QEMUFile *f, void *opaque, int version_id)
4545 {
4546 int ret = 0;
4547 static uint64_t seq_iter;
4548 /*
4549 * If system is running in postcopy mode, page inserts to host memory must
4497 - * be atomic
4550 + * be atomic. However, fast snapshot load uses the mapped ram precopy like
4551 + * path to read block headers and populating bitmaps.
4552 */
4499 - bool postcopy_running = postcopy_is_running();
4553 + bool load_postcopy_pages = ram_should_load_postcopy_pages();
4554
4555 seq_iter++;
4556
@@ -4512,7 +4566,7 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
4566 */
4567 trace_ram_load_start();
4568 WITH_RCU_READ_LOCK_GUARD() {
4515 - if (postcopy_running) {
4569 + if (load_postcopy_pages) {
4570 /*
4571 * Note! Here RAM_CHANNEL_PRECOPY is the precopy channel of
4572 * postcopy migration, we have another RAM_CHANNEL_POSTCOPY to