Commit c594b83457cc ("arm64: mm: call pagetable dtor when freeing hot-removed page tables") made free_hotplug_pgtable_page() unconditionally run the page-table destructor. This matches page tables allocated by the arm64 mapping code, which runs the corresponding constructors. However, arm64 also uses the generic sparse-vmemmap population code. Runtime intermediate page tables allocated by that code do not run a page-table constructor. Freeing one during memory hot-remove therefore runs a destructor without a matching constructor and corrupts NR_PAGETABLE accounting. Free intermediate page-table pages through pagetable_free(). It runs the destructor only for constructor-backed pages and then releases the page through the page-table freeing path. Fixes: c594b83457cc ("arm64: mm: call pagetable dtor when freeing hot-removed page tables") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Muchun Song Reviewed-by: Kevin Brodsky Acked-by: David Hildenbrand (Arm) --- v3: - Collect Acked-by from David Hildenbrand - Collect Reviewed-by from Kevin Brodsky v2: - Free intermediate tables through pagetable_free() (suggested by David Hildenbrand) - Update the commit message for the page-table freeing path --- arch/arm64/mm/mmu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 79d90226fd5d..4dce8fa227c3 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -1495,8 +1495,7 @@ static void free_hotplug_page_range(struct page *page, size_t size, static void free_hotplug_pgtable_page(struct page *page) { - pagetable_dtor(page_ptdesc(page)); - free_hotplug_page_range(page, PAGE_SIZE, NULL); + pagetable_free(page_ptdesc(page)); } static bool pgtable_range_aligned(unsigned long start, unsigned long end, -- 2.54.0