From a9730e49531c299c80a2cb8bad959dbbfcf46211 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Thu, 26 Mar 2026 11:11:09 +0100 Subject: [PATCH] Revert "xen/mm: allow deferred scrub of physmap populate allocated pages" This reverts commit b304f948427218421af24abcb04b40d3c34fefbf and its prereq 7e10bb1501c0289988d27772823c5eaf52f1b6a5 ("xen/mm: remove aliasing of PGC_need_scrub over PGC_allocated"). Further fixes are needed before this can be considered for backporting. --- xen/arch/arm/include/asm/mm.h | 10 ++- xen/arch/ppc/include/asm/mm.h | 10 ++- xen/arch/riscv/include/asm/mm.h | 10 ++- xen/arch/x86/include/asm/mm.h | 18 +++--- xen/common/domain.c | 25 -------- xen/common/memory.c | 105 +------------------------------- xen/common/page_alloc.c | 4 +- xen/include/xen/mm.h | 11 ---- xen/include/xen/sched.h | 5 -- 9 files changed, 35 insertions(+), 163 deletions(-) diff --git a/xen/arch/arm/include/asm/mm.h b/xen/arch/arm/include/asm/mm.h index efbe19aba4..a0d8e5afe9 100644 --- a/xen/arch/arm/include/asm/mm.h +++ b/xen/arch/arm/include/asm/mm.h @@ -150,9 +150,6 @@ struct page_info #define _PGC_colored PG_shift(4) #define PGC_colored PG_mask(1, 4) #endif -/* Page needs to be scrubbed. */ -#define _PGC_need_scrub PG_shift(5) -#define PGC_need_scrub PG_mask(1, 5) /* ... */ /* Page is broken? */ #define _PGC_broken PG_shift(7) @@ -172,6 +169,13 @@ struct page_info #define PGC_count_width PG_shift(10) #define PGC_count_mask ((1UL<count_info & PGC_xen_heap) #define is_xen_heap_mfn(mfn) \ (mfn_valid(mfn) && is_xen_heap_page(mfn_to_page(mfn))) diff --git a/xen/arch/riscv/include/asm/mm.h b/xen/arch/riscv/include/asm/mm.h index 70849ce2e6..292aa48fc1 100644 --- a/xen/arch/riscv/include/asm/mm.h +++ b/xen/arch/riscv/include/asm/mm.h @@ -221,15 +221,19 @@ static inline bool arch_mfns_in_directmap(unsigned long mfn, unsigned long nr) #define PGT_count_width PG_shift(2) #define PGT_count_mask ((1UL << PGT_count_width) - 1) +/* + * Page needs to be scrubbed. Since this bit can only be set on a page that is + * free (i.e. in PGC_state_free) we can reuse PGC_allocated bit. + */ +#define _PGC_need_scrub _PGC_allocated +#define PGC_need_scrub PGC_allocated + /* Cleared when the owning guest 'frees' this page. */ #define _PGC_allocated PG_shift(1) #define PGC_allocated PG_mask(1, 1) /* Page is Xen heap? */ #define _PGC_xen_heap PG_shift(2) #define PGC_xen_heap PG_mask(1, 2) -/* Page needs to be scrubbed. */ -#define _PGC_need_scrub PG_shift(4) -#define PGC_need_scrub PG_mask(1, 4) /* Page is broken? */ #define _PGC_broken PG_shift(7) #define PGC_broken PG_mask(1, 7) diff --git a/xen/arch/x86/include/asm/mm.h b/xen/arch/x86/include/asm/mm.h index 30e993baba..6c7e66ee21 100644 --- a/xen/arch/x86/include/asm/mm.h +++ b/xen/arch/x86/include/asm/mm.h @@ -83,25 +83,29 @@ #define PGC_state_offlined PG_mask(2, 6) #define PGC_state_free PG_mask(3, 6) #define page_state_is(pg, st) (((pg)->count_info&PGC_state) == PGC_state_##st) -/* Page needs to be scrubbed. */ -#define _PGC_need_scrub PG_shift(7) -#define PGC_need_scrub PG_mask(1, 7) #ifdef CONFIG_SHADOW_PAGING /* Set when a page table page has been shadowed. */ -#define _PGC_shadowed_pt PG_shift(8) -#define PGC_shadowed_pt PG_mask(1, 8) +#define _PGC_shadowed_pt PG_shift(7) +#define PGC_shadowed_pt PG_mask(1, 7) #else #define PGC_shadowed_pt 0 #endif /* Count of references to this frame. */ #if PGC_shadowed_pt -#define PGC_count_width PG_shift(8) -#else #define PGC_count_width PG_shift(7) +#else +#define PGC_count_width PG_shift(6) #endif #define PGC_count_mask ((1UL<page_alloc_lock); - if ( d->pending_scrub ) - { - FREE_DOMHEAP_PAGES(d->pending_scrub, d->pending_scrub_order); - d->pending_scrub_order = 0; - d->pending_scrub_index = 0; - } - rspin_unlock(&d->page_alloc_lock); -} - /* * Release resources held by a domain. There may or may not be live * references to the domain, and it may or may not be fully constructed. @@ -517,9 +505,6 @@ static int domain_teardown(struct domain *d) case PROG_none: BUILD_BUG_ON(PROG_none != 0); - /* Trivial teardown, not long-running enough to need a preemption check. */ - domain_pending_scrub_free(d); - PROGRESS(gnttab_mappings): rc = gnttab_release_mappings(d); if ( rc ) @@ -562,7 +547,6 @@ static void _domain_destroy(struct domain *d) { BUG_ON(!d->is_dying); BUG_ON(atomic_read(&d->refcnt) != DOMAIN_DESTROYED); - ASSERT(!d->pending_scrub); xfree(d->pbuf); @@ -1494,15 +1478,6 @@ int domain_unpause_by_systemcontroller(struct domain *d) */ if ( new == 0 && !d->creation_finished ) { - if ( d->pending_scrub ) - { - printk(XENLOG_ERR - "%pd: cannot be started with pending unscrubbed pages, destroying\n", - d); - domain_crash(d); - domain_pending_scrub_free(d); - return -EBUSY; - } d->creation_finished = true; arch_domain_creation_finished(d); } diff --git a/xen/common/memory.c b/xen/common/memory.c index 00ba0d61dc..c78f0dcd0a 100644 --- a/xen/common/memory.c +++ b/xen/common/memory.c @@ -157,73 +157,6 @@ static void increase_reservation(struct memop_args *a) a->nr_done = i; } -/* - * Temporary storage for a domain assigned page that's not been fully scrubbed. - * Stored pages must be domheap ones. - * - * The stashed page can be freed at any time by Xen, the caller must pass the - * order and NUMA node requirement to the fetch function to ensure the - * currently stashed page matches it's requirements. - */ -static void stash_allocation(struct domain *d, struct page_info *page, - unsigned int order, unsigned int scrub_index) -{ - rspin_lock(&d->page_alloc_lock); - - /* - * Drop the passed page in preference for the already stashed one. This - * interface is designed to be used for single-threaded domain creation. - */ - if ( d->pending_scrub || d->is_dying ) - free_domheap_pages(page, order); - else - { - d->pending_scrub_index = scrub_index; - d->pending_scrub_order = order; - d->pending_scrub = page; - } - - rspin_unlock(&d->page_alloc_lock); -} - -static struct page_info *get_stashed_allocation(struct domain *d, - unsigned int order, - nodeid_t node, - unsigned int *scrub_index) -{ - struct page_info *page = NULL; - - rspin_lock(&d->page_alloc_lock); - - /* - * If there's a pending page to scrub check if it satisfies the current - * request. If it doesn't free it and return NULL. - */ - if ( d->pending_scrub ) - { - if ( d->pending_scrub_order == order && - (node == NUMA_NO_NODE || node == page_to_nid(d->pending_scrub)) ) - { - page = d->pending_scrub; - *scrub_index = d->pending_scrub_index; - } - else - free_domheap_pages(d->pending_scrub, d->pending_scrub_order); - - /* - * The caller now owns the page or it has been freed, clear stashed - * information. Prevent concurrent usages of get_stashed_allocation() - * from returning the same page to different contexts. - */ - d->pending_scrub_index = 0; - d->pending_scrub_order = 0; - d->pending_scrub = NULL; - } - - rspin_unlock(&d->page_alloc_lock); - return page; -} - static void populate_physmap(struct memop_args *a) { struct page_info *page; @@ -340,19 +273,7 @@ static void populate_physmap(struct memop_args *a) } else { - unsigned int scrub_start = 0; - unsigned int memflags = - a->memflags | (d->creation_finished ? 0 - : MEMF_no_scrub); - nodeid_t node = - (a->memflags & MEMF_exact_node) ? MEMF_get_node(a->memflags) - : NUMA_NO_NODE; - - page = get_stashed_allocation(d, a->extent_order, node, - &scrub_start); - - if ( !page ) - page = alloc_domheap_pages(d, a->extent_order, memflags); + page = alloc_domheap_pages(d, a->extent_order, a->memflags); if ( unlikely(!page) ) { @@ -363,30 +284,6 @@ static void populate_physmap(struct memop_args *a) goto out; } - if ( memflags & MEMF_no_scrub ) - { - unsigned int dirty_cnt = 0; - - /* Check if there's anything to scrub. */ - for ( j = scrub_start; j < (1U << a->extent_order); j++ ) - { - if ( !test_and_clear_bit(_PGC_need_scrub, - &page[j].count_info) ) - continue; - - scrub_one_page(&page[j]); - - if ( (j + 1) != (1U << a->extent_order) && - !(++dirty_cnt & 0xff) && - hypercall_preempt_check() ) - { - a->preempted = 1; - stash_allocation(d, page, a->extent_order, j + 1); - goto out; - } - } - } - if ( unlikely(a->memflags & MEMF_no_tlbflush) ) { for ( j = 0; j < (1U << a->extent_order); j++ ) diff --git a/xen/common/page_alloc.c b/xen/common/page_alloc.c index cab35bc39c..6919722e9a 100644 --- a/xen/common/page_alloc.c +++ b/xen/common/page_alloc.c @@ -169,7 +169,7 @@ /* * Flags that are preserved in assign_pages() (and only there) */ -#define PGC_preserved (PGC_extra | PGC_static | PGC_colored | PGC_need_scrub) +#define PGC_preserved (PGC_extra | PGC_static | PGC_colored) #ifndef PGT_TYPE_INFO_INITIALIZER #define PGT_TYPE_INFO_INITIALIZER 0 @@ -764,7 +764,7 @@ static void page_list_add_scrub(struct page_info *pg, unsigned int node, #endif #define SCRUB_BYTE_PATTERN (SCRUB_PATTERN & 0xff) -void scrub_one_page(const struct page_info *pg) +static void scrub_one_page(const struct page_info *pg) { if ( unlikely(pg->count_info & PGC_broken) ) return; diff --git a/xen/include/xen/mm.h b/xen/include/xen/mm.h index e8f4a2fba3..16f733281a 100644 --- a/xen/include/xen/mm.h +++ b/xen/include/xen/mm.h @@ -144,17 +144,6 @@ unsigned long avail_domheap_pages(void); unsigned long avail_node_heap_pages(unsigned int nodeid); #define alloc_domheap_page(d,f) (alloc_domheap_pages(d,0,f)) #define free_domheap_page(p) (free_domheap_pages(p,0)) - -/* Free an allocation, and zero the pointer to it. */ -#define FREE_DOMHEAP_PAGES(p, o) do { \ - void *_ptr_ = (p); \ - (p) = NULL; \ - free_domheap_pages(_ptr_, o); \ -} while ( false ) -#define FREE_DOMHEAP_PAGE(p) FREE_DOMHEAP_PAGES(p, 0) - -void scrub_one_page(const struct page_info *pg); - unsigned int online_page(mfn_t mfn, uint32_t *status); int offline_page(mfn_t mfn, int broken, uint32_t *status); int query_page_offline(mfn_t mfn, uint32_t *status); diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h index e664221184..de621eb123 100644 --- a/xen/include/xen/sched.h +++ b/xen/include/xen/sched.h @@ -646,11 +646,6 @@ struct domain unsigned int num_llc_colors; const unsigned int *llc_colors; #endif - - /* Pointer to allocated domheap page that possibly needs scrubbing. */ - struct page_info *pending_scrub; - unsigned int pending_scrub_order; - unsigned int pending_scrub_index; }; static inline struct page_list_head *page_to_list(