* [PATCH v2 0/4] mm: Fix page-table teardown during memory hot-remove
@ 2026-10-10 11:24 Muchun Song
2026-10-10 11:24 ` [PATCH v2 1/4] mm: fix missing page-table destructor in pagetable_free() Muchun Song
` (3 more replies)
0 siblings, 4 replies; 15+ messages in thread
From: Muchun Song @ 2026-10-10 11:24 UTC (permalink / raw)
To: Andrew Morton
Cc: David Hildenbrand, Oscar Salvador, Dave Hansen, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Björn Töpel, Catalin Marinas, Will Deacon,
Mark Rutland, Kevin Brodsky, Alexander Gordeev, Alistair Popple,
linux-mm, linux-riscv, linux-arm-kernel, linux-kernel, stable,
Muchun Song, Muchun Song
Memory hot-remove can free page-table pages allocated through paths with
different constructor state. Some paths run the page-table constructor,
while generic sparse-vmemmap population may not. The x86, RISC-V, and
arm64 teardown paths did not handle that distinction consistently,
leaving constructor/destructor accounting unbalanced.
Make pagetable_free() test PageTable() and run the destructor only when
the constructor initialized the page. The RISC-V and arm64 fixes then
use this common helper for their memory-hot-remove paths.
The architecture fixes depend on the common MM change in patch 1, so
this remains a single series. I expect it to go through the MM tree,
with review or acks from the relevant architecture maintainers.
Changes since v1:
- Move the destructor decision from x86 into pagetable_free(), as
suggested by David Hildenbrand.
- Use pagetable_free() for non-reserved RISC-V page-table pages and
simplify reserved-page handling.
- Collect David Hildenbrand's Acked-by on the RISC-V PUD fix.
- Use pagetable_free() for arm64 intermediate page-table pages, as
suggested by David Hildenbrand.
- Trim and update the commit messages, as requested by Dave Hansen.
Muchun Song (4):
mm: fix missing page-table destructor in pagetable_free()
riscv/mm: fix hotplug page-table destructor handling
riscv/mm: fix missing destructor for hotplug PUD tables
arm64/mm: fix destructor for unconstructed hotplug page tables
arch/arm64/mm/mmu.c | 3 +--
arch/riscv/mm/init.c | 32 +++++++++++++-------------------
include/linux/mm.h | 37 ++++++++++++++++++++-----------------
3 files changed, 34 insertions(+), 38 deletions(-)
base-commit: 8b38ed9ab5b09c8ba168cbcc49524e9b380ee5c4
--
2.54.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 1/4] mm: fix missing page-table destructor in pagetable_free()
2026-10-10 11:24 [PATCH v2 0/4] mm: Fix page-table teardown during memory hot-remove Muchun Song
@ 2026-10-10 11:24 ` Muchun Song
2026-10-12 11:15 ` Jose A. Perez de Azpillaga
` (2 more replies)
2026-10-10 11:24 ` [PATCH v2 2/4] riscv/mm: fix hotplug page-table destructor handling Muchun Song
` (2 subsequent siblings)
3 siblings, 3 replies; 15+ messages in thread
From: Muchun Song @ 2026-10-10 11:24 UTC (permalink / raw)
To: Andrew Morton
Cc: David Hildenbrand, Oscar Salvador, Dave Hansen, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Björn Töpel, Catalin Marinas, Will Deacon,
Mark Rutland, Kevin Brodsky, Alexander Gordeev, Alistair Popple,
linux-mm, linux-riscv, linux-arm-kernel, linux-kernel, stable,
Muchun Song, Muchun Song
Since commit 49f599666420 ("mm: call ctor/dtor for kernel PTEs"),
pte_alloc_one_kernel() runs the page-table constructor for kernel PTE
tables.
HVO (HugeTLB Vmemmap Optimization) uses pte_alloc_one_kernel() when
splitting a PMD. Restoring the vmemmap backing pages does not collapse
the PTE table, so a later memory hot-remove eventually frees it through
pagetable_free() on x86-64. That releases the page without running the
matching destructor and leaves NR_PAGETABLE elevated.
Make pagetable_free() detect constructor-backed pages and run the
destructor before releasing their memory to fix the problem.
Fixes: 49f599666420 ("mm: call ctor/dtor for kernel PTEs")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
v2:
- Move destructor handling from x86 into pagetable_free() (suggested by
David Hildenbrand)
- Trim and update the commit message (requested by Dave Hansen)
---
include/linux/mm.h | 37 ++++++++++++++++++++-----------------
1 file changed, 20 insertions(+), 17 deletions(-)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 5cca90fc1df6..771a7d60b0a5 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -3969,23 +3969,6 @@ static inline void pagetable_free_kernel(struct ptdesc *pt)
__pagetable_free(pt);
}
#endif
-/**
- * pagetable_free - Free pagetables
- * @pt: The page table descriptor
- *
- * pagetable_free frees the memory of all page tables described by a page
- * table descriptor and the memory for the descriptor itself.
- */
-static inline void pagetable_free(struct ptdesc *pt)
-{
- if (ptdesc_test_kernel(pt)) {
- ptdesc_clear_kernel(pt);
- pagetable_free_kernel(pt);
- } else {
- __pagetable_free(pt);
- }
-}
-
#if defined(CONFIG_SPLIT_PTE_PTLOCKS)
#if ALLOC_SPLIT_PTLOCKS
void __init ptlock_cache_init(void);
@@ -4078,6 +4061,26 @@ static inline void pagetable_dtor(struct ptdesc *ptdesc)
lruvec_stat_sub_folio(folio, NR_PAGETABLE);
}
+/**
+ * pagetable_free - Free pagetables
+ * @pt: The page table descriptor
+ *
+ * pagetable_free frees the memory of all page tables described by a page
+ * table descriptor and the memory for the descriptor itself.
+ */
+static inline void pagetable_free(struct ptdesc *pt)
+{
+ if (PageTable(ptdesc_page(pt)))
+ pagetable_dtor(pt);
+
+ if (ptdesc_test_kernel(pt)) {
+ ptdesc_clear_kernel(pt);
+ pagetable_free_kernel(pt);
+ } else {
+ __pagetable_free(pt);
+ }
+}
+
static inline void pagetable_dtor_free(struct ptdesc *ptdesc)
{
pagetable_dtor(ptdesc);
--
2.54.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 2/4] riscv/mm: fix hotplug page-table destructor handling
2026-10-10 11:24 [PATCH v2 0/4] mm: Fix page-table teardown during memory hot-remove Muchun Song
2026-10-10 11:24 ` [PATCH v2 1/4] mm: fix missing page-table destructor in pagetable_free() Muchun Song
@ 2026-10-10 11:24 ` Muchun Song
2026-10-12 13:28 ` David Hildenbrand (Arm)
2026-10-12 15:32 ` Björn Töpel
2026-10-10 11:24 ` [PATCH v2 3/4] riscv/mm: fix missing destructor for hotplug PUD tables Muchun Song
2026-10-10 11:24 ` [PATCH v2 4/4] arm64/mm: fix destructor for unconstructed hotplug page tables Muchun Song
3 siblings, 2 replies; 15+ messages in thread
From: Muchun Song @ 2026-10-10 11:24 UTC (permalink / raw)
To: Andrew Morton
Cc: David Hildenbrand, Oscar Salvador, Dave Hansen, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Björn Töpel, Catalin Marinas, Will Deacon,
Mark Rutland, Kevin Brodsky, Alexander Gordeev, Alistair Popple,
linux-mm, linux-riscv, linux-arm-kernel, linux-kernel, stable,
Muchun Song, Muchun Song
RISC-V uses the same memory-hotplug teardown code for linear-map and
vmemmap page tables. Late linear-map allocations run page-table
constructors, while vmemmap and early allocations may not.
free_pte_table() unconditionally calls pagetable_dtor(), corrupting
NR_PAGETABLE accounting for constructor-free tables. free_pmd_table()
avoids that for vmemmap using is_vmemmap, but mapping type is not a
reliable proxy for constructor state. In particular, it becomes invalid
once runtime vmemmap tables use the normal pgalloc helpers.
Introduce free_pgtable_page() to select free_reserved_page() for reserved
pages and pagetable_free() otherwise. The latter runs the destructor only
when PageTable() is set, handling both constructor-backed and
constructor-free tables.
Fixes: c75a74f4ba19 ("riscv: mm: Add memory hotplugging support")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
v2:
- Use pagetable_free() for non-reserved tables (suggested by David
Hildenbrand)
- Simplify reserved-page handling
---
arch/riscv/mm/init.c | 27 ++++++++++++---------------
1 file changed, 12 insertions(+), 15 deletions(-)
diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c
index 857f9a55039c..151eb197cb09 100644
--- a/arch/riscv/mm/init.c
+++ b/arch/riscv/mm/init.c
@@ -1486,10 +1486,17 @@ struct execmem_info __init *execmem_arch_setup(void)
#endif /* CONFIG_EXECMEM */
#ifdef CONFIG_MEMORY_HOTPLUG
+static void __meminit free_pgtable_page(struct page *page)
+{
+ if (PageReserved(page))
+ free_reserved_page(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 +1506,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_pgtable_page(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 +1522,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_pgtable_page(page);
pud_clear(pud);
}
@@ -1645,7 +1642,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
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 3/4] riscv/mm: fix missing destructor for hotplug PUD tables
2026-10-10 11:24 [PATCH v2 0/4] mm: Fix page-table teardown during memory hot-remove Muchun Song
2026-10-10 11:24 ` [PATCH v2 1/4] mm: fix missing page-table destructor in pagetable_free() Muchun Song
2026-10-10 11:24 ` [PATCH v2 2/4] riscv/mm: fix hotplug page-table destructor handling Muchun Song
@ 2026-10-10 11:24 ` Muchun Song
2026-10-12 15:35 ` Björn Töpel
2026-10-10 11:24 ` [PATCH v2 4/4] arm64/mm: fix destructor for unconstructed hotplug page tables Muchun Song
3 siblings, 1 reply; 15+ messages in thread
From: Muchun Song @ 2026-10-10 11:24 UTC (permalink / raw)
To: Andrew Morton
Cc: David Hildenbrand, Oscar Salvador, Dave Hansen, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Björn Töpel, Catalin Marinas, Will Deacon,
Mark Rutland, Kevin Brodsky, Alexander Gordeev, Alistair Popple,
linux-mm, linux-riscv, linux-arm-kernel, linux-kernel, stable,
Muchun Song, Muchun Song
Commit 8472cc4503eb ("riscv: mm: call PUD/P4D ctor in special kernel
pgtable alloc") made alloc_pud_late() run the PUD page-table constructor.
However, free_pud_table() still frees non-reserved PUD tables directly
without decrementing NR_PAGETABLE.
Free PUD tables through the common free_pgtable_page() helper so that
constructor-backed tables run the matching destructor before being
freed.
Fixes: 8472cc4503eb ("riscv: mm: call PUD/P4D ctor in special kernel pgtable alloc")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
---
v2:
- Collect Acked-by from David Hildenbrand
---
arch/riscv/mm/init.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c
index 151eb197cb09..4914864cb350 100644
--- a/arch/riscv/mm/init.c
+++ b/arch/riscv/mm/init.c
@@ -1538,10 +1538,7 @@ static void __meminit free_pud_table(pud_t *pud_start, p4d_t *p4d)
return;
}
- if (PageReserved(page))
- free_reserved_page(page);
- else
- __free_pages(page, 0);
+ free_pgtable_page(page);
p4d_clear(p4d);
}
--
2.54.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 4/4] arm64/mm: fix destructor for unconstructed hotplug page tables
2026-10-10 11:24 [PATCH v2 0/4] mm: Fix page-table teardown during memory hot-remove Muchun Song
` (2 preceding siblings ...)
2026-10-10 11:24 ` [PATCH v2 3/4] riscv/mm: fix missing destructor for hotplug PUD tables Muchun Song
@ 2026-10-10 11:24 ` Muchun Song
2026-10-12 13:30 ` David Hildenbrand (Arm)
3 siblings, 1 reply; 15+ messages in thread
From: Muchun Song @ 2026-10-10 11:24 UTC (permalink / raw)
To: Andrew Morton
Cc: David Hildenbrand, Oscar Salvador, Dave Hansen, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Björn Töpel, Catalin Marinas, Will Deacon,
Mark Rutland, Kevin Brodsky, Alexander Gordeev, Alistair Popple,
linux-mm, linux-riscv, linux-arm-kernel, linux-kernel, stable,
Muchun Song, Muchun Song
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 <songmuchun@bytedance.com>
---
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 7343ac9294f8..26a8c665ae52 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
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2 1/4] mm: fix missing page-table destructor in pagetable_free()
2026-10-10 11:24 ` [PATCH v2 1/4] mm: fix missing page-table destructor in pagetable_free() Muchun Song
@ 2026-10-12 11:15 ` Jose A. Perez de Azpillaga
2026-10-12 12:55 ` Muchun Song
2026-10-12 13:27 ` David Hildenbrand (Arm)
2026-10-12 14:06 ` Dave Hansen
2 siblings, 1 reply; 15+ messages in thread
From: Jose A. Perez de Azpillaga @ 2026-10-12 11:15 UTC (permalink / raw)
To: Muchun Song
Cc: Andrew Morton, David Hildenbrand, Oscar Salvador, Dave Hansen,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Björn Töpel, Catalin Marinas, Will Deacon,
Mark Rutland, Kevin Brodsky, Alexander Gordeev, Alistair Popple,
linux-mm, linux-riscv, linux-arm-kernel, linux-kernel, stable,
Muchun Song
On Sat, Oct 10, 2026 at 07:24:54PM +0800, Muchun Song wrote:
> Since commit 49f599666420 ("mm: call ctor/dtor for kernel PTEs"),
> pte_alloc_one_kernel() runs the page-table constructor for kernel PTE
> tables.
>
> HVO (HugeTLB Vmemmap Optimization) uses pte_alloc_one_kernel() when
> splitting a PMD. Restoring the vmemmap backing pages does not collapse
> the PTE table, so a later memory hot-remove eventually frees it through
> pagetable_free() on x86-64. That releases the page without running the
> matching destructor and leaves NR_PAGETABLE elevated.
>
> Make pagetable_free() detect constructor-backed pages and run the
> destructor before releasing their memory to fix the problem.
>
> Fixes: 49f599666420 ("mm: call ctor/dtor for kernel PTEs")
> Cc: stable@vger.kernel.org
is the x86 case covered for 6.16-6.18? as far as I can see, x86
free_pagetable() only uses pagetable_free() since bf9e4e30f353
("x86/mm: use pagetable_free()") in v6.19, so the backport wouldn't
change anything there. or am I missing something?
> Assisted-by: LLM
> Signed-off-by: Muchun Song <songmuchun@bytedance.com>
> ---
> v2:
> - Move destructor handling from x86 into pagetable_free() (suggested by
> David Hildenbrand)
> - Trim and update the commit message (requested by Dave Hansen)
> ---
> include/linux/mm.h | 37 ++++++++++++++++++++-----------------
> 1 file changed, 20 insertions(+), 17 deletions(-)
>
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index 5cca90fc1df6..771a7d60b0a5 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -3969,23 +3969,6 @@ static inline void pagetable_free_kernel(struct ptdesc *pt)
> __pagetable_free(pt);
> }
> #endif
> -/**
> - * pagetable_free - Free pagetables
> - * @pt: The page table descriptor
> - *
> - * pagetable_free frees the memory of all page tables described by a page
> - * table descriptor and the memory for the descriptor itself.
> - */
> -static inline void pagetable_free(struct ptdesc *pt)
> -{
> - if (ptdesc_test_kernel(pt)) {
> - ptdesc_clear_kernel(pt);
> - pagetable_free_kernel(pt);
> - } else {
> - __pagetable_free(pt);
> - }
> -}
> -
> #if defined(CONFIG_SPLIT_PTE_PTLOCKS)
> #if ALLOC_SPLIT_PTLOCKS
> void __init ptlock_cache_init(void);
> @@ -4078,6 +4061,26 @@ static inline void pagetable_dtor(struct ptdesc *ptdesc)
> lruvec_stat_sub_folio(folio, NR_PAGETABLE);
> }
>
> +/**
> + * pagetable_free - Free pagetables
> + * @pt: The page table descriptor
> + *
> + * pagetable_free frees the memory of all page tables described by a page
> + * table descriptor and the memory for the descriptor itself.
> + */
> +static inline void pagetable_free(struct ptdesc *pt)
> +{
> + if (PageTable(ptdesc_page(pt)))
> + pagetable_dtor(pt);
> +
> + if (ptdesc_test_kernel(pt)) {
> + ptdesc_clear_kernel(pt);
> + pagetable_free_kernel(pt);
> + } else {
> + __pagetable_free(pt);
> + }
> +}
> +
> static inline void pagetable_dtor_free(struct ptdesc *ptdesc)
> {
> pagetable_dtor(ptdesc);
> --
> 2.54.0
>
>
--
cheers,
jose a. p-a
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2 1/4] mm: fix missing page-table destructor in pagetable_free()
2026-10-12 11:15 ` Jose A. Perez de Azpillaga
@ 2026-10-12 12:55 ` Muchun Song
2026-10-12 17:21 ` Jose A. Perez de Azpillaga
0 siblings, 1 reply; 15+ messages in thread
From: Muchun Song @ 2026-10-12 12:55 UTC (permalink / raw)
To: Jose A. Perez de Azpillaga, Andrew Morton
Cc: Muchun Song, David Hildenbrand, Oscar Salvador, Dave Hansen,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Björn Töpel, Catalin Marinas, Will Deacon,
Mark Rutland, Kevin Brodsky, Alexander Gordeev, Alistair Popple,
linux-mm, linux-riscv, linux-arm-kernel, linux-kernel, stable
> On Oct 12, 2026, at 19:15, Jose A. Perez de Azpillaga <azpijr@gmail.com> wrote:
>
> On Sat, Oct 10, 2026 at 07:24:54PM +0800, Muchun Song wrote:
>> Since commit 49f599666420 ("mm: call ctor/dtor for kernel PTEs"),
>> pte_alloc_one_kernel() runs the page-table constructor for kernel PTE
>> tables.
>>
>> HVO (HugeTLB Vmemmap Optimization) uses pte_alloc_one_kernel() when
>> splitting a PMD. Restoring the vmemmap backing pages does not collapse
>> the PTE table, so a later memory hot-remove eventually frees it through
>> pagetable_free() on x86-64. That releases the page without running the
>> matching destructor and leaves NR_PAGETABLE elevated.
>>
>> Make pagetable_free() detect constructor-backed pages and run the
>> destructor before releasing their memory to fix the problem.
>>
>> Fixes: 49f599666420 ("mm: call ctor/dtor for kernel PTEs")
>> Cc: stable@vger.kernel.org
>
> is the x86 case covered for 6.16-6.18? as far as I can see, x86
> free_pagetable() only uses pagetable_free() since bf9e4e30f353
> ("x86/mm: use pagetable_free()") in v6.19, so the backport wouldn't
> change anything there. or am I missing something?
Yes, you are absolutely right — thanks for catching this.
I double-checked and indeed x86's free_pagetable() only starts using
pagetable_free() since bf9e4e30f353 ("x86/mm: use pagetable_free()"),
which landed in v6.19. So for 6.16-6.18 the prerequisite is not present
and the backport would be a no-op there.
The correct trailer is:
Cc: <stable@vger.kernel.org> # 6.16-6.18: bf9e4e30f353: x86/mm: use pagetable_free()
Andrew, would you be able to update the trailer in the patch when you
pick it up? If there are further review comments that require code
changes, I'll fix this trailer as part of the v3 respin. Thanks a lot!
Thanks,
Muchun
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2 1/4] mm: fix missing page-table destructor in pagetable_free()
2026-10-10 11:24 ` [PATCH v2 1/4] mm: fix missing page-table destructor in pagetable_free() Muchun Song
2026-10-12 11:15 ` Jose A. Perez de Azpillaga
@ 2026-10-12 13:27 ` David Hildenbrand (Arm)
2026-10-12 13:29 ` Muchun Song
2026-10-12 14:06 ` Dave Hansen
2 siblings, 1 reply; 15+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-12 13:27 UTC (permalink / raw)
To: Muchun Song, Andrew Morton
Cc: Oscar Salvador, Dave Hansen, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Björn Töpel,
Catalin Marinas, Will Deacon, Mark Rutland, Kevin Brodsky,
Alexander Gordeev, Alistair Popple, linux-mm, linux-riscv,
linux-arm-kernel, linux-kernel, stable, Muchun Song
On 10/10/26 13:24, Muchun Song wrote:
> Since commit 49f599666420 ("mm: call ctor/dtor for kernel PTEs"),
> pte_alloc_one_kernel() runs the page-table constructor for kernel PTE
> tables.
>
> HVO (HugeTLB Vmemmap Optimization) uses pte_alloc_one_kernel() when
> splitting a PMD. Restoring the vmemmap backing pages does not collapse
> the PTE table, so a later memory hot-remove eventually frees it through
> pagetable_free() on x86-64. That releases the page without running the
> matching destructor and leaves NR_PAGETABLE elevated.
>
> Make pagetable_free() detect constructor-backed pages and run the
> destructor before releasing their memory to fix the problem.
>
> Fixes: 49f599666420 ("mm: call ctor/dtor for kernel PTEs")
> Cc: stable@vger.kernel.org
> Assisted-by: LLM
> Signed-off-by: Muchun Song <songmuchun@bytedance.com>
> ---
> v2:
> - Move destructor handling from x86 into pagetable_free() (suggested by
> David Hildenbrand)
> - Trim and update the commit message (requested by Dave Hansen)
> ---
> include/linux/mm.h | 37 ++++++++++++++++++++-----------------
> 1 file changed, 20 insertions(+), 17 deletions(-)
>
> diff --git a/include/linux/mm.h b/include/linux/mm.h
> index 5cca90fc1df6..771a7d60b0a5 100644
> --- a/include/linux/mm.h
> +++ b/include/linux/mm.h
> @@ -3969,23 +3969,6 @@ static inline void pagetable_free_kernel(struct ptdesc *pt)
> __pagetable_free(pt);
> }
> #endif
> -/**
> - * pagetable_free - Free pagetables
> - * @pt: The page table descriptor
> - *
> - * pagetable_free frees the memory of all page tables described by a page
> - * table descriptor and the memory for the descriptor itself.
> - */
> -static inline void pagetable_free(struct ptdesc *pt)
> -{
> - if (ptdesc_test_kernel(pt)) {
> - ptdesc_clear_kernel(pt);
> - pagetable_free_kernel(pt);
> - } else {
> - __pagetable_free(pt);
> - }
> -}
> -
> #if defined(CONFIG_SPLIT_PTE_PTLOCKS)
> #if ALLOC_SPLIT_PTLOCKS
> void __init ptlock_cache_init(void);
> @@ -4078,6 +4061,26 @@ static inline void pagetable_dtor(struct ptdesc *ptdesc)
> lruvec_stat_sub_folio(folio, NR_PAGETABLE);
> }
>
> +/**
> + * pagetable_free - Free pagetables
> + * @pt: The page table descriptor
> + *
> + * pagetable_free frees the memory of all page tables described by a page
> + * table descriptor and the memory for the descriptor itself.
> + */
> +static inline void pagetable_free(struct ptdesc *pt)
> +{
> + if (PageTable(ptdesc_page(pt)))
> + pagetable_dtor(pt);
We do have
static inline void pagetable_dtor_free(struct ptdesc *ptdesc)
{
pagetable_dtor(ptdesc);
pagetable_free(ptdesc);
}
... can we now get rid of pagetable_dtor_free() and simply use pagetable_free() ?
(separate cleanup)
> +
> + if (ptdesc_test_kernel(pt)) {
> + ptdesc_clear_kernel(pt);
> + pagetable_free_kernel(pt);
> + } else {
> + __pagetable_free(pt);
> + }
This goes into the right direction. I'm still confused, but the less freeing
functions we have the better.
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
--
Cheers,
David
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2 2/4] riscv/mm: fix hotplug page-table destructor handling
2026-10-10 11:24 ` [PATCH v2 2/4] riscv/mm: fix hotplug page-table destructor handling Muchun Song
@ 2026-10-12 13:28 ` David Hildenbrand (Arm)
2026-10-12 15:32 ` Björn Töpel
1 sibling, 0 replies; 15+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-12 13:28 UTC (permalink / raw)
To: Muchun Song, Andrew Morton
Cc: Oscar Salvador, Dave Hansen, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Björn Töpel,
Catalin Marinas, Will Deacon, Mark Rutland, Kevin Brodsky,
Alexander Gordeev, Alistair Popple, linux-mm, linux-riscv,
linux-arm-kernel, linux-kernel, stable, Muchun Song
On 10/10/26 13:24, Muchun Song wrote:
> RISC-V uses the same memory-hotplug teardown code for linear-map and
> vmemmap page tables. Late linear-map allocations run page-table
> constructors, while vmemmap and early allocations may not.
>
> free_pte_table() unconditionally calls pagetable_dtor(), corrupting
> NR_PAGETABLE accounting for constructor-free tables. free_pmd_table()
> avoids that for vmemmap using is_vmemmap, but mapping type is not a
> reliable proxy for constructor state. In particular, it becomes invalid
> once runtime vmemmap tables use the normal pgalloc helpers.
>
> Introduce free_pgtable_page() to select free_reserved_page() for reserved
> pages and pagetable_free() otherwise. The latter runs the destructor only
> when PageTable() is set, handling both constructor-backed and
> constructor-free tables.
>
> Fixes: c75a74f4ba19 ("riscv: mm: Add memory hotplugging support")
> Cc: stable@vger.kernel.org
> Assisted-by: LLM
> Signed-off-by: Muchun Song <songmuchun@bytedance.com>
> ---
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
--
Cheers,
David
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2 1/4] mm: fix missing page-table destructor in pagetable_free()
2026-10-12 13:27 ` David Hildenbrand (Arm)
@ 2026-10-12 13:29 ` Muchun Song
0 siblings, 0 replies; 15+ messages in thread
From: Muchun Song @ 2026-10-12 13:29 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Muchun Song, Andrew Morton, Oscar Salvador, Dave Hansen,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Björn Töpel, Catalin Marinas, Will Deacon,
Mark Rutland, Kevin Brodsky, Alexander Gordeev, Alistair Popple,
linux-mm, linux-riscv, linux-arm-kernel, linux-kernel, stable
> On Oct 12, 2026, at 21:27, David Hildenbrand (Arm) <david@kernel.org> wrote:
>
> On 10/10/26 13:24, Muchun Song wrote:
>> Since commit 49f599666420 ("mm: call ctor/dtor for kernel PTEs"),
>> pte_alloc_one_kernel() runs the page-table constructor for kernel PTE
>> tables.
>>
>> HVO (HugeTLB Vmemmap Optimization) uses pte_alloc_one_kernel() when
>> splitting a PMD. Restoring the vmemmap backing pages does not collapse
>> the PTE table, so a later memory hot-remove eventually frees it through
>> pagetable_free() on x86-64. That releases the page without running the
>> matching destructor and leaves NR_PAGETABLE elevated.
>>
>> Make pagetable_free() detect constructor-backed pages and run the
>> destructor before releasing their memory to fix the problem.
>>
>> Fixes: 49f599666420 ("mm: call ctor/dtor for kernel PTEs")
>> Cc: stable@vger.kernel.org
>> Assisted-by: LLM
>> Signed-off-by: Muchun Song <songmuchun@bytedance.com>
>> ---
>> v2:
>> - Move destructor handling from x86 into pagetable_free() (suggested by
>> David Hildenbrand)
>> - Trim and update the commit message (requested by Dave Hansen)
>> ---
>> include/linux/mm.h | 37 ++++++++++++++++++++-----------------
>> 1 file changed, 20 insertions(+), 17 deletions(-)
>>
>> diff --git a/include/linux/mm.h b/include/linux/mm.h
>> index 5cca90fc1df6..771a7d60b0a5 100644
>> --- a/include/linux/mm.h
>> +++ b/include/linux/mm.h
>> @@ -3969,23 +3969,6 @@ static inline void pagetable_free_kernel(struct ptdesc *pt)
>> __pagetable_free(pt);
>> }
>> #endif
>> -/**
>> - * pagetable_free - Free pagetables
>> - * @pt: The page table descriptor
>> - *
>> - * pagetable_free frees the memory of all page tables described by a page
>> - * table descriptor and the memory for the descriptor itself.
>> - */
>> -static inline void pagetable_free(struct ptdesc *pt)
>> -{
>> - if (ptdesc_test_kernel(pt)) {
>> - ptdesc_clear_kernel(pt);
>> - pagetable_free_kernel(pt);
>> - } else {
>> - __pagetable_free(pt);
>> - }
>> -}
>> -
>> #if defined(CONFIG_SPLIT_PTE_PTLOCKS)
>> #if ALLOC_SPLIT_PTLOCKS
>> void __init ptlock_cache_init(void);
>> @@ -4078,6 +4061,26 @@ static inline void pagetable_dtor(struct ptdesc *ptdesc)
>> lruvec_stat_sub_folio(folio, NR_PAGETABLE);
>> }
>>
>> +/**
>> + * pagetable_free - Free pagetables
>> + * @pt: The page table descriptor
>> + *
>> + * pagetable_free frees the memory of all page tables described by a page
>> + * table descriptor and the memory for the descriptor itself.
>> + */
>> +static inline void pagetable_free(struct ptdesc *pt)
>> +{
>> + if (PageTable(ptdesc_page(pt)))
>> + pagetable_dtor(pt);
>
>
> We do have
>
> static inline void pagetable_dtor_free(struct ptdesc *ptdesc)
> {
> pagetable_dtor(ptdesc);
> pagetable_free(ptdesc);
> }
>
> ... can we now get rid of pagetable_dtor_free() and simply use pagetable_free() ?
> (separate cleanup)
You're right, that's exactly my plan.
>
>> +
>> + if (ptdesc_test_kernel(pt)) {
>> + ptdesc_clear_kernel(pt);
>> + pagetable_free_kernel(pt);
>> + } else {
>> + __pagetable_free(pt);
>> + }
>
>
> This goes into the right direction. I'm still confused, but the less freeing
> functions we have the better.
>
> Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Thanks David.
>
> --
> Cheers,
>
> David
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2 4/4] arm64/mm: fix destructor for unconstructed hotplug page tables
2026-10-10 11:24 ` [PATCH v2 4/4] arm64/mm: fix destructor for unconstructed hotplug page tables Muchun Song
@ 2026-10-12 13:30 ` David Hildenbrand (Arm)
0 siblings, 0 replies; 15+ messages in thread
From: David Hildenbrand (Arm) @ 2026-10-12 13:30 UTC (permalink / raw)
To: Muchun Song, Andrew Morton
Cc: Oscar Salvador, Dave Hansen, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Björn Töpel,
Catalin Marinas, Will Deacon, Mark Rutland, Kevin Brodsky,
Alexander Gordeev, Alistair Popple, linux-mm, linux-riscv,
linux-arm-kernel, linux-kernel, stable, Muchun Song
On 10/10/26 13:24, Muchun Song wrote:
> 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 <songmuchun@bytedance.com>
> ---
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
--
Cheers,
David
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2 1/4] mm: fix missing page-table destructor in pagetable_free()
2026-10-10 11:24 ` [PATCH v2 1/4] mm: fix missing page-table destructor in pagetable_free() Muchun Song
2026-10-12 11:15 ` Jose A. Perez de Azpillaga
2026-10-12 13:27 ` David Hildenbrand (Arm)
@ 2026-10-12 14:06 ` Dave Hansen
2 siblings, 0 replies; 15+ messages in thread
From: Dave Hansen @ 2026-10-12 14:06 UTC (permalink / raw)
To: Muchun Song, Andrew Morton
Cc: David Hildenbrand, Oscar Salvador, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Björn Töpel,
Catalin Marinas, Will Deacon, Mark Rutland, Kevin Brodsky,
Alexander Gordeev, Alistair Popple, linux-mm, linux-riscv,
linux-arm-kernel, linux-kernel, stable, Muchun Song
On 10/10/26 04:24, Muchun Song wrote:
> +/**
> + * pagetable_free - Free pagetables
> + * @pt: The page table descriptor
> + *
> + * pagetable_free frees the memory of all page tables described by a page
> + * table descriptor and the memory for the descriptor itself.
> + */
> +static inline void pagetable_free(struct ptdesc *pt)
> +{
> + if (PageTable(ptdesc_page(pt)))
> + pagetable_dtor(pt);
> +
> + if (ptdesc_test_kernel(pt)) {
> + ptdesc_clear_kernel(pt);
> + pagetable_free_kernel(pt);
> + } else {
> + __pagetable_free(pt);
> + }
> +}
This is an improvement on the last one, especially the changelog, thanks!
There's also some room here to comment why this needs separate user and
kernel page table handling and why some ptdesc have dtors and others
don't. But that's another patch.
Acked-by: Dave Hansen <dave.hansen@intel.com>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2 2/4] riscv/mm: fix hotplug page-table destructor handling
2026-10-10 11:24 ` [PATCH v2 2/4] riscv/mm: fix hotplug page-table destructor handling Muchun Song
2026-10-12 13:28 ` David Hildenbrand (Arm)
@ 2026-10-12 15:32 ` Björn Töpel
1 sibling, 0 replies; 15+ messages in thread
From: Björn Töpel @ 2026-10-12 15:32 UTC (permalink / raw)
To: Muchun Song, Andrew Morton
Cc: David Hildenbrand, Oscar Salvador, Dave Hansen, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Björn Töpel, Catalin Marinas, Will Deacon,
Mark Rutland, Kevin Brodsky, Alexander Gordeev, Alistair Popple,
linux-mm, linux-riscv, linux-arm-kernel, linux-kernel, stable,
Muchun Song, Muchun Song
Muchun Song <songmuchun@bytedance.com> writes:
> RISC-V uses the same memory-hotplug teardown code for linear-map and
> vmemmap page tables. Late linear-map allocations run page-table
> constructors, while vmemmap and early allocations may not.
>
> free_pte_table() unconditionally calls pagetable_dtor(), corrupting
> NR_PAGETABLE accounting for constructor-free tables. free_pmd_table()
> avoids that for vmemmap using is_vmemmap, but mapping type is not a
> reliable proxy for constructor state. In particular, it becomes invalid
> once runtime vmemmap tables use the normal pgalloc helpers.
>
> Introduce free_pgtable_page() to select free_reserved_page() for reserved
> pages and pagetable_free() otherwise. The latter runs the destructor only
> when PageTable() is set, handling both constructor-backed and
> constructor-free tables.
>
> Fixes: c75a74f4ba19 ("riscv: mm: Add memory hotplugging support")
> Cc: stable@vger.kernel.org
> Assisted-by: LLM
> Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Nice!
Reviewed-by: Björn Töpel <bjorn@kernel.org>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2 3/4] riscv/mm: fix missing destructor for hotplug PUD tables
2026-10-10 11:24 ` [PATCH v2 3/4] riscv/mm: fix missing destructor for hotplug PUD tables Muchun Song
@ 2026-10-12 15:35 ` Björn Töpel
0 siblings, 0 replies; 15+ messages in thread
From: Björn Töpel @ 2026-10-12 15:35 UTC (permalink / raw)
To: Muchun Song, Andrew Morton
Cc: David Hildenbrand, Oscar Salvador, Dave Hansen, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Björn Töpel, Catalin Marinas, Will Deacon,
Mark Rutland, Kevin Brodsky, Alexander Gordeev, Alistair Popple,
linux-mm, linux-riscv, linux-arm-kernel, linux-kernel, stable,
Muchun Song, Muchun Song
Muchun Song <songmuchun@bytedance.com> writes:
> Commit 8472cc4503eb ("riscv: mm: call PUD/P4D ctor in special kernel
> pgtable alloc") made alloc_pud_late() run the PUD page-table constructor.
> However, free_pud_table() still frees non-reserved PUD tables directly
> without decrementing NR_PAGETABLE.
>
> Free PUD tables through the common free_pgtable_page() helper so that
> constructor-backed tables run the matching destructor before being
> freed.
>
> Fixes: 8472cc4503eb ("riscv: mm: call PUD/P4D ctor in special kernel pgtable alloc")
> Cc: stable@vger.kernel.org
> Assisted-by: LLM
> Signed-off-by: Muchun Song <songmuchun@bytedance.com>
> Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Reviewed-by: Björn Töpel <bjorn@kernel.org>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2 1/4] mm: fix missing page-table destructor in pagetable_free()
2026-10-12 12:55 ` Muchun Song
@ 2026-10-12 17:21 ` Jose A. Perez de Azpillaga
0 siblings, 0 replies; 15+ messages in thread
From: Jose A. Perez de Azpillaga @ 2026-10-12 17:21 UTC (permalink / raw)
To: Muchun Song, Andrew Morton
Cc: Muchun Song, David Hildenbrand, Oscar Salvador, Dave Hansen,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Björn Töpel, Catalin Marinas, Will Deacon,
Mark Rutland, Kevin Brodsky, Alexander Gordeev, Alistair Popple,
linux-mm, linux-riscv, linux-arm-kernel, linux-kernel, stable
On 10/12/26 2:55 PM, Muchun Song wrote:
>
>
>> On Oct 12, 2026, at 19:15, Jose A. Perez de Azpillaga <azpijr@gmail.com> wrote:
>>
>> On Sat, Oct 10, 2026 at 07:24:54PM +0800, Muchun Song wrote:
>>> Since commit 49f599666420 ("mm: call ctor/dtor for kernel PTEs"),
>>> pte_alloc_one_kernel() runs the page-table constructor for kernel PTE
>>> tables.
>>>
>>> HVO (HugeTLB Vmemmap Optimization) uses pte_alloc_one_kernel() when
>>> splitting a PMD. Restoring the vmemmap backing pages does not collapse
>>> the PTE table, so a later memory hot-remove eventually frees it through
>>> pagetable_free() on x86-64. That releases the page without running the
>>> matching destructor and leaves NR_PAGETABLE elevated.
>>>
>>> Make pagetable_free() detect constructor-backed pages and run the
>>> destructor before releasing their memory to fix the problem.
>>>
>>> Fixes: 49f599666420 ("mm: call ctor/dtor for kernel PTEs")
>>> Cc: stable@vger.kernel.org
>>
>> is the x86 case covered for 6.16-6.18? as far as I can see, x86
>> free_pagetable() only uses pagetable_free() since bf9e4e30f353
>> ("x86/mm: use pagetable_free()") in v6.19, so the backport wouldn't
>> change anything there. or am I missing something?
>
> Yes, you are absolutely right — thanks for catching this.
>
> I double-checked and indeed x86's free_pagetable() only starts using
> pagetable_free() since bf9e4e30f353 ("x86/mm: use pagetable_free()"),
> which landed in v6.19. So for 6.16-6.18 the prerequisite is not present
> and the backport would be a no-op there.
>
> The correct trailer is:
>
> Cc: <stable@vger.kernel.org> # 6.16-6.18: bf9e4e30f353: x86/mm: use pagetable_free()
also, bf9e4e30f353 alone leaks PMD-sized vmemmap pages on those trees,
it needs 39406c05f8f1 ("x86/mm: Fix freeing of PMD-sized vmemmap pages")
as well :)
Reviewed-by: Jose A. Perez de Azpillaga <azpijr@gmail.com>
--
cheers,
jose a. p-a
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-10-12 17:21 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-10 11:24 [PATCH v2 0/4] mm: Fix page-table teardown during memory hot-remove Muchun Song
2026-10-10 11:24 ` [PATCH v2 1/4] mm: fix missing page-table destructor in pagetable_free() Muchun Song
2026-10-12 11:15 ` Jose A. Perez de Azpillaga
2026-10-12 12:55 ` Muchun Song
2026-10-12 17:21 ` Jose A. Perez de Azpillaga
2026-10-12 13:27 ` David Hildenbrand (Arm)
2026-10-12 13:29 ` Muchun Song
2026-10-12 14:06 ` Dave Hansen
2026-10-10 11:24 ` [PATCH v2 2/4] riscv/mm: fix hotplug page-table destructor handling Muchun Song
2026-10-12 13:28 ` David Hildenbrand (Arm)
2026-10-12 15:32 ` Björn Töpel
2026-10-10 11:24 ` [PATCH v2 3/4] riscv/mm: fix missing destructor for hotplug PUD tables Muchun Song
2026-10-12 15:35 ` Björn Töpel
2026-10-10 11:24 ` [PATCH v2 4/4] arm64/mm: fix destructor for unconstructed hotplug page tables Muchun Song
2026-10-12 13:30 ` David Hildenbrand (Arm)
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®