From 50baf2d9f7bb8502d497385846789ea1755901cc Mon Sep 17 00:00:00 2001 From: Frediano Ziglio Date: Fri, 12 Sep 2025 10:57:44 +0100 Subject: [PATCH] tools/libs/guest: Use superpages where possible on migrate/resume Right now on migrate/resume, all guest memory is allocated with 4k small pages. This was an intended oversight in the original Migration v2 implementation. With some test memory program stressing TLB (many small random memory accesses) superpages can show a 15% performance improvement. On the first memory iteration the sender is currently sending memory in 4MB aligned chunks of GFN space which allows the receiver to allocate most pages as 2MB superpages instead of single 4KB pages. This change does not handle 1GB superpages as this will require changes to the migration protocol. Signed-off-by: Frediano Ziglio Reviewed-by: Andrew Cooper Release-Acked-by: Oleksii Kurochko --- tools/libs/guest/xg_sr_restore.c | 57 ++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/tools/libs/guest/xg_sr_restore.c b/tools/libs/guest/xg_sr_restore.c index 06231ca826..4aed5cf4ac 100644 --- a/tools/libs/guest/xg_sr_restore.c +++ b/tools/libs/guest/xg_sr_restore.c @@ -129,6 +129,35 @@ static int pfn_set_populated(struct xc_sr_context *ctx, xen_pfn_t pfn) return 0; } +#if defined(__i386__) || defined(__x86_64__) +/* Order of the smallest superpage */ +#define SMALL_SUPERPAGE_ORDER 9 +#else +#error Define SMALL_SUPERPAGE_ORDER for this platform +#endif + +static bool populate_small_superpage(struct xc_sr_context *ctx, xen_pfn_t gfn) +{ + xen_pfn_t mfn = gfn; + + if ( xc_domain_populate_physmap_exact( + ctx->xch, ctx->domid, 1, SMALL_SUPERPAGE_ORDER, 0, &mfn) ) + return false; + + /* + * XENMEM_populate_physmap has no coherent error semantics. + * Assume a failure here is ENOMEM, and fall back to allocating + * small pages. + */ + if ( mfn == INVALID_MFN ) + return false; + + for ( size_t i = 0; i < (1UL << SMALL_SUPERPAGE_ORDER); ++i ) + ctx->restore.ops.set_gfn(ctx, gfn + i, mfn + i); + + return true; +} + /* * Given a set of pfns, obtain memory from Xen to fill the physmap for the * unpopulated subset. If types is NULL, no page type checking is performed @@ -142,6 +171,9 @@ int populate_pfns(struct xc_sr_context *ctx, unsigned int count, *pfns = malloc(count * sizeof(*pfns)); unsigned int i, nr_pfns = 0; int rc = -1; + xen_pfn_t prev = 0; + unsigned int num_contiguous = 0; + xen_pfn_t mask = (1ULL << SMALL_SUPERPAGE_ORDER) - 1; if ( !mfns || !pfns ) { @@ -152,14 +184,33 @@ int populate_pfns(struct xc_sr_context *ctx, unsigned int count, for ( i = 0; i < count; ++i ) { + xen_pfn_t pfn = original_pfns[i]; + if ( (!types || page_type_to_populate(types[i])) && - !pfn_is_populated(ctx, original_pfns[i]) ) + !pfn_is_populated(ctx, pfn) ) { - rc = pfn_set_populated(ctx, original_pfns[i]); + rc = pfn_set_populated(ctx, pfn); if ( rc ) goto err; - pfns[nr_pfns] = mfns[nr_pfns] = original_pfns[i]; + pfns[nr_pfns] = mfns[nr_pfns] = pfn; ++nr_pfns; + + /* + * During the first pass for x86 HVM guests, PAGE_DATA + * records contain metadata about 4M aligned chunks of GFN + * space. Reconstruct 2M superpages where possible. + */ + if ( pfn != prev + 1 ) + num_contiguous = 0; + num_contiguous++; + prev = pfn; + + if ( num_contiguous > mask && (pfn & mask) == mask && + populate_small_superpage(ctx, pfn - mask) ) + { + nr_pfns -= mask + 1; + num_contiguous = 0; + } } }