RISC-V uses the same memory-hotplug teardown code for the linear map and vmemmap, although their page-table pages are not always allocated in the same way. Late linear-map allocations run page-table constructors, while vmemmap and early allocations may provide constructor-free pages. The PTE path unconditionally runs the destructor, which is wrong for constructor-free vmemmap tables. The PMD path avoids that problem by using is_vmemmap as a proxy for constructor state, but that assumption will no longer hold once runtime vmemmap allocations use the normal pgalloc helpers. Page-table constructors record their state in PG_table. Centralize page-table freeing and use PageTable() to decide whether the destructor is required. Keep reserved and constructor-free pages on their existing freeing paths. Fixes: c75a74f4ba19 ("riscv: mm: Add memory hotplugging support") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Muchun Song --- arch/riscv/mm/init.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 857f9a55039c..429a0b015ec1 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -1486,10 +1486,19 @@ struct execmem_info __init *execmem_arch_setup(void) #endif /* CONFIG_EXECMEM */ #ifdef CONFIG_MEMORY_HOTPLUG +static void __meminit free_pagetable(struct page *page) +{ + if (PageReserved(page)) + free_reserved_page(page); + else if (PageTable(page)) + pagetable_dtor_free(page_ptdesc(page)); + else + pagetable_free(page_ptdesc(page)); +} + static void __meminit free_pte_table(pte_t *pte_start, pmd_t *pmd) { struct page *page = pmd_page(*pmd); - struct ptdesc *ptdesc = page_ptdesc(page); pte_t *pte; int i; @@ -1499,18 +1508,13 @@ static void __meminit free_pte_table(pte_t *pte_start, pmd_t *pmd) return; } - pagetable_dtor(ptdesc); - if (PageReserved(page)) - free_reserved_page(page); - else - pagetable_free(ptdesc); + free_pagetable(page); pmd_clear(pmd); } -static void __meminit free_pmd_table(pmd_t *pmd_start, pud_t *pud, bool is_vmemmap) +static void __meminit free_pmd_table(pmd_t *pmd_start, pud_t *pud) { struct page *page = pud_page(*pud); - struct ptdesc *ptdesc = page_ptdesc(page); pmd_t *pmd; int i; @@ -1520,12 +1524,7 @@ static void __meminit free_pmd_table(pmd_t *pmd_start, pud_t *pud, bool is_vmemm return; } - if (!is_vmemmap) - pagetable_dtor(ptdesc); - if (PageReserved(page)) - free_reserved_page(page); - else - pagetable_free(ptdesc); + free_pagetable(page); pud_clear(pud); } @@ -1645,7 +1644,7 @@ static void __meminit remove_pud_mapping(pud_t *pud_base, unsigned long addr, un remove_pmd_mapping(pmd_base, addr, next, is_vmemmap, altmap); if (pgtable_l4_enabled) - free_pmd_table(pmd_base, pudp, is_vmemmap); + free_pmd_table(pmd_base, pudp); } } -- 2.54.0