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