Re: [PATCH v4 00/12] mm: make userland page table freeing RCU-safe
From: Andrew Morton <akpm@linux-foundation.org>
Date: 2026-09-23 19:59:31
Also in:
linux-alpha, linux-arch, linux-doc, linux-m68k, linux-mips, linux-mm, linux-riscv, linux-s390, linux-sh, linux-um, lkml, loongarch, sparclinux
On Tue, 22 Sep 2026 16:35:31 +0100 "Lorenzo Stoakes (ARM)" [off-list ref] wrote:
The majority of architectures in the kernel defer page table freeing until an RCU grace period has elapsed, this series converts all remaining architectures to do so too and eliminates CONFIG_MMU_GATHER_RCU_TABLE_FREE altogether. This is important because it enables safe lockless page table walking under RCU alone. Doing so allows for reduced lock contention, avoids lock ordering concerns and enables fast, efficient and correct page table walking as a result. Additionally it removes a bunch of code and architecture-specific behaviour which is always a beneficial thing to do. There has been much recent work on this:
Thanks, I've updated mm.git's mm-unstable branch to this version. Plus one -fix for [01/12].
v4: * Updated the 1st patch to allocate a new page table on PTE deposit rather than deposit a page table that might currently be being walked by an RCU-only page table walker, as per David.
Here's how v4 altered mm.git: mm/huge_memory.c | 2 +- mm/khugepaged.c | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-)
--- a/mm/huge_memory.c~b
+++ a/mm/huge_memory.c@@ -2478,7 +2478,7 @@ static inline void zap_deposited_table(s pgtable_t pgtable; pgtable = pgtable_trans_huge_withdraw(mm, pmd); - pte_free_defer(mm, pgtable); + pte_free(mm, pgtable); mm_dec_nr_ptes(mm); } --- a/mm/khugepaged.c~b +++ a/mm/khugepaged.c
@@ -1278,6 +1278,23 @@ static enum scan_result alloc_charge_fol return SCAN_SUCCEED; } +static pgtable_t alloc_deposit_pte(struct mm_struct *mm) +{ + /* + * khugepaged is run from a kernel thread, so need to manually set the + * correct memcg so the allocation gets charged correctly. + */ + struct mem_cgroup *memcg = get_mem_cgroup_from_mm(mm); + struct mem_cgroup *old_memcg = set_active_memcg(memcg); + pgtable_t pgtable; + + pgtable = pte_alloc_one(mm); + + set_active_memcg(old_memcg); + mem_cgroup_put(memcg); + return pgtable; +} + /* * collapse_huge_page() expects the mmap_lock to be unlocked before entering and * will always return with the lock unlocked, to avoid holding the mmap_lock
@@ -1293,7 +1310,7 @@ static enum scan_result collapse_huge_pa LIST_HEAD(compound_pagelist); pmd_t *pmd, _pmd; pte_t *pte = NULL; - pgtable_t pgtable; + pgtable_t pgtable = NULL; struct folio *folio; spinlock_t *pmd_ptl, *pte_ptl; enum scan_result result = SCAN_FAIL;
@@ -1310,6 +1327,14 @@ static enum scan_result collapse_huge_pa goto out_nolock; } + if (is_pmd_order(order)) { + pgtable = alloc_deposit_pte(mm); + if (!pgtable) { + result = SCAN_ALLOC_HUGE_PAGE_FAIL; + goto out_nolock; + } + } + mmap_read_lock(mm); result = hugepage_vma_revalidate(mm, pmd_addr, /*expect_anon=*/ true, &vma, cc, order);
@@ -1433,8 +1458,8 @@ static enum scan_result collapse_huge_pa spin_lock(pmd_ptl); VM_WARN_ON_ONCE(!pmd_none(*pmd)); if (is_pmd_order(order)) { - pgtable = pmd_pgtable(_pmd); pgtable_trans_huge_deposit(mm, pmd, pgtable); + pgtable = NULL; map_anon_folio_pmd_nopf(folio, pmd, vma, pmd_addr); } else { /*
@@ -1453,6 +1478,9 @@ static enum scan_result collapse_huge_pa } spin_unlock(pmd_ptl); + if (is_pmd_order(order)) + pte_free_defer(mm, pmd_pgtable(_pmd)); + folio = NULL; result = SCAN_SUCCEED;
@@ -1463,6 +1491,8 @@ out_up_write: anon_vma_unlock_write(vma->anon_vma); mmap_write_unlock(mm); out_nolock: + if (pgtable) + pte_free(mm, pgtable); if (folio) folio_put(folio); trace_mm_collapse_huge_page(mm, result == SCAN_SUCCEED, result, order);
_