From c22f60e6e55f1bf300dd76d2222a93911f3b2bb2 Mon Sep 17 00:00:00 2001 From: CoprDistGit Date: Thu, 12 Oct 2023 04:00:49 +0000 Subject: automatic import of xen --- libxc.sr.superpage.patch | 905 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 905 insertions(+) create mode 100644 libxc.sr.superpage.patch (limited to 'libxc.sr.superpage.patch') diff --git a/libxc.sr.superpage.patch b/libxc.sr.superpage.patch new file mode 100644 index 0000000..ccb66bc --- /dev/null +++ b/libxc.sr.superpage.patch @@ -0,0 +1,905 @@ +tools/libxc: use superpages during restore of HVM guest +bsc#1035231 - migration of HVM domU does not use superpages on destination dom0 +bsc#1055695 - XEN: 11SP4 and 12SP3 HVM guests can not be restored + +During creating of a HVM domU meminit_hvm() tries to map superpages. +After save/restore or migration this mapping is lost, everything is +allocated in single pages. This causes a performance degradition after +migration. + +Add neccessary code to preallocate a superpage for the chunk of pfns +that is received. In case a pfn was not populated on the sending side it +must be freed on the receiving side to avoid over-allocation. + +The existing code for x86_pv is moved unmodified into its own file. +Index: xen-4.13.1-testing/tools/libxc/xc_dom_x86.c +=================================================================== +--- xen-4.13.1-testing.orig/tools/libxc/xc_dom_x86.c ++++ xen-4.13.1-testing/tools/libxc/xc_dom_x86.c +@@ -45,11 +45,6 @@ + + #define SUPERPAGE_BATCH_SIZE 512 + +-#define SUPERPAGE_2MB_SHIFT 9 +-#define SUPERPAGE_2MB_NR_PFNS (1UL << SUPERPAGE_2MB_SHIFT) +-#define SUPERPAGE_1GB_SHIFT 18 +-#define SUPERPAGE_1GB_NR_PFNS (1UL << SUPERPAGE_1GB_SHIFT) +- + #define X86_CR0_PE 0x01 + #define X86_CR0_ET 0x10 + +Index: xen-4.13.1-testing/tools/libxc/xc_private.h +=================================================================== +--- xen-4.13.1-testing.orig/tools/libxc/xc_private.h ++++ xen-4.13.1-testing/tools/libxc/xc_private.h +@@ -71,6 +71,11 @@ struct iovec { + #define DECLARE_FLASK_OP struct xen_flask_op op + #define DECLARE_PLATFORM_OP struct xen_platform_op platform_op + ++#define SUPERPAGE_2MB_SHIFT 9 ++#define SUPERPAGE_2MB_NR_PFNS (1UL << SUPERPAGE_2MB_SHIFT) ++#define SUPERPAGE_1GB_SHIFT 18 ++#define SUPERPAGE_1GB_NR_PFNS (1UL << SUPERPAGE_1GB_SHIFT) ++ + #undef PAGE_SHIFT + #undef PAGE_SIZE + #undef PAGE_MASK +Index: xen-4.13.1-testing/tools/libxc/xc_sr_common.c +=================================================================== +--- xen-4.13.1-testing.orig/tools/libxc/xc_sr_common.c ++++ xen-4.13.1-testing/tools/libxc/xc_sr_common.c +@@ -156,6 +156,47 @@ static void __attribute__((unused)) buil + } + + /* ++ * Expand the tracking structures as needed. ++ * To avoid realloc()ing too excessively, the size increased to the nearest power ++ * of two large enough to contain the required number of bits. ++ */ ++bool _xc_sr_bitmap_resize(struct xc_sr_bitmap *bm, unsigned long bits) ++{ ++ if (bits > bm->bits) ++ { ++ size_t new_max; ++ size_t old_sz, new_sz; ++ void *p; ++ ++ /* Round up to the nearest power of two larger than bit, less 1. */ ++ new_max = bits; ++ new_max |= new_max >> 1; ++ new_max |= new_max >> 2; ++ new_max |= new_max >> 4; ++ new_max |= new_max >> 8; ++ new_max |= new_max >> 16; ++#ifdef __x86_64__ ++ new_max |= new_max >> 32; ++#endif ++ ++ old_sz = bitmap_size(bm->bits + 1); ++ new_sz = bitmap_size(new_max + 1); ++ p = realloc(bm->p, new_sz); ++ if (!p) ++ return false; ++ ++ if (bm->p) ++ memset(p + old_sz, 0, new_sz - old_sz); ++ else ++ memset(p, 0, new_sz); ++ ++ bm->p = p; ++ bm->bits = new_max; ++ } ++ return true; ++} ++ ++/* + * Local variables: + * mode: C + * c-file-style: "BSD" +Index: xen-4.13.1-testing/tools/libxc/xc_sr_common.h +=================================================================== +--- xen-4.13.1-testing.orig/tools/libxc/xc_sr_common.h ++++ xen-4.13.1-testing/tools/libxc/xc_sr_common.h +@@ -140,6 +140,16 @@ struct xc_sr_restore_ops + int (*setup)(struct xc_sr_context *ctx); + + /** ++ * Populate PFNs ++ * ++ * Given a set of pfns, obtain memory from Xen to fill the physmap for the ++ * unpopulated subset. ++ */ ++ int (*populate_pfns)(struct xc_sr_context *ctx, unsigned count, ++ const xen_pfn_t *original_pfns, const uint32_t *types); ++ ++ ++ /** + * Process an individual record from the stream. The caller shall take + * care of processing common records (e.g. END, PAGE_DATA). + * +@@ -172,6 +182,12 @@ struct xc_sr_x86_pv_restore_vcpu + size_t basicsz, extdsz, xsavesz, msrsz; + }; + ++struct xc_sr_bitmap ++{ ++ void *p; ++ unsigned long bits; ++}; ++ + struct xc_sr_context + { + xc_interface *xch; +@@ -216,6 +232,8 @@ struct xc_sr_context + + int send_back_fd; + unsigned long p2m_size; ++ unsigned long max_pages; ++ unsigned long tot_pages; + xc_hypercall_buffer_t dirty_bitmap_hbuf; + + /* From Image Header. */ +@@ -253,8 +271,7 @@ struct xc_sr_context + uint32_t xenstore_domid, console_domid; + + /* Bitmap of currently populated PFNs during restore. */ +- unsigned long *populated_pfns; +- xen_pfn_t max_populated_pfn; ++ struct xc_sr_bitmap populated_pfns; + + /* Sender has invoked verify mode on the stream. */ + bool verify; +@@ -329,6 +346,12 @@ struct xc_sr_context + /* HVM context blob. */ + void *context; + size_t contextsz; ++ ++ /* Bitmap of currently allocated PFNs during restore. */ ++ struct xc_sr_bitmap attempted_1g; ++ struct xc_sr_bitmap attempted_2m; ++ struct xc_sr_bitmap allocated_pfns; ++ xen_pfn_t idx1G_prev, idx2M_prev; + } restore; + }; + } x86_hvm; +@@ -341,6 +364,69 @@ extern struct xc_sr_save_ops save_ops_x8 + extern struct xc_sr_restore_ops restore_ops_x86_pv; + extern struct xc_sr_restore_ops restore_ops_x86_hvm; + ++extern bool _xc_sr_bitmap_resize(struct xc_sr_bitmap *bm, unsigned long bits); ++ ++static inline bool xc_sr_bitmap_resize(struct xc_sr_bitmap *bm, unsigned long bits) ++{ ++ if (bits > bm->bits) ++ return _xc_sr_bitmap_resize(bm, bits); ++ return true; ++} ++ ++static inline void xc_sr_bitmap_free(struct xc_sr_bitmap *bm) ++{ ++ free(bm->p); ++ bm->p = NULL; ++} ++ ++static inline bool xc_sr_set_bit(unsigned long bit, struct xc_sr_bitmap *bm) ++{ ++ if (!xc_sr_bitmap_resize(bm, bit)) ++ return false; ++ ++ set_bit(bit, bm->p); ++ return true; ++} ++ ++static inline bool xc_sr_test_bit(unsigned long bit, struct xc_sr_bitmap *bm) ++{ ++ if (bit > bm->bits) ++ return false; ++ return !!test_bit(bit, bm->p); ++} ++ ++static inline bool xc_sr_test_and_clear_bit(unsigned long bit, struct xc_sr_bitmap *bm) ++{ ++ if (bit > bm->bits) ++ return false; ++ return !!test_and_clear_bit(bit, bm->p); ++} ++ ++static inline bool xc_sr_test_and_set_bit(unsigned long bit, struct xc_sr_bitmap *bm) ++{ ++ if (bit > bm->bits) ++ return false; ++ return !!test_and_set_bit(bit, bm->p); ++} ++ ++static inline bool pfn_is_populated(struct xc_sr_context *ctx, xen_pfn_t pfn) ++{ ++ return xc_sr_test_bit(pfn, &ctx->restore.populated_pfns); ++} ++ ++static inline int pfn_set_populated(struct xc_sr_context *ctx, xen_pfn_t pfn) ++{ ++ xc_interface *xch = ctx->xch; ++ ++ if ( !xc_sr_set_bit(pfn, &ctx->restore.populated_pfns) ) ++ { ++ ERROR("Failed to realloc populated_pfns bitmap"); ++ errno = ENOMEM; ++ return -1; ++ } ++ return 0; ++} ++ + struct xc_sr_record + { + uint32_t type; +@@ -389,14 +475,6 @@ static inline int write_record(struct xc + */ + int read_record(struct xc_sr_context *ctx, int fd, struct xc_sr_record *rec); + +-/* +- * This would ideally be private in restore.c, but is needed by +- * x86_pv_localise_page() if we receive pagetables frames ahead of the +- * contents of the frames they point at. +- */ +-int populate_pfns(struct xc_sr_context *ctx, unsigned count, +- const xen_pfn_t *original_pfns, const uint32_t *types); +- + #endif + /* + * Local variables: +Index: xen-4.13.1-testing/tools/libxc/xc_sr_restore.c +=================================================================== +--- xen-4.13.1-testing.orig/tools/libxc/xc_sr_restore.c ++++ xen-4.13.1-testing/tools/libxc/xc_sr_restore.c +@@ -69,132 +69,6 @@ static int read_headers(struct xc_sr_con + } + + /* +- * Is a pfn populated? +- */ +-static bool pfn_is_populated(const struct xc_sr_context *ctx, xen_pfn_t pfn) +-{ +- if ( pfn > ctx->restore.max_populated_pfn ) +- return false; +- return test_bit(pfn, ctx->restore.populated_pfns); +-} +- +-/* +- * Set a pfn as populated, expanding the tracking structures if needed. To +- * avoid realloc()ing too excessively, the size increased to the nearest power +- * of two large enough to contain the required pfn. +- */ +-static int pfn_set_populated(struct xc_sr_context *ctx, xen_pfn_t pfn) +-{ +- xc_interface *xch = ctx->xch; +- +- if ( pfn > ctx->restore.max_populated_pfn ) +- { +- xen_pfn_t new_max; +- size_t old_sz, new_sz; +- unsigned long *p; +- +- /* Round up to the nearest power of two larger than pfn, less 1. */ +- new_max = pfn; +- new_max |= new_max >> 1; +- new_max |= new_max >> 2; +- new_max |= new_max >> 4; +- new_max |= new_max >> 8; +- new_max |= new_max >> 16; +-#ifdef __x86_64__ +- new_max |= new_max >> 32; +-#endif +- +- old_sz = bitmap_size(ctx->restore.max_populated_pfn + 1); +- new_sz = bitmap_size(new_max + 1); +- p = realloc(ctx->restore.populated_pfns, new_sz); +- if ( !p ) +- { +- ERROR("Failed to realloc populated bitmap"); +- errno = ENOMEM; +- return -1; +- } +- +- memset((uint8_t *)p + old_sz, 0x00, new_sz - old_sz); +- +- ctx->restore.populated_pfns = p; +- ctx->restore.max_populated_pfn = new_max; +- } +- +- assert(!test_bit(pfn, ctx->restore.populated_pfns)); +- set_bit(pfn, ctx->restore.populated_pfns); +- +- return 0; +-} +- +-/* +- * 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 +- * and all unpopulated pfns are populated. +- */ +-int populate_pfns(struct xc_sr_context *ctx, unsigned count, +- const xen_pfn_t *original_pfns, const uint32_t *types) +-{ +- xc_interface *xch = ctx->xch; +- xen_pfn_t *mfns = malloc(count * sizeof(*mfns)), +- *pfns = malloc(count * sizeof(*pfns)); +- unsigned i, nr_pfns = 0; +- int rc = -1; +- +- if ( !mfns || !pfns ) +- { +- ERROR("Failed to allocate %zu bytes for populating the physmap", +- 2 * count * sizeof(*mfns)); +- goto err; +- } +- +- for ( i = 0; i < count; ++i ) +- { +- if ( (!types || (types && +- (types[i] != XEN_DOMCTL_PFINFO_XTAB && +- types[i] != XEN_DOMCTL_PFINFO_BROKEN))) && +- !pfn_is_populated(ctx, original_pfns[i]) ) +- { +- rc = pfn_set_populated(ctx, original_pfns[i]); +- if ( rc ) +- goto err; +- pfns[nr_pfns] = mfns[nr_pfns] = original_pfns[i]; +- ++nr_pfns; +- } +- } +- +- if ( nr_pfns ) +- { +- rc = xc_domain_populate_physmap_exact( +- xch, ctx->domid, nr_pfns, 0, 0, mfns); +- if ( rc ) +- { +- PERROR("Failed to populate physmap"); +- goto err; +- } +- +- for ( i = 0; i < nr_pfns; ++i ) +- { +- if ( mfns[i] == INVALID_MFN ) +- { +- ERROR("Populate physmap failed for pfn %u", i); +- rc = -1; +- goto err; +- } +- +- ctx->restore.ops.set_gfn(ctx, pfns[i], mfns[i]); +- } +- } +- +- rc = 0; +- +- err: +- free(pfns); +- free(mfns); +- +- return rc; +-} +- +-/* + * Given a list of pfns, their types, and a block of page data from the + * stream, populate and record their types, map the relevant subset and copy + * the data into the guest. +@@ -219,7 +93,7 @@ static int process_page_data(struct xc_s + goto err; + } + +- rc = populate_pfns(ctx, count, pfns, types); ++ rc = ctx->restore.ops.populate_pfns(ctx, count, pfns, types); + if ( rc ) + { + ERROR("Failed to populate pfns for batch of %u pages", count); +@@ -684,10 +558,8 @@ static int setup(struct xc_sr_context *c + if ( rc ) + goto err; + +- ctx->restore.max_populated_pfn = (32 * 1024 / 4) - 1; +- ctx->restore.populated_pfns = bitmap_alloc( +- ctx->restore.max_populated_pfn + 1); +- if ( !ctx->restore.populated_pfns ) ++ rc = !xc_sr_bitmap_resize(&ctx->restore.populated_pfns, 32 * 1024 / 4); ++ if ( rc ) + { + ERROR("Unable to allocate memory for populated_pfns bitmap"); + rc = -1; +@@ -722,7 +594,7 @@ static void cleanup(struct xc_sr_context + xc_hypercall_buffer_free_pages(xch, dirty_bitmap, + NRPAGES(bitmap_size(ctx->restore.p2m_size))); + free(ctx->restore.buffered_records); +- free(ctx->restore.populated_pfns); ++ xc_sr_bitmap_free(&ctx->restore.populated_pfns); + if ( ctx->restore.ops.cleanup(ctx) ) + PERROR("Failed to clean up"); + } +@@ -886,7 +758,12 @@ int xc_domain_restore(xc_interface *xch, + return -1; + } + ++ /* See xc_domain_getinfo */ ++ ctx.restore.max_pages = ctx.dominfo.max_memkb >> (PAGE_SHIFT-10); ++ ctx.restore.tot_pages = ctx.dominfo.nr_pages; + ctx.restore.p2m_size = nr_pfns; ++ DPRINTF("dom %u p2m_size %lx max_pages %lx", ++ ctx.domid, ctx.restore.p2m_size, ctx.restore.max_pages); + + if ( ctx.dominfo.hvm ) + { +Index: xen-4.13.1-testing/tools/libxc/xc_sr_restore_x86_hvm.c +=================================================================== +--- xen-4.13.1-testing.orig/tools/libxc/xc_sr_restore_x86_hvm.c ++++ xen-4.13.1-testing/tools/libxc/xc_sr_restore_x86_hvm.c +@@ -135,6 +135,8 @@ static int x86_hvm_localise_page(struct + static int x86_hvm_setup(struct xc_sr_context *ctx) + { + xc_interface *xch = ctx->xch; ++ struct xc_sr_bitmap *bm; ++ unsigned long bits; + + if ( ctx->restore.guest_type != DHDR_TYPE_X86_HVM ) + { +@@ -158,7 +160,30 @@ static int x86_hvm_setup(struct xc_sr_co + } + #endif + ++ bm = &ctx->x86_hvm.restore.attempted_1g; ++ bits = (ctx->restore.p2m_size >> SUPERPAGE_1GB_SHIFT) + 1; ++ if ( xc_sr_bitmap_resize(bm, bits) == false ) ++ goto out; ++ ++ bm = &ctx->x86_hvm.restore.attempted_2m; ++ bits = (ctx->restore.p2m_size >> SUPERPAGE_2MB_SHIFT) + 1; ++ if ( xc_sr_bitmap_resize(bm, bits) == false ) ++ goto out; ++ ++ bm = &ctx->x86_hvm.restore.allocated_pfns; ++ bits = ctx->restore.p2m_size + 1; ++ if ( xc_sr_bitmap_resize(bm, bits) == false ) ++ goto out; ++ ++ /* No superpage in 1st 2MB due to VGA hole */ ++ xc_sr_set_bit(0, &ctx->x86_hvm.restore.attempted_1g); ++ xc_sr_set_bit(0, &ctx->x86_hvm.restore.attempted_2m); ++ + return 0; ++ ++out: ++ ERROR("Unable to allocate memory for pfn bitmaps"); ++ return -1; + } + + /* +@@ -233,10 +258,325 @@ static int x86_hvm_stream_complete(struc + static int x86_hvm_cleanup(struct xc_sr_context *ctx) + { + free(ctx->x86_hvm.restore.context); ++ xc_sr_bitmap_free(&ctx->x86_hvm.restore.attempted_1g); ++ xc_sr_bitmap_free(&ctx->x86_hvm.restore.attempted_2m); ++ xc_sr_bitmap_free(&ctx->x86_hvm.restore.allocated_pfns); ++ ++ return 0; ++} ++ ++/* ++ * Set a pfn as allocated, expanding the tracking structures if needed. ++ */ ++static int pfn_set_allocated(struct xc_sr_context *ctx, xen_pfn_t pfn) ++{ ++ xc_interface *xch = ctx->xch; + ++ if ( !xc_sr_set_bit(pfn, &ctx->x86_hvm.restore.allocated_pfns) ) ++ { ++ ERROR("Failed to realloc allocated_pfns bitmap"); ++ errno = ENOMEM; ++ return -1; ++ } + return 0; + } + ++struct x86_hvm_sp { ++ xen_pfn_t pfn; ++ xen_pfn_t base_pfn; ++ unsigned long index; ++ unsigned long count; ++}; ++ ++/* ++ * Try to allocate a 1GB page for this pfn, but avoid Over-allocation. ++ * If this succeeds, mark the range of 2MB pages as busy. ++ */ ++static bool x86_hvm_alloc_1g(struct xc_sr_context *ctx, struct x86_hvm_sp *sp) ++{ ++ xc_interface *xch = ctx->xch; ++ struct xc_sr_bitmap *bm; ++ unsigned int order, shift; ++ int i, done; ++ xen_pfn_t extent; ++ ++ bm = &ctx->x86_hvm.restore.attempted_1g; ++ ++ /* Only one attempt to avoid overlapping allocation */ ++ if ( xc_sr_test_and_set_bit(sp->index, bm) ) ++ return false; ++ ++ order = SUPERPAGE_1GB_SHIFT; ++ sp->count = 1ULL << order; ++ ++ /* Allocate only if there is room for another superpage */ ++ if ( ctx->restore.tot_pages + sp->count > ctx->restore.max_pages ) ++ return false; ++ ++ extent = sp->base_pfn = (sp->pfn >> order) << order; ++ done = xc_domain_populate_physmap(xch, ctx->domid, 1, order, 0, &extent); ++ if ( done < 0 ) { ++ PERROR("populate_physmap failed."); ++ return false; ++ } ++ if ( done == 0 ) ++ return false; ++ ++ DPRINTF("1G base_pfn %" PRI_xen_pfn "\n", sp->base_pfn); ++ ++ /* Mark all 2MB pages as done to avoid overlapping allocation */ ++ bm = &ctx->x86_hvm.restore.attempted_2m; ++ shift = SUPERPAGE_1GB_SHIFT - SUPERPAGE_2MB_SHIFT; ++ for ( i = 0; i < (sp->count >> shift); i++ ) ++ xc_sr_set_bit((sp->base_pfn >> SUPERPAGE_2MB_SHIFT) + i, bm); ++ ++ return true; ++} ++ ++/* Allocate a 2MB page if x86_hvm_alloc_1g failed, avoid Over-allocation. */ ++static bool x86_hvm_alloc_2m(struct xc_sr_context *ctx, struct x86_hvm_sp *sp) ++{ ++ xc_interface *xch = ctx->xch; ++ struct xc_sr_bitmap *bm; ++ unsigned int order; ++ int done; ++ xen_pfn_t extent; ++ ++ bm = &ctx->x86_hvm.restore.attempted_2m; ++ ++ /* Only one attempt to avoid overlapping allocation */ ++ if ( xc_sr_test_and_set_bit(sp->index, bm) ) ++ return false; ++ ++ order = SUPERPAGE_2MB_SHIFT; ++ sp->count = 1ULL << order; ++ ++ /* Allocate only if there is room for another superpage */ ++ if ( ctx->restore.tot_pages + sp->count > ctx->restore.max_pages ) ++ return false; ++ ++ extent = sp->base_pfn = (sp->pfn >> order) << order; ++ done = xc_domain_populate_physmap(xch, ctx->domid, 1, order, 0, &extent); ++ if ( done < 0 ) { ++ PERROR("populate_physmap failed."); ++ return false; ++ } ++ if ( done == 0 ) ++ return false; ++ ++ DPRINTF("2M base_pfn %" PRI_xen_pfn "\n", sp->base_pfn); ++ return true; ++} ++ ++/* Allocate a single page if x86_hvm_alloc_2m failed. */ ++static bool x86_hvm_alloc_4k(struct xc_sr_context *ctx, struct x86_hvm_sp *sp) ++{ ++ xc_interface *xch = ctx->xch; ++ unsigned int order; ++ int done; ++ xen_pfn_t extent; ++ ++ order = 0; ++ sp->count = 1ULL << order; ++ ++ /* Allocate only if there is room for another page */ ++ if ( ctx->restore.tot_pages + sp->count > ctx->restore.max_pages ) ++ return false; ++ ++ extent = sp->base_pfn = (sp->pfn >> order) << order; ++ done = xc_domain_populate_physmap(xch, ctx->domid, 1, order, 0, &extent); ++ if ( done < 0 ) { ++ PERROR("populate_physmap failed."); ++ return false; ++ } ++ if ( done == 0 ) ++ return false; ++ ++ DPRINTF("4K base_pfn %" PRI_xen_pfn "\n", sp->base_pfn); ++ return true; ++} ++/* ++ * Attempt to allocate a superpage where the pfn resides. ++ */ ++static int x86_hvm_allocate_pfn(struct xc_sr_context *ctx, xen_pfn_t pfn) ++{ ++ xc_interface *xch = ctx->xch; ++ bool success; ++ int rc = -1; ++ unsigned long idx_1g, idx_2m; ++ struct x86_hvm_sp sp = { ++ .pfn = pfn ++ }; ++ ++ if ( xc_sr_test_bit(pfn, &ctx->x86_hvm.restore.allocated_pfns) ) ++ return 0; ++ ++ idx_1g = pfn >> SUPERPAGE_1GB_SHIFT; ++ idx_2m = pfn >> SUPERPAGE_2MB_SHIFT; ++ if ( !xc_sr_bitmap_resize(&ctx->x86_hvm.restore.attempted_1g, idx_1g) ) ++ { ++ PERROR("Failed to realloc attempted_1g"); ++ return -1; ++ } ++ if ( !xc_sr_bitmap_resize(&ctx->x86_hvm.restore.attempted_2m, idx_2m) ) ++ { ++ PERROR("Failed to realloc attempted_2m"); ++ return -1; ++ } ++ ++ sp.index = idx_1g; ++ success = x86_hvm_alloc_1g(ctx, &sp); ++ ++ if ( success == false ) { ++ sp.index = idx_2m; ++ success = x86_hvm_alloc_2m(ctx, &sp); ++ } ++ ++ if ( success == false ) { ++ sp.index = 0; ++ success = x86_hvm_alloc_4k(ctx, &sp); ++ } ++ ++ if ( success == true ) { ++ do { ++ sp.count--; ++ ctx->restore.tot_pages++; ++ rc = pfn_set_allocated(ctx, sp.base_pfn + sp.count); ++ if ( rc ) ++ break; ++ } while ( sp.count ); ++ } ++ return rc; ++} ++ ++static bool x86_hvm_punch_hole(struct xc_sr_context *ctx, xen_pfn_t max_pfn) ++{ ++ xc_interface *xch = ctx->xch; ++ struct xc_sr_bitmap *bm; ++ xen_pfn_t _pfn, pfn, min_pfn; ++ uint32_t domid, freed = 0, order; ++ int rc = -1; ++ ++ /* ++ * Scan the entire superpage because several batches will fit into ++ * a superpage, and it is unknown which pfn triggered the allocation. ++ */ ++ order = SUPERPAGE_1GB_SHIFT; ++ pfn = min_pfn = (max_pfn >> order) << order; ++ ++ while ( pfn <= max_pfn ) ++ { ++ bm = &ctx->x86_hvm.restore.allocated_pfns; ++ if ( !xc_sr_bitmap_resize(bm, pfn) ) ++ { ++ PERROR("Failed to realloc allocated_pfns %" PRI_xen_pfn, pfn); ++ return false; ++ } ++ if ( !pfn_is_populated(ctx, pfn) && ++ xc_sr_test_and_clear_bit(pfn, bm) ) { ++ domid = ctx->domid; ++ _pfn = pfn; ++ rc = xc_domain_decrease_reservation_exact(xch, domid, 1, 0, &_pfn); ++ if ( rc ) ++ { ++ PERROR("Failed to release pfn %" PRI_xen_pfn, pfn); ++ return false; ++ } ++ ctx->restore.tot_pages--; ++ freed++; ++ } ++ pfn++; ++ } ++ if ( freed ) ++ DPRINTF("freed %u between %" PRI_xen_pfn " %" PRI_xen_pfn "\n", ++ freed, min_pfn, max_pfn); ++ return true; ++} ++ ++/* ++ * Try to allocate superpages. ++ * This works without memory map only if the pfns arrive in incremental order. ++ */ ++static int x86_hvm_populate_pfns(struct xc_sr_context *ctx, unsigned count, ++ const xen_pfn_t *original_pfns, ++ const uint32_t *types) ++{ ++ xc_interface *xch = ctx->xch; ++ xen_pfn_t pfn, min_pfn = original_pfns[0], max_pfn = original_pfns[0]; ++ xen_pfn_t idx1G, idx2M; ++ unsigned i, order; ++ int rc = -1; ++ ++ /* Loop once over the array to show statistics */ ++ for ( i = 0; i < count; ++i ) ++ { ++ if ( original_pfns[i] < min_pfn ) ++ min_pfn = original_pfns[i]; ++ if ( original_pfns[i] > max_pfn ) ++ max_pfn = original_pfns[i]; ++ } ++ DPRINTF("batch of %u pfns between %" PRI_xen_pfn " %" PRI_xen_pfn "\n", ++ count, min_pfn, max_pfn); ++ ++ for ( i = 0; i < count; ++i ) ++ { ++ pfn = original_pfns[i]; ++ idx1G = pfn >> SUPERPAGE_1GB_SHIFT; ++ idx2M = pfn >> SUPERPAGE_2MB_SHIFT; ++ ++ /* ++ * If this pfn is in another 2MB superpage it is required to punch holes ++ * to release memory, starting from the 1GB boundary up to the highest ++ * pfn within the previous 2MB superpage. ++ */ ++ if ( ctx->x86_hvm.restore.idx1G_prev == idx1G && ++ ctx->x86_hvm.restore.idx2M_prev == idx2M ) ++ { ++ /* Same 2MB superpage, nothing to do */ ++ ; ++ } else { ++ /* ++ * If this next pfn is within another 1GB or 2MB superpage it is ++ * required to scan the entire previous superpage because there ++ * might be holes between the last pfn and the end of the superpage. ++ */ ++ if ( ctx->x86_hvm.restore.idx1G_prev != idx1G ) ++ { ++ order = SUPERPAGE_1GB_SHIFT; ++ max_pfn = ((ctx->x86_hvm.restore.idx1G_prev + 1) << order) - 1; ++ } ++ else ++ { ++ order = SUPERPAGE_2MB_SHIFT; ++ max_pfn = ((ctx->x86_hvm.restore.idx2M_prev + 1) << order) - 1; ++ } ++ ++ if ( x86_hvm_punch_hole(ctx, max_pfn) == false ) ++ goto err; ++ } ++ ++ if ( (types[i] != XEN_DOMCTL_PFINFO_XTAB && ++ types[i] != XEN_DOMCTL_PFINFO_BROKEN) && ++ !pfn_is_populated(ctx, pfn) ) ++ { ++ rc = x86_hvm_allocate_pfn(ctx, pfn); ++ if ( rc ) ++ goto err; ++ rc = pfn_set_populated(ctx, pfn); ++ if ( rc ) ++ goto err; ++ } ++ ctx->x86_hvm.restore.idx1G_prev = idx1G; ++ ctx->x86_hvm.restore.idx2M_prev = idx2M; ++ } ++ ++ rc = 0; ++ ++ err: ++ return rc; ++} ++ ++ + struct xc_sr_restore_ops restore_ops_x86_hvm = + { + .pfn_is_valid = x86_hvm_pfn_is_valid, +@@ -245,6 +585,7 @@ struct xc_sr_restore_ops restore_ops_x86 + .set_page_type = x86_hvm_set_page_type, + .localise_page = x86_hvm_localise_page, + .setup = x86_hvm_setup, ++ .populate_pfns = x86_hvm_populate_pfns, + .process_record = x86_hvm_process_record, + .stream_complete = x86_hvm_stream_complete, + .cleanup = x86_hvm_cleanup, +Index: xen-4.13.1-testing/tools/libxc/xc_sr_restore_x86_pv.c +=================================================================== +--- xen-4.13.1-testing.orig/tools/libxc/xc_sr_restore_x86_pv.c ++++ xen-4.13.1-testing/tools/libxc/xc_sr_restore_x86_pv.c +@@ -960,6 +960,75 @@ static void x86_pv_set_gfn(struct xc_sr_ + } + + /* ++ * 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 ++ * and all unpopulated pfns are populated. ++ */ ++static int x86_pv_populate_pfns(struct xc_sr_context *ctx, unsigned count, ++ const xen_pfn_t *original_pfns, ++ const uint32_t *types) ++{ ++ xc_interface *xch = ctx->xch; ++ xen_pfn_t *mfns = malloc(count * sizeof(*mfns)), ++ *pfns = malloc(count * sizeof(*pfns)); ++ unsigned i, nr_pfns = 0; ++ int rc = -1; ++ ++ if ( !mfns || !pfns ) ++ { ++ ERROR("Failed to allocate %zu bytes for populating the physmap", ++ 2 * count * sizeof(*mfns)); ++ goto err; ++ } ++ ++ for ( i = 0; i < count; ++i ) ++ { ++ if ( (!types || (types && ++ (types[i] != XEN_DOMCTL_PFINFO_XTAB && ++ types[i] != XEN_DOMCTL_PFINFO_BROKEN))) && ++ !pfn_is_populated(ctx, original_pfns[i]) ) ++ { ++ rc = pfn_set_populated(ctx, original_pfns[i]); ++ if ( rc ) ++ goto err; ++ pfns[nr_pfns] = mfns[nr_pfns] = original_pfns[i]; ++ ++nr_pfns; ++ } ++ } ++ ++ if ( nr_pfns ) ++ { ++ rc = xc_domain_populate_physmap_exact( ++ xch, ctx->domid, nr_pfns, 0, 0, mfns); ++ if ( rc ) ++ { ++ PERROR("Failed to populate physmap"); ++ goto err; ++ } ++ ++ for ( i = 0; i < nr_pfns; ++i ) ++ { ++ if ( mfns[i] == INVALID_MFN ) ++ { ++ ERROR("Populate physmap failed for pfn %u", i); ++ rc = -1; ++ goto err; ++ } ++ ++ ctx->restore.ops.set_gfn(ctx, pfns[i], mfns[i]); ++ } ++ } ++ ++ rc = 0; ++ ++ err: ++ free(pfns); ++ free(mfns); ++ ++ return rc; ++} ++ ++/* + * restore_ops function. Convert pfns back to mfns in pagetables. Possibly + * needs to populate new frames if a PTE is found referring to a frame which + * hasn't yet been seen from PAGE_DATA records. +@@ -1003,7 +1072,7 @@ static int x86_pv_localise_page(struct x + } + } + +- if ( to_populate && populate_pfns(ctx, to_populate, pfns, NULL) ) ++ if ( to_populate && x86_pv_populate_pfns(ctx, to_populate, pfns, NULL) ) + return -1; + + for ( i = 0; i < (PAGE_SIZE / sizeof(uint64_t)); ++i ) +@@ -1183,6 +1252,7 @@ struct xc_sr_restore_ops restore_ops_x86 + .set_gfn = x86_pv_set_gfn, + .localise_page = x86_pv_localise_page, + .setup = x86_pv_setup, ++ .populate_pfns = x86_pv_populate_pfns, + .process_record = x86_pv_process_record, + .stream_complete = x86_pv_stream_complete, + .cleanup = x86_pv_cleanup, -- cgit v1.2.3