* [PATCH v2 00/12] mm: Decouple folio-based vmemmap optimization from HugeTLB
@ 2026-11-02 3:25 Muchun Song
2026-11-02 3:25 ` [PATCH v2 01/12] mm: hugetlb_vmemmap: remove HugeTLB-specific vmemmap optimization macros Muchun Song
` (11 more replies)
0 siblings, 12 replies; 28+ messages in thread
From: Muchun Song @ 2026-11-02 3:25 UTC (permalink / raw)
To: Oscar Salvador, Andrew Morton, Mike Rapoport
Cc: Qi Zheng, Lance Yang, James Houghton, David Hildenbrand,
Muchun Song, linux-mm, linux-kernel, Muchun Song
Hi,
Section-based vmemmap optimization is implemented in sparse-vmemmap.c,
while folio-based optimization currently lives in hugetlb_vmemmap.c.
The latter is tightly coupled to HugeTLB-specific state even though the
underlying page-table remapping operates on one folio at a time.
To make these interfaces easier to maintain, the folio-based remapping
mechanics first need a boundary that is independent of HugeTLB-specific
state and raw address ranges. This will allow the mechanics to be moved
into a common vmemmap optimization layer alongside the section-based
interface.
This series establishes that boundary through three folio-oriented
operations, each with a distinct role:
- vmemmap_optimize_folio() optimizes the vmemmap backing of a folio;
- vmemmap_restore_folio() restores the vmemmap backing of a folio; and
- vmemmap_pteify_folio() converts the vmemmap backing of a folio to
PTE mappings.
Together, these operations encapsulate the address-range calculation and
remapping mechanics for a folio. HugeTLB wrappers retain policy decisions
such as eligibility, optimized state, batching and TLB flush handling.
Patches 1-8 generalize the underlying vmemmap remapping infrastructure by
removing HugeTLB-specific assumptions and simplifying remap control and
resource handling. Patches 9-12 then establish folio-oriented operations
for optimization, restoration and PTE conversion.
The implementation remains in hugetlb_vmemmap.c for now. A follow-up can
move the folio-based operations into the common vmemmap optimization layer
once the interface boundary is in place.
v2:
- Rebase onto mm/mm-new at d42dfde37af7
- Rename the unified TLB flag to VMEMMAP_DEFER_TLB_FLUSH and clarify that
it defers only safely batchable flushes (suggested by Qi Zheng)
- Update the vmemmap page-list documentation to describe its input/output
contract (suggested by Lance Yang and Qi Zheng)
- Use page_address() instead of page_to_virt() when copying a vmemmap
replacement page
- Collect Reviewed-by tags from Lance Yang and Qi Zheng
v1: https://lore.kernel.org/20261020115306.813483-1-songmuchun@bytedance.com/
Muchun Song (12):
mm: hugetlb_vmemmap: remove HugeTLB-specific vmemmap optimization
macros
mm: hugetlb_vmemmap: unify VMEMMAP_{SPLIT,REMAP}_NO_TLB_FLUSH
mm: hugetlb_vmemmap: simplify vmemmap head page handling
mm: hugetlb_vmemmap: generalize vmemmap page allocation
mm: hugetlb_vmemmap: unify writable vmemmap page preparation
mm: hugetlb_vmemmap: rename vmemmap_remap_pte() to
vmemmap_optimize_pte()
mm: hugetlb_vmemmap: simplify shared tail page handling
mm: hugetlb_vmemmap: move alloc_vmemmap_page_list() next to its free
helper
mm: hugetlb_vmemmap: make vmemmap optimization folio-oriented
mm: hugetlb_vmemmap: make vmemmap restoration folio-oriented
mm: hugetlb_vmemmap: unify vmemmap range restoration
mm: hugetlb_vmemmap: make vmemmap PTE conversion folio-oriented
include/linux/vmemmap-optimization.h | 10 +-
mm/hugetlb.c | 4 +-
mm/hugetlb_vmemmap.c | 373 ++++++++++++---------------
mm/hugetlb_vmemmap.h | 10 +-
mm/mm_init.c | 6 +-
mm/sparse-vmemmap.c | 7 +-
6 files changed, 182 insertions(+), 228 deletions(-)
base-commit: d42dfde37af766d1c272467352c0a56d3169ee58
--
2.54.0
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v2 01/12] mm: hugetlb_vmemmap: remove HugeTLB-specific vmemmap optimization macros
2026-11-02 3:25 [PATCH v2 00/12] mm: Decouple folio-based vmemmap optimization from HugeTLB Muchun Song
@ 2026-11-02 3:25 ` Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 02/12] mm: hugetlb_vmemmap: unify VMEMMAP_{SPLIT,REMAP}_NO_TLB_FLUSH Muchun Song
` (10 subsequent siblings)
11 siblings, 1 reply; 28+ messages in thread
From: Muchun Song @ 2026-11-02 3:25 UTC (permalink / raw)
To: Oscar Salvador, Andrew Morton, Mike Rapoport
Cc: Qi Zheng, Lance Yang, James Houghton, David Hildenbrand,
Muchun Song, linux-mm, linux-kernel, Muchun Song
Vmemmap optimization is no longer HugeTLB-specific, so the remaining
HugeTLB-local reserve macros are redundant.
Replace them with the generic definitions to remove duplication and keep
the HugeTLB vmemmap code aligned with the common optimization macros.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Reviewed-by: Lance Yang <lance.yang@linux.dev>
Reviewed-by: Qi Zheng <qi.zheng@linux.dev>
---
v2:
- Collect Reviewed-by from Lance Yang and Qi Zheng
---
include/linux/vmemmap-optimization.h | 4 ++--
mm/hugetlb.c | 4 ++--
mm/hugetlb_vmemmap.c | 6 +++---
mm/hugetlb_vmemmap.h | 10 ++--------
4 files changed, 9 insertions(+), 15 deletions(-)
diff --git a/include/linux/vmemmap-optimization.h b/include/linux/vmemmap-optimization.h
index fa9e9abd6656..065840f3c241 100644
--- a/include/linux/vmemmap-optimization.h
+++ b/include/linux/vmemmap-optimization.h
@@ -27,8 +27,8 @@
/* The number of retained vmemmap pages with HVO enabled. */
#define VMEMMAP_OPTIMIZATION_PAGES 1
-#define VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES \
- (VMEMMAP_OPTIMIZATION_PAGES * PAGE_SIZE / sizeof(struct page))
+#define VMEMMAP_OPTIMIZATION_SIZE (VMEMMAP_OPTIMIZATION_PAGES * PAGE_SIZE)
+#define VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES (VMEMMAP_OPTIMIZATION_SIZE / sizeof(struct page))
#define VMEMMAP_OPTIMIZATION_MIN_ORDER (ilog2(VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES) + 1)
#ifdef CONFIG_VMEMMAP_OPTIMIZATION
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index ff9dbfbc7a96..7c26be15a741 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -3301,7 +3301,7 @@ static void __init prep_and_add_bootmem_folios(struct hstate *h,
* be no contention.
*/
hugetlb_folio_init_tail_vmemmap(folio, h,
- HUGETLB_VMEMMAP_RESERVE_PAGES,
+ VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES,
pages_per_huge_page(h));
}
hugetlb_bootmem_init_migratetype(folio, h);
@@ -3341,7 +3341,7 @@ static void __init gather_bootmem_prealloc_node(unsigned long nid)
WARN_ON(folio_ref_count(folio) != 1);
hugetlb_folio_init_vmemmap(folio, h,
- HUGETLB_VMEMMAP_RESERVE_PAGES);
+ VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES);
init_new_hugetlb_folio(folio);
if (vmemmap_optimizable_order(pfn_to_section_compound_order(pfn)))
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index b8ce014ba6e6..4b73d6ec8dce 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -408,7 +408,7 @@ static int __hugetlb_vmemmap_restore_folio(const struct hstate *h,
vmemmap_start = (unsigned long)&folio->page;
vmemmap_end = vmemmap_start + hugetlb_vmemmap_size(h);
- vmemmap_start += HUGETLB_VMEMMAP_RESERVE_SIZE;
+ vmemmap_start += VMEMMAP_OPTIMIZATION_SIZE;
/*
* The pages which the vmemmap virtual address range [@vmemmap_start,
@@ -676,8 +676,8 @@ static int __init hugetlb_vmemmap_init(void)
{
const struct hstate *h;
- /* HUGETLB_VMEMMAP_RESERVE_SIZE should cover all used struct pages */
- BUILD_BUG_ON(__NR_USED_SUBPAGE > HUGETLB_VMEMMAP_RESERVE_PAGES);
+ /* VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES should cover all used struct pages */
+ BUILD_BUG_ON(__NR_USED_SUBPAGE > VMEMMAP_OPTIMIZATION_NR_STRUCT_PAGES);
for_each_hstate(h) {
if (hugetlb_vmemmap_optimizable(h)) {
diff --git a/mm/hugetlb_vmemmap.h b/mm/hugetlb_vmemmap.h
index d717d0d10960..d057e8b12bf1 100644
--- a/mm/hugetlb_vmemmap.h
+++ b/mm/hugetlb_vmemmap.h
@@ -9,13 +9,7 @@
#ifndef _LINUX_HUGETLB_VMEMMAP_H
#define _LINUX_HUGETLB_VMEMMAP_H
#include <linux/hugetlb.h>
-
-/*
- * Reserve one vmemmap page, all vmemmap addresses are mapped to it. See
- * Documentation/mm/vmemmap_dedup.rst.
- */
-#define HUGETLB_VMEMMAP_RESERVE_SIZE PAGE_SIZE
-#define HUGETLB_VMEMMAP_RESERVE_PAGES (HUGETLB_VMEMMAP_RESERVE_SIZE / sizeof(struct page))
+#include <linux/vmemmap-optimization.h>
#ifdef CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP
int hugetlb_vmemmap_restore_folio(const struct hstate *h, struct folio *folio);
@@ -37,7 +31,7 @@ static inline unsigned int hugetlb_vmemmap_size(const struct hstate *h)
*/
static inline unsigned int hugetlb_vmemmap_optimizable_size(const struct hstate *h)
{
- int size = hugetlb_vmemmap_size(h) - HUGETLB_VMEMMAP_RESERVE_SIZE;
+ int size = hugetlb_vmemmap_size(h) - VMEMMAP_OPTIMIZATION_SIZE;
if (!is_power_of_2(sizeof(struct page)))
return 0;
--
2.54.0
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v2 02/12] mm: hugetlb_vmemmap: unify VMEMMAP_{SPLIT,REMAP}_NO_TLB_FLUSH
2026-11-02 3:25 [PATCH v2 00/12] mm: Decouple folio-based vmemmap optimization from HugeTLB Muchun Song
2026-11-02 3:25 ` [PATCH v2 01/12] mm: hugetlb_vmemmap: remove HugeTLB-specific vmemmap optimization macros Muchun Song
@ 2026-11-02 3:25 ` Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 03/12] mm: hugetlb_vmemmap: simplify vmemmap head page handling Muchun Song
` (9 subsequent siblings)
11 siblings, 1 reply; 28+ messages in thread
From: Muchun Song @ 2026-11-02 3:25 UTC (permalink / raw)
To: Oscar Salvador, Andrew Morton, Mike Rapoport
Cc: Qi Zheng, Lance Yang, James Houghton, David Hildenbrand,
Muchun Song, linux-mm, linux-kernel, Muchun Song
VMEMMAP_SPLIT_NO_TLB_FLUSH and VMEMMAP_REMAP_NO_TLB_FLUSH do not
describe fully independent choices. A split-only walk can defer the
PMD-split flush to a later global flush, and a remap-only walk can
defer the final remap flush. However, when a PMD split is followed
by PTE remapping, the split flush must happen before the PTEs are
changed, because stale large-page translations must not overlap with
new small-page translations that map different physical pages.
Replace the two flags with a single VMEMMAP_DEFER_TLB_FLUSH flag. The
new name reflects that only safely batchable flushes are deferred. Keep
split-only walks able to defer the flush, but force the split flush
whenever a remap_pte callback is present. Callers that batch HVO work
can still defer the final remap flush and issue their own global flush.
Current callers keep the same flushing behavior.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Reviewed-by: Lance Yang <lance.yang@linux.dev>
Reviewed-by: Qi Zheng <qi.zheng@linux.dev>
---
v2:
- Rename the unified flag to VMEMMAP_DEFER_TLB_FLUSH and update its
comment (suggested by Qi Zheng)
- Update the commit message for the new flag name
- Collect Reviewed-by from Lance Yang and Qi Zheng
---
mm/hugetlb_vmemmap.c | 23 +++++++++++++----------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index 4b73d6ec8dce..63e899cfaac3 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -42,10 +42,8 @@ struct vmemmap_remap_walk {
struct list_head *vmemmap_pages;
-/* Skip the TLB flush when we split the PMD */
-#define VMEMMAP_SPLIT_NO_TLB_FLUSH BIT(0)
-/* Skip the TLB flush when we remap the PTE */
-#define VMEMMAP_REMAP_NO_TLB_FLUSH BIT(1)
+/* Defer safely batchable TLB flushes to the caller. */
+#define VMEMMAP_DEFER_TLB_FLUSH BIT(0)
unsigned long flags;
};
@@ -85,7 +83,12 @@ static int vmemmap_split_pmd(pmd_t *pmd, struct page *head, unsigned long start,
/* Make pte visible before pmd. See comment in pmd_install(). */
smp_wmb();
pmd_populate_kernel(&init_mm, pmd, pgtable);
- if (!(walk->flags & VMEMMAP_SPLIT_NO_TLB_FLUSH))
+ /*
+ * Modern MMUs allow overlapping TLB entries with different mapping
+ * granularities only if they map the same physical address. So
+ * remapping must not proceed without first flushing the TLB.
+ */
+ if (walk->remap_pte || !(walk->flags & VMEMMAP_DEFER_TLB_FLUSH))
flush_tlb_kernel_range(start, start + PMD_SIZE);
} else {
pte_free_kernel(&init_mm, pgtable);
@@ -166,7 +169,7 @@ static int vmemmap_remap_range(unsigned long start, unsigned long end,
if (ret)
return ret;
- if (walk->remap_pte && !(walk->flags & VMEMMAP_REMAP_NO_TLB_FLUSH))
+ if (walk->remap_pte && !(walk->flags & VMEMMAP_DEFER_TLB_FLUSH))
flush_tlb_kernel_range(start, end);
return 0;
@@ -274,7 +277,7 @@ static int vmemmap_remap_split(unsigned long start, unsigned long end)
{
struct vmemmap_remap_walk walk = {
.remap_pte = NULL,
- .flags = VMEMMAP_SPLIT_NO_TLB_FLUSH,
+ .flags = VMEMMAP_DEFER_TLB_FLUSH,
};
return vmemmap_remap_range(start, end, &walk);
@@ -458,7 +461,7 @@ long hugetlb_vmemmap_restore_folios(const struct hstate *h,
struct folio *folio, *t_folio;
long restored = 0;
long ret = 0;
- unsigned long flags = VMEMMAP_REMAP_NO_TLB_FLUSH;
+ unsigned long flags = VMEMMAP_DEFER_TLB_FLUSH;
list_for_each_entry_safe(folio, t_folio, folio_list, lru) {
if (folio_test_hugetlb_vmemmap_optimized(folio)) {
@@ -516,7 +519,7 @@ static int __hugetlb_vmemmap_optimize_folio(const struct hstate *h,
/*
* Very Subtle
- * If VMEMMAP_REMAP_NO_TLB_FLUSH is set, TLB flushing is not performed
+ * If VMEMMAP_DEFER_TLB_FLUSH is set, TLB flushing is not performed
* immediately after remapping. As a result, subsequent accesses
* and modifications to struct pages associated with the hugetlb
* page could be to the OLD struct pages. Set the vmemmap optimized
@@ -595,7 +598,7 @@ void hugetlb_vmemmap_optimize_folios(struct hstate *h, struct list_head *folio_l
struct folio *folio;
unsigned long nr_to_optimize = 0;
LIST_HEAD(vmemmap_pages);
- unsigned long flags = VMEMMAP_REMAP_NO_TLB_FLUSH;
+ unsigned long flags = VMEMMAP_DEFER_TLB_FLUSH;
if (list_empty(folio_list))
return;
--
2.54.0
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v2 03/12] mm: hugetlb_vmemmap: simplify vmemmap head page handling
2026-11-02 3:25 [PATCH v2 00/12] mm: Decouple folio-based vmemmap optimization from HugeTLB Muchun Song
2026-11-02 3:25 ` [PATCH v2 01/12] mm: hugetlb_vmemmap: remove HugeTLB-specific vmemmap optimization macros Muchun Song
2026-11-02 3:25 ` [PATCH v2 02/12] mm: hugetlb_vmemmap: unify VMEMMAP_{SPLIT,REMAP}_NO_TLB_FLUSH Muchun Song
@ 2026-11-02 3:25 ` Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 04/12] mm: hugetlb_vmemmap: generalize vmemmap page allocation Muchun Song
` (8 subsequent siblings)
11 siblings, 1 reply; 28+ messages in thread
From: Muchun Song @ 2026-11-02 3:25 UTC (permalink / raw)
To: Oscar Salvador, Andrew Morton, Mike Rapoport
Cc: Qi Zheng, Lance Yang, James Houghton, David Hildenbrand,
Muchun Song, linux-mm, linux-kernel, Muchun Song
The new head page is already tracked on the vmemmap page list before
remapping starts. Passing it separately gives the remap walker two
ways to refer to the same page and unnecessarily complicates its
interface.
Use the page list as the single handoff mechanism for the head page,
removing the redundant state from the walker.
No functional change intended.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Reviewed-by: Lance Yang <lance.yang@linux.dev>
Reviewed-by: Qi Zheng <qi.zheng@linux.dev>
---
v2:
- Remove outdated head-page wording from vmemmap_remap_free() kernel-doc
(suggested by Lance Yang)
- Document the vmemmap page list contract (suggested by Qi Zheng)
- Collect Reviewed-by from Lance Yang and Qi Zheng
---
mm/hugetlb_vmemmap.c | 30 +++++++++++++-----------------
1 file changed, 13 insertions(+), 17 deletions(-)
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index 63e899cfaac3..0e5b2b182d1b 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -25,10 +25,9 @@
*
* @remap_pte: called for each lowest-level entry (PTE).
* @nr_walked: the number of walked pte.
- * @vmemmap_head: the page to be installed as first in the vmemmap range
* @vmemmap_tail: the page to be installed as non-first in the vmemmap range
- * @vmemmap_pages: the list head of the vmemmap pages that can be freed
- * or is mapped from.
+ * @vmemmap_pages: list supplying replacement pages for the remap and
+ * receiving displaced backing pages for the caller to free.
* @flags: used to modify behavior in vmemmap page table walking
* operations.
*/
@@ -37,7 +36,6 @@ struct vmemmap_remap_walk {
struct vmemmap_remap_walk *walk);
unsigned long nr_walked;
- struct page *vmemmap_head;
struct page *vmemmap_tail;
struct list_head *vmemmap_pages;
@@ -208,10 +206,13 @@ static void vmemmap_remap_pte(pte_t *pte, unsigned long addr,
pte_t entry;
/* Remapping the head page requires r/w */
- if (unlikely(walk->nr_walked == 0 && walk->vmemmap_head)) {
+ if (unlikely(walk->nr_walked == 0)) {
+ struct page *head;
+
VM_WARN_ON_ONCE(!PageHead((const struct page *)addr));
- list_del(&walk->vmemmap_head->lru);
+ head = list_first_entry(walk->vmemmap_pages, struct page, lru);
+ list_del(&head->lru);
/*
* Makes sure that preceding stores to the page contents from
@@ -220,7 +221,7 @@ static void vmemmap_remap_pte(pte_t *pte, unsigned long addr,
*/
smp_wmb();
- entry = mk_pte(walk->vmemmap_head, PAGE_KERNEL);
+ entry = mk_pte(head, PAGE_KERNEL);
} else {
VM_WARN_ON_ONCE(!PageTail((const struct page *)addr));
@@ -284,23 +285,20 @@ static int vmemmap_remap_split(unsigned long start, unsigned long end)
}
/**
- * vmemmap_remap_free - remap the vmemmap virtual address range [@start, @end)
- * to use @vmemmap_head/tail, then free vmemmap which
- * the range are mapped to.
+ * vmemmap_remap_free - remap a vmemmap range and collect old backing pages
* @start: start address of the vmemmap virtual address range that we want
* to remap.
* @end: end address of the vmemmap virtual address range that we want to
* remap.
- * @vmemmap_head: the page to be installed as first in the vmemmap range
* @vmemmap_tail: the page to be installed as non-first in the vmemmap range
- * @vmemmap_pages: list to deposit vmemmap pages to be freed. It is callers
- * responsibility to free pages.
+ * @vmemmap_pages: input/output list whose first entry supplies the new backing
+ * page for @start; replaced backing pages are added to the list for
+ * the caller to free.
* @flags: modifications to vmemmap_remap_walk flags
*
* Return: %0 on success, negative error code otherwise.
*/
static int vmemmap_remap_free(unsigned long start, unsigned long end,
- struct page *vmemmap_head,
struct page *vmemmap_tail,
struct list_head *vmemmap_pages,
unsigned long flags)
@@ -308,7 +306,6 @@ static int vmemmap_remap_free(unsigned long start, unsigned long end,
int ret;
struct vmemmap_remap_walk walk = {
.remap_pte = vmemmap_remap_pte,
- .vmemmap_head = vmemmap_head,
.vmemmap_tail = vmemmap_tail,
.vmemmap_pages = vmemmap_pages,
.flags = flags,
@@ -549,8 +546,7 @@ static int __hugetlb_vmemmap_optimize_folio(const struct hstate *h,
* they can be freed by the caller.
*/
ret = vmemmap_remap_free(vmemmap_start, vmemmap_end,
- vmemmap_head, vmemmap_tail,
- vmemmap_pages, flags);
+ vmemmap_tail, vmemmap_pages, flags);
out:
if (ret)
folio_clear_hugetlb_vmemmap_optimized(folio);
--
2.54.0
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v2 04/12] mm: hugetlb_vmemmap: generalize vmemmap page allocation
2026-11-02 3:25 [PATCH v2 00/12] mm: Decouple folio-based vmemmap optimization from HugeTLB Muchun Song
` (2 preceding siblings ...)
2026-11-02 3:25 ` [PATCH v2 03/12] mm: hugetlb_vmemmap: simplify vmemmap head page handling Muchun Song
@ 2026-11-02 3:25 ` Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 05/12] mm: hugetlb_vmemmap: unify writable vmemmap page preparation Muchun Song
` (7 subsequent siblings)
11 siblings, 1 reply; 28+ messages in thread
From: Muchun Song @ 2026-11-02 3:25 UTC (permalink / raw)
To: Oscar Salvador, Andrew Morton, Mike Rapoport
Cc: Qi Zheng, Lance Yang, James Houghton, David Hildenbrand,
Muchun Song, linux-mm, linux-kernel, Muchun Song
Vmemmap page allocation is currently tied to restoring a virtual range
and assumes an empty destination list. These constraints prevent it from
being reused when pages are requested independently of a vmemmap range or
appended to an existing list.
Generalize the allocator and give it an all-or-nothing list contract so
the restore and optimization paths can share the same allocation policy.
This also allows HVO head-page allocation to fail gracefully under memory
pressure without triggering the OOM killer.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Reviewed-by: Lance Yang <lance.yang@linux.dev>
Reviewed-by: Qi Zheng <qi.zheng@linux.dev>
---
v2:
- Collect Reviewed-by from Lance Yang and Qi Zheng
---
mm/hugetlb_vmemmap.c | 39 ++++++++++++++++++---------------------
1 file changed, 18 insertions(+), 21 deletions(-)
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index 0e5b2b182d1b..4b20a8efc067 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -334,28 +334,26 @@ static int vmemmap_remap_free(unsigned long start, unsigned long end,
return ret;
}
-static int alloc_vmemmap_page_list(unsigned long start, unsigned long end,
- struct list_head *list)
+static int alloc_vmemmap_page_list(struct list_head *head, unsigned int nr_pages,
+ int nid)
{
- gfp_t gfp_mask = GFP_KERNEL | __GFP_RETRY_MAYFAIL;
- unsigned long nr_pages = (end - start) >> PAGE_SHIFT;
- int nid = page_to_nid((struct page *)start);
- struct page *page, *next;
- int i;
+ LIST_HEAD(local);
- for (i = 0; i < nr_pages; i++) {
- page = alloc_pages_node(nid, gfp_mask, 0);
- if (!page)
- goto out;
- list_add(&page->lru, list);
+ for (unsigned int i = 0; i < nr_pages; i++) {
+ struct page *page;
+
+ page = alloc_pages_node(nid, GFP_KERNEL | __GFP_RETRY_MAYFAIL, 0);
+ if (!page) {
+ memmap_pages_add(i);
+ free_vmemmap_page_list(&local);
+ return -ENOMEM;
+ }
+ list_add(&page->lru, &local);
}
+ list_splice(&local, head);
memmap_pages_add(nr_pages);
return 0;
-out:
- list_for_each_entry_safe(page, next, list, lru)
- __free_page(page);
- return -ENOMEM;
}
/**
@@ -374,13 +372,14 @@ static int vmemmap_remap_alloc(unsigned long start, unsigned long end,
unsigned long flags)
{
LIST_HEAD(vmemmap_pages);
+ int nid = page_to_nid((struct page *)start);
struct vmemmap_remap_walk walk = {
.remap_pte = vmemmap_restore_pte,
.vmemmap_pages = &vmemmap_pages,
.flags = flags,
};
- if (alloc_vmemmap_page_list(start, end, &vmemmap_pages))
+ if (alloc_vmemmap_page_list(&vmemmap_pages, (end - start) / PAGE_SIZE, nid))
return -ENOMEM;
return vmemmap_remap_range(start, end, &walk);
@@ -527,15 +526,13 @@ static int __hugetlb_vmemmap_optimize_folio(const struct hstate *h,
*/
folio_set_hugetlb_vmemmap_optimized(folio);
- vmemmap_head = alloc_pages_node(nid, GFP_KERNEL, 0);
- if (!vmemmap_head) {
+ if (alloc_vmemmap_page_list(vmemmap_pages, VMEMMAP_OPTIMIZATION_PAGES, nid)) {
ret = -ENOMEM;
goto out;
}
+ vmemmap_head = list_first_entry(vmemmap_pages, struct page, lru);
copy_page(page_to_virt(vmemmap_head), folio);
- list_add(&vmemmap_head->lru, vmemmap_pages);
- memmap_pages_add(1);
vmemmap_start = (unsigned long)&folio->page;
vmemmap_end = vmemmap_start + hugetlb_vmemmap_size(h);
--
2.54.0
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v2 05/12] mm: hugetlb_vmemmap: unify writable vmemmap page preparation
2026-11-02 3:25 [PATCH v2 00/12] mm: Decouple folio-based vmemmap optimization from HugeTLB Muchun Song
` (3 preceding siblings ...)
2026-11-02 3:25 ` [PATCH v2 04/12] mm: hugetlb_vmemmap: generalize vmemmap page allocation Muchun Song
@ 2026-11-02 3:25 ` Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 06/12] mm: hugetlb_vmemmap: rename vmemmap_remap_pte() to vmemmap_optimize_pte() Muchun Song
` (6 subsequent siblings)
11 siblings, 1 reply; 28+ messages in thread
From: Muchun Song @ 2026-11-02 3:25 UTC (permalink / raw)
To: Oscar Salvador, Andrew Morton, Mike Rapoport
Cc: Qi Zheng, Lance Yang, James Houghton, David Hildenbrand,
Muchun Song, linux-mm, linux-kernel, Muchun Song
Installing a writable vmemmap page requires consuming a replacement page,
copying the current contents into it and making the copy visible before
publishing the new mapping. HVO currently splits this sequence between
the page-table walker and its caller depending on the remap path.
Centralize the sequence in the walker so all writable replacements follow
the same copy-and-publish protocol. This keeps page preparation with the
mapping update and removes path-specific handling from higher-level HVO
code.
No functional change intended.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Reviewed-by: Lance Yang <lance.yang@linux.dev>
Reviewed-by: Qi Zheng <qi.zheng@linux.dev>
---
v2:
- Collect Reviewed-by from Lance Yang and Qi Zheng
- Replace page_to_virt() with page_address()
---
mm/hugetlb_vmemmap.c | 40 +++++++++++++++++-----------------------
1 file changed, 17 insertions(+), 23 deletions(-)
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index 4b20a8efc067..c9635679a36d 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -199,6 +199,20 @@ static void free_vmemmap_page_list(struct list_head *list)
free_vmemmap_page(page);
}
+static struct page *vmemmap_dup_page_from_list(const struct page *src,
+ struct list_head *vmemmap_pages)
+{
+ struct page *dst = list_first_entry(vmemmap_pages, struct page, lru);
+
+ list_del(&dst->lru);
+ copy_page(page_address(dst), page_address(src));
+
+ /* Ensure updates to @dst page are visible before set_pte_at(). */
+ smp_wmb();
+
+ return dst;
+}
+
static void vmemmap_remap_pte(pte_t *pte, unsigned long addr,
struct vmemmap_remap_walk *walk)
{
@@ -211,16 +225,7 @@ static void vmemmap_remap_pte(pte_t *pte, unsigned long addr,
VM_WARN_ON_ONCE(!PageHead((const struct page *)addr));
- head = list_first_entry(walk->vmemmap_pages, struct page, lru);
- list_del(&head->lru);
-
- /*
- * Makes sure that preceding stores to the page contents from
- * vmemmap_remap_free() become visible before the set_pte_at()
- * write.
- */
- smp_wmb();
-
+ head = vmemmap_dup_page_from_list(page, walk->vmemmap_pages);
entry = mk_pte(head, PAGE_KERNEL);
} else {
VM_WARN_ON_ONCE(!PageTail((const struct page *)addr));
@@ -253,15 +258,7 @@ static void vmemmap_restore_pte(pte_t *pte, unsigned long addr,
VM_WARN_ON_ONCE(PageHead((const struct page *)addr));
- dst = list_first_entry(walk->vmemmap_pages, struct page, lru);
- list_del(&dst->lru);
- copy_page(page_to_virt(dst), page_to_virt(src));
-
- /*
- * Makes sure that preceding stores to the page contents become visible
- * before the set_pte_at() write.
- */
- smp_wmb();
+ dst = vmemmap_dup_page_from_list(src, walk->vmemmap_pages);
set_pte_at(&init_mm, addr, pte, mk_pte(dst, PAGE_KERNEL));
}
@@ -499,7 +496,7 @@ static int __hugetlb_vmemmap_optimize_folio(const struct hstate *h,
unsigned long flags)
{
unsigned long vmemmap_start, vmemmap_end;
- struct page *vmemmap_head, *vmemmap_tail;
+ struct page *vmemmap_tail;
int nid, ret = 0;
VM_WARN_ON_ONCE_FOLIO(!folio_test_hugetlb(folio), folio);
@@ -531,9 +528,6 @@ static int __hugetlb_vmemmap_optimize_folio(const struct hstate *h,
goto out;
}
- vmemmap_head = list_first_entry(vmemmap_pages, struct page, lru);
- copy_page(page_to_virt(vmemmap_head), folio);
-
vmemmap_start = (unsigned long)&folio->page;
vmemmap_end = vmemmap_start + hugetlb_vmemmap_size(h);
--
2.54.0
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v2 06/12] mm: hugetlb_vmemmap: rename vmemmap_remap_pte() to vmemmap_optimize_pte()
2026-11-02 3:25 [PATCH v2 00/12] mm: Decouple folio-based vmemmap optimization from HugeTLB Muchun Song
` (4 preceding siblings ...)
2026-11-02 3:25 ` [PATCH v2 05/12] mm: hugetlb_vmemmap: unify writable vmemmap page preparation Muchun Song
@ 2026-11-02 3:25 ` Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 07/12] mm: hugetlb_vmemmap: simplify shared tail page handling Muchun Song
` (5 subsequent siblings)
11 siblings, 1 reply; 28+ messages in thread
From: Muchun Song @ 2026-11-02 3:25 UTC (permalink / raw)
To: Oscar Salvador, Andrew Morton, Mike Rapoport
Cc: Qi Zheng, Lance Yang, James Houghton, David Hildenbrand,
Muchun Song, linux-mm, linux-kernel, Muchun Song
Both optimization and restoration update vmemmap PTEs by remapping them,
so "remap" does not identify the callback's role. Use "optimize" for the
optimization callback to match "restore" and make the two directions
explicit and consistent.
No functional change intended.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Reviewed-by: Lance Yang <lance.yang@linux.dev>
Reviewed-by: Qi Zheng <qi.zheng@linux.dev>
---
v2:
- Collect Reviewed-by from Lance Yang and Qi Zheng
---
mm/hugetlb_vmemmap.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index c9635679a36d..5e84004db4b8 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -213,8 +213,8 @@ static struct page *vmemmap_dup_page_from_list(const struct page *src,
return dst;
}
-static void vmemmap_remap_pte(pte_t *pte, unsigned long addr,
- struct vmemmap_remap_walk *walk)
+static void vmemmap_optimize_pte(pte_t *pte, unsigned long addr,
+ struct vmemmap_remap_walk *walk)
{
struct page *page = pte_page(ptep_get(pte));
pte_t entry;
@@ -302,7 +302,7 @@ static int vmemmap_remap_free(unsigned long start, unsigned long end,
{
int ret;
struct vmemmap_remap_walk walk = {
- .remap_pte = vmemmap_remap_pte,
+ .remap_pte = vmemmap_optimize_pte,
.vmemmap_tail = vmemmap_tail,
.vmemmap_pages = vmemmap_pages,
.flags = flags,
--
2.54.0
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v2 07/12] mm: hugetlb_vmemmap: simplify shared tail page handling
2026-11-02 3:25 [PATCH v2 00/12] mm: Decouple folio-based vmemmap optimization from HugeTLB Muchun Song
` (5 preceding siblings ...)
2026-11-02 3:25 ` [PATCH v2 06/12] mm: hugetlb_vmemmap: rename vmemmap_remap_pte() to vmemmap_optimize_pte() Muchun Song
@ 2026-11-02 3:25 ` Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 08/12] mm: hugetlb_vmemmap: move alloc_vmemmap_page_list() next to its free helper Muchun Song
` (4 subsequent siblings)
11 siblings, 1 reply; 28+ messages in thread
From: Muchun Song @ 2026-11-02 3:25 UTC (permalink / raw)
To: Oscar Salvador, Andrew Morton, Mike Rapoport
Cc: Qi Zheng, Lance Yang, James Houghton, David Hildenbrand,
Muchun Song, linux-mm, linux-kernel, Muchun Song
The remap walker currently carries a shared tail page selected by its
caller, even though the optimize and restore callbacks can derive the
same information from the folio being processed. This adds redundant
state to the walker and couples its interface to caller-side preparation.
Derive the shared mapping state in the callbacks instead, keeping the
mapping policy with the PTE being processed and simplifying the remap
interface.
No functional change intended.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Reviewed-by: Lance Yang <lance.yang@linux.dev>
Reviewed-by: Qi Zheng <qi.zheng@linux.dev>
---
v2:
- Update the vmemmap page-list contract for shared tail derivation
(follow-up to review by Lance Yang and Qi Zheng)
- Collect Reviewed-by from Lance Yang and Qi Zheng
---
include/linux/vmemmap-optimization.h | 6 +-
mm/hugetlb_vmemmap.c | 90 +++++++++++++---------------
mm/mm_init.c | 6 +-
mm/sparse-vmemmap.c | 7 ++-
4 files changed, 51 insertions(+), 58 deletions(-)
diff --git a/include/linux/vmemmap-optimization.h b/include/linux/vmemmap-optimization.h
index 065840f3c241..858311a71b0c 100644
--- a/include/linux/vmemmap-optimization.h
+++ b/include/linux/vmemmap-optimization.h
@@ -91,11 +91,13 @@ static inline struct page *vmemmap_shared_tail_page(unsigned int order,
}
#endif /* CONFIG_VMEMMAP_OPTIMIZATION */
-static inline bool vmemmap_optimizable_pfn(unsigned long pfn)
+static inline bool vmemmap_optimizable_pfn(unsigned long pfn, unsigned int order)
{
- const unsigned int order = pfn_to_section_compound_order(pfn);
const unsigned long nr_pages = 1UL << order;
+ if (!IS_ENABLED(CONFIG_VMEMMAP_OPTIMIZATION))
+ return false;
+
if (!is_power_of_2(sizeof(struct page)))
return false;
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index 5e84004db4b8..d0af251aa533 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -25,21 +25,17 @@
*
* @remap_pte: called for each lowest-level entry (PTE).
* @nr_walked: the number of walked pte.
- * @vmemmap_tail: the page to be installed as non-first in the vmemmap range
* @vmemmap_pages: list supplying replacement pages for the remap and
* receiving displaced backing pages for the caller to free.
* @flags: used to modify behavior in vmemmap page table walking
* operations.
*/
struct vmemmap_remap_walk {
- void (*remap_pte)(pte_t *pte, unsigned long addr,
+ int (*remap_pte)(pte_t *pte, unsigned long addr,
struct vmemmap_remap_walk *walk);
-
unsigned long nr_walked;
- struct page *vmemmap_tail;
struct list_head *vmemmap_pages;
-
/* Defer safely batchable TLB flushes to the caller. */
#define VMEMMAP_DEFER_TLB_FLUSH BIT(0)
unsigned long flags;
@@ -140,12 +136,14 @@ static int vmemmap_pmd_entry(pmd_t *pmd, unsigned long addr,
static int vmemmap_pte_entry(pte_t *pte, unsigned long addr,
unsigned long next, struct mm_walk *walk)
{
+ int ret;
struct vmemmap_remap_walk *vmemmap_walk = walk->private;
- vmemmap_walk->remap_pte(pte, addr, vmemmap_walk);
- vmemmap_walk->nr_walked++;
+ ret = vmemmap_walk->remap_pte(pte, addr, vmemmap_walk);
+ if (!ret)
+ vmemmap_walk->nr_walked++;
- return 0;
+ return ret;
}
static const struct mm_walk_ops vmemmap_remap_ops = {
@@ -213,53 +211,53 @@ static struct page *vmemmap_dup_page_from_list(const struct page *src,
return dst;
}
-static void vmemmap_optimize_pte(pte_t *pte, unsigned long addr,
- struct vmemmap_remap_walk *walk)
+static inline struct page *folio_vmemmap_shared_tail_page(const struct folio *folio)
{
- struct page *page = pte_page(ptep_get(pte));
- pte_t entry;
-
- /* Remapping the head page requires r/w */
- if (unlikely(walk->nr_walked == 0)) {
- struct page *head;
+ return vmemmap_shared_tail_page(folio_order(folio), folio_zone(folio));
+}
- VM_WARN_ON_ONCE(!PageHead((const struct page *)addr));
+static int vmemmap_optimize_pte(pte_t *pte, unsigned long addr,
+ struct vmemmap_remap_walk *walk)
+{
+ const struct page *page = (const struct page *)addr;
+ const struct folio *folio = page_folio(page);
+ struct page *src = pte_page(ptep_get(pte)), *dst;
- head = vmemmap_dup_page_from_list(page, walk->vmemmap_pages);
- entry = mk_pte(head, PAGE_KERNEL);
+ if (likely(vmemmap_optimizable_pfn(page_to_pfn(page), folio_order(folio)))) {
+ dst = folio_vmemmap_shared_tail_page(folio);
+ if (!dst)
+ return -ENOMEM;
} else {
- VM_WARN_ON_ONCE(!PageTail((const struct page *)addr));
-
- /*
- * Map the shared tail page writable. A lockless get that read
- * a non-zero refcount before the remap can still do its
- * cmpxchg after it. The refcount is zero, so the cmpxchg
- * fails and leaves the page unchanged.
- */
- entry = mk_pte(walk->vmemmap_tail, PAGE_KERNEL);
+ dst = vmemmap_dup_page_from_list(src, walk->vmemmap_pages);
}
- list_add(&page->lru, walk->vmemmap_pages);
- set_pte_at(&init_mm, addr, pte, entry);
+ list_add(&src->lru, walk->vmemmap_pages);
+ /*
+ * Map the shared tail page writable intentionally. A lockless get that
+ * read a non-zero refcount before the remap can still do its cmpxchg
+ * after it. The refcount is zero, so the cmpxchg fails and leaves the
+ * page unchanged.
+ */
+ set_pte_at(&init_mm, addr, pte, mk_pte(dst, PAGE_KERNEL));
+
+ return 0;
}
-static void vmemmap_restore_pte(pte_t *pte, unsigned long addr,
- struct vmemmap_remap_walk *walk)
+static int vmemmap_restore_pte(pte_t *pte, unsigned long addr,
+ struct vmemmap_remap_walk *walk)
{
+ const struct page *page = (const struct page *)addr;
+ const struct folio *folio = page_folio(page);
struct page *src = pte_page(ptep_get(pte)), *dst;
- /*
- * When rolling back vmemmap_remap_free(), keep the copied head page
- * mapping and restore only PTEs currently pointing at the shared tail
- * page.
- */
- if (walk->vmemmap_tail && walk->vmemmap_tail != src)
- return;
-
- VM_WARN_ON_ONCE(PageHead((const struct page *)addr));
+ if (unlikely(!vmemmap_optimizable_pfn(page_to_pfn(page), folio_order(folio))))
+ return 0;
+ VM_WARN_ON_ONCE(folio_vmemmap_shared_tail_page(folio) != src);
dst = vmemmap_dup_page_from_list(src, walk->vmemmap_pages);
set_pte_at(&init_mm, addr, pte, mk_pte(dst, PAGE_KERNEL));
+
+ return 0;
}
/**
@@ -287,7 +285,6 @@ static int vmemmap_remap_split(unsigned long start, unsigned long end)
* to remap.
* @end: end address of the vmemmap virtual address range that we want to
* remap.
- * @vmemmap_tail: the page to be installed as non-first in the vmemmap range
* @vmemmap_pages: input/output list whose first entry supplies the new backing
* page for @start; replaced backing pages are added to the list for
* the caller to free.
@@ -296,14 +293,12 @@ static int vmemmap_remap_split(unsigned long start, unsigned long end)
* Return: %0 on success, negative error code otherwise.
*/
static int vmemmap_remap_free(unsigned long start, unsigned long end,
- struct page *vmemmap_tail,
struct list_head *vmemmap_pages,
unsigned long flags)
{
int ret;
struct vmemmap_remap_walk walk = {
.remap_pte = vmemmap_optimize_pte,
- .vmemmap_tail = vmemmap_tail,
.vmemmap_pages = vmemmap_pages,
.flags = flags,
};
@@ -321,7 +316,6 @@ static int vmemmap_remap_free(unsigned long start, unsigned long end,
*/
walk = (struct vmemmap_remap_walk) {
.remap_pte = vmemmap_restore_pte,
- .vmemmap_tail = vmemmap_tail,
.vmemmap_pages = vmemmap_pages,
.flags = 0,
};
@@ -496,7 +490,6 @@ static int __hugetlb_vmemmap_optimize_folio(const struct hstate *h,
unsigned long flags)
{
unsigned long vmemmap_start, vmemmap_end;
- struct page *vmemmap_tail;
int nid, ret = 0;
VM_WARN_ON_ONCE_FOLIO(!folio_test_hugetlb(folio), folio);
@@ -506,9 +499,6 @@ static int __hugetlb_vmemmap_optimize_folio(const struct hstate *h,
return ret;
nid = folio_nid(folio);
- vmemmap_tail = vmemmap_shared_tail_page(h->order, folio_zone(folio));
- if (!vmemmap_tail)
- return -ENOMEM;
/*
* Very Subtle
@@ -537,7 +527,7 @@ static int __hugetlb_vmemmap_optimize_folio(const struct hstate *h,
* they can be freed by the caller.
*/
ret = vmemmap_remap_free(vmemmap_start, vmemmap_end,
- vmemmap_tail, vmemmap_pages, flags);
+ vmemmap_pages, flags);
out:
if (ret)
folio_clear_hugetlb_vmemmap_optimized(folio);
diff --git a/mm/mm_init.c b/mm/mm_init.c
index d99af80ec752..a9d848c92678 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -914,6 +914,8 @@ void __meminit memmap_init_range(unsigned long size, int nid, unsigned long zone
#endif
for (pfn = start_pfn; pfn < end_pfn; ) {
+ const unsigned int order = pfn_to_section_compound_order(pfn);
+
/*
* There can be holes in boot-time mem_map[]s handed to this
* function. They do not exist on hotplugged memory.
@@ -929,9 +931,7 @@ void __meminit memmap_init_range(unsigned long size, int nid, unsigned long zone
* Vmemmap-optimizable PFNs are backed by shared tail struct pages,
* which have already been initialized during vmemmap population.
*/
- if (vmemmap_optimizable_pfn(pfn)) {
- const unsigned int order = pfn_to_section_compound_order(pfn);
-
+ if (vmemmap_optimizable_pfn(pfn, order)) {
pfn = min(ALIGN(pfn, 1UL << order), end_pfn);
continue;
}
diff --git a/mm/sparse-vmemmap.c b/mm/sparse-vmemmap.c
index 3e9d6617c4bb..b8b55e12f5ba 100644
--- a/mm/sparse-vmemmap.c
+++ b/mm/sparse-vmemmap.c
@@ -241,7 +241,7 @@ static __meminit void *vmemmap_alloc_pte(unsigned long pfn, int node,
struct page *page;
const unsigned int order = pfn_to_section_compound_order(pfn);
- if (!vmemmap_optimizable_pfn(pfn))
+ if (!vmemmap_optimizable_pfn(pfn, order))
return vmemmap_alloc_block_buf(PAGE_SIZE, node, altmap);
/*
@@ -286,6 +286,7 @@ static pte_t * __meminit vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, in
{
pte_t *pte = pte_offset_kernel(pmd, addr);
unsigned long pfn = page_to_pfn((struct page *)addr);
+ const unsigned int order = pfn_to_section_compound_order(pfn);
if (pte_none(ptep_get(pte))) {
pte_t entry;
@@ -296,10 +297,10 @@ static pte_t * __meminit vmemmap_pte_populate(pmd_t *pmd, unsigned long addr, in
entry = pfn_pte(PHYS_PFN(__pa(p)), PAGE_KERNEL);
/* Map the shared tail page as read-only to catch illegal writes. */
- if (vmemmap_optimizable_pfn(pfn))
+ if (vmemmap_optimizable_pfn(pfn, order))
entry = pte_wrprotect(entry);
set_pte_at(&init_mm, addr, pte, entry);
- } else if (WARN_ON_ONCE(vmemmap_optimizable_pfn(pfn)))
+ } else if (WARN_ON_ONCE(vmemmap_optimizable_pfn(pfn, order)))
return NULL;
return pte;
}
--
2.54.0
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v2 08/12] mm: hugetlb_vmemmap: move alloc_vmemmap_page_list() next to its free helper
2026-11-02 3:25 [PATCH v2 00/12] mm: Decouple folio-based vmemmap optimization from HugeTLB Muchun Song
` (6 preceding siblings ...)
2026-11-02 3:25 ` [PATCH v2 07/12] mm: hugetlb_vmemmap: simplify shared tail page handling Muchun Song
@ 2026-11-02 3:25 ` Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 09/12] mm: hugetlb_vmemmap: make vmemmap optimization folio-oriented Muchun Song
` (3 subsequent siblings)
11 siblings, 1 reply; 28+ messages in thread
From: Muchun Song @ 2026-11-02 3:25 UTC (permalink / raw)
To: Oscar Salvador, Andrew Morton, Mike Rapoport
Cc: Qi Zheng, Lance Yang, James Houghton, David Hildenbrand,
Muchun Song, linux-mm, linux-kernel, Muchun Song
Keep alloc_vmemmap_page_list() together with
free_vmemmap_page_list(). This also allows the folio-oriented
optimization helper introduced in a subsequent change to reuse the
allocator without a forward declaration.
No functional change intended.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Reviewed-by: Lance Yang <lance.yang@linux.dev>
Reviewed-by: Qi Zheng <qi.zheng@linux.dev>
---
v2:
- Collect Reviewed-by from Lance Yang and Qi Zheng
---
mm/hugetlb_vmemmap.c | 44 ++++++++++++++++++++++----------------------
1 file changed, 22 insertions(+), 22 deletions(-)
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index d0af251aa533..244cd8a0c78a 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -197,6 +197,28 @@ static void free_vmemmap_page_list(struct list_head *list)
free_vmemmap_page(page);
}
+static int alloc_vmemmap_page_list(struct list_head *head, unsigned int nr_pages,
+ int nid)
+{
+ LIST_HEAD(local);
+
+ for (unsigned int i = 0; i < nr_pages; i++) {
+ struct page *page;
+
+ page = alloc_pages_node(nid, GFP_KERNEL | __GFP_RETRY_MAYFAIL, 0);
+ if (!page) {
+ memmap_pages_add(i);
+ free_vmemmap_page_list(&local);
+ return -ENOMEM;
+ }
+ list_add(&page->lru, &local);
+ }
+ list_splice(&local, head);
+ memmap_pages_add(nr_pages);
+
+ return 0;
+}
+
static struct page *vmemmap_dup_page_from_list(const struct page *src,
struct list_head *vmemmap_pages)
{
@@ -325,28 +347,6 @@ static int vmemmap_remap_free(unsigned long start, unsigned long end,
return ret;
}
-static int alloc_vmemmap_page_list(struct list_head *head, unsigned int nr_pages,
- int nid)
-{
- LIST_HEAD(local);
-
- for (unsigned int i = 0; i < nr_pages; i++) {
- struct page *page;
-
- page = alloc_pages_node(nid, GFP_KERNEL | __GFP_RETRY_MAYFAIL, 0);
- if (!page) {
- memmap_pages_add(i);
- free_vmemmap_page_list(&local);
- return -ENOMEM;
- }
- list_add(&page->lru, &local);
- }
- list_splice(&local, head);
- memmap_pages_add(nr_pages);
-
- return 0;
-}
-
/**
* vmemmap_remap_alloc - remap the vmemmap virtual address range [@start, end)
* to the page which is from the @vmemmap_pages
--
2.54.0
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v2 09/12] mm: hugetlb_vmemmap: make vmemmap optimization folio-oriented
2026-11-02 3:25 [PATCH v2 00/12] mm: Decouple folio-based vmemmap optimization from HugeTLB Muchun Song
` (7 preceding siblings ...)
2026-11-02 3:25 ` [PATCH v2 08/12] mm: hugetlb_vmemmap: move alloc_vmemmap_page_list() next to its free helper Muchun Song
@ 2026-11-02 3:25 ` Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 10/12] mm: hugetlb_vmemmap: make vmemmap restoration folio-oriented Muchun Song
` (2 subsequent siblings)
11 siblings, 1 reply; 28+ messages in thread
From: Muchun Song @ 2026-11-02 3:25 UTC (permalink / raw)
To: Oscar Salvador, Andrew Morton, Mike Rapoport
Cc: Qi Zheng, Lance Yang, James Houghton, David Hildenbrand,
Muchun Song, linux-mm, linux-kernel, Muchun Song
Vmemmap optimization operates on one folio at a time, but the current
interface exposes a raw address range and requires the HugeTLB caller to
prepare part of the operation. This splits the common mechanism across
layers and obscures its natural unit of work.
Make the folio the unit of vmemmap optimization and move the common
mechanics behind a folio-oriented interface. This keeps HugeTLB-specific
eligibility and state in the caller while making the remapping code
applicable to any eligible folio.
No functional change intended.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Reviewed-by: Lance Yang <lance.yang@linux.dev>
Reviewed-by: Qi Zheng <qi.zheng@linux.dev>
---
v2:
- Collect Reviewed-by from Lance Yang and Qi Zheng
---
mm/hugetlb_vmemmap.c | 67 ++++++++++++++++++++------------------------
1 file changed, 31 insertions(+), 36 deletions(-)
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index 244cd8a0c78a..14390ee59ba2 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -233,6 +233,11 @@ static struct page *vmemmap_dup_page_from_list(const struct page *src,
return dst;
}
+static inline unsigned long folio_vmemmap_size(const struct folio *folio)
+{
+ return folio_nr_pages(folio) * sizeof(struct page);
+}
+
static inline struct page *folio_vmemmap_shared_tail_page(const struct folio *folio)
{
return vmemmap_shared_tail_page(folio_order(folio), folio_zone(folio));
@@ -302,29 +307,39 @@ static int vmemmap_remap_split(unsigned long start, unsigned long end)
}
/**
- * vmemmap_remap_free - remap a vmemmap range and collect old backing pages
- * @start: start address of the vmemmap virtual address range that we want
- * to remap.
- * @end: end address of the vmemmap virtual address range that we want to
- * remap.
- * @vmemmap_pages: input/output list whose first entry supplies the new backing
- * page for @start; replaced backing pages are added to the list for
- * the caller to free.
- * @flags: modifications to vmemmap_remap_walk flags
+ * vmemmap_optimize_folio - optimize the vmemmap backing of a folio
+ * @folio: folio whose vmemmap pages should be optimized.
+ * @vmemmap_pages: list holding replacement pages for the remap and displaced
+ * backing pages for the caller to free.
+ * @flags: vmemmap remap control flags passed to vmemmap_remap_walk.
+ *
+ * Remap @folio's vmemmap so optimizable entries point at the shared tail page
+ * while non-optimizable entries are copied into replacement pages. Caller is
+ * responsible for freeing the pages left on the @vmemmap_pages list, even on
+ * failure.
*
* Return: %0 on success, negative error code otherwise.
*/
-static int vmemmap_remap_free(unsigned long start, unsigned long end,
- struct list_head *vmemmap_pages,
- unsigned long flags)
+static int vmemmap_optimize_folio(const struct folio *folio,
+ struct list_head *vmemmap_pages, unsigned long flags)
{
- int ret;
+ int ret, nid = folio_nid(folio);
+ unsigned long start = (unsigned long)folio_page(folio, 0);
+ unsigned long end = start + folio_vmemmap_size(folio);
struct vmemmap_remap_walk walk = {
.remap_pte = vmemmap_optimize_pte,
.vmemmap_pages = vmemmap_pages,
.flags = flags,
};
+ VM_WARN_ON_ONCE_FOLIO(folio_ref_count(folio), folio);
+
+ if (!vmemmap_optimizable_order(folio_order(folio)))
+ return -EOPNOTSUPP;
+
+ if (alloc_vmemmap_page_list(vmemmap_pages, VMEMMAP_OPTIMIZATION_PAGES, nid))
+ return -ENOMEM;
+
ret = vmemmap_remap_range(start, end, &walk);
if (!ret || !walk.nr_walked)
return ret;
@@ -489,16 +504,12 @@ static int __hugetlb_vmemmap_optimize_folio(const struct hstate *h,
struct list_head *vmemmap_pages,
unsigned long flags)
{
- unsigned long vmemmap_start, vmemmap_end;
- int nid, ret = 0;
+ int ret;
VM_WARN_ON_ONCE_FOLIO(!folio_test_hugetlb(folio), folio);
- VM_WARN_ON_ONCE_FOLIO(folio_ref_count(folio), folio);
if (!vmemmap_should_optimize_folio(h, folio))
- return ret;
-
- nid = folio_nid(folio);
+ return 0;
/*
* Very Subtle
@@ -512,23 +523,7 @@ static int __hugetlb_vmemmap_optimize_folio(const struct hstate *h,
* the TLB and clear the flag below.
*/
folio_set_hugetlb_vmemmap_optimized(folio);
-
- if (alloc_vmemmap_page_list(vmemmap_pages, VMEMMAP_OPTIMIZATION_PAGES, nid)) {
- ret = -ENOMEM;
- goto out;
- }
-
- vmemmap_start = (unsigned long)&folio->page;
- vmemmap_end = vmemmap_start + hugetlb_vmemmap_size(h);
-
- /*
- * Remap the vmemmap virtual address range [@vmemmap_start, @vmemmap_end).
- * Add pages previously mapping the range to vmemmap_pages list so that
- * they can be freed by the caller.
- */
- ret = vmemmap_remap_free(vmemmap_start, vmemmap_end,
- vmemmap_pages, flags);
-out:
+ ret = vmemmap_optimize_folio(folio, vmemmap_pages, flags);
if (ret)
folio_clear_hugetlb_vmemmap_optimized(folio);
--
2.54.0
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v2 10/12] mm: hugetlb_vmemmap: make vmemmap restoration folio-oriented
2026-11-02 3:25 [PATCH v2 00/12] mm: Decouple folio-based vmemmap optimization from HugeTLB Muchun Song
` (8 preceding siblings ...)
2026-11-02 3:25 ` [PATCH v2 09/12] mm: hugetlb_vmemmap: make vmemmap optimization folio-oriented Muchun Song
@ 2026-11-02 3:25 ` Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 11/12] mm: hugetlb_vmemmap: unify vmemmap range restoration Muchun Song
2026-11-02 3:25 ` [PATCH v2 12/12] mm: hugetlb_vmemmap: make vmemmap PTE conversion folio-oriented Muchun Song
11 siblings, 1 reply; 28+ messages in thread
From: Muchun Song @ 2026-11-02 3:25 UTC (permalink / raw)
To: Oscar Salvador, Andrew Morton, Mike Rapoport
Cc: Qi Zheng, Lance Yang, James Houghton, David Hildenbrand,
Muchun Song, linux-mm, linux-kernel, Muchun Song
Like optimization, vmemmap restoration operates on one folio at a time,
but the current interface exposes a raw address range and requires the
HugeTLB caller to prepare part of the operation. This splits the common
mechanism across layers and obscures its natural unit of work.
Make the folio the unit of vmemmap restoration and move the common
mechanics behind a folio-oriented interface. This keeps HugeTLB-specific
eligibility and state in the caller and gives both directions of HVO
consistent folio-level interfaces.
No functional change intended.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Reviewed-by: Lance Yang <lance.yang@linux.dev>
Reviewed-by: Qi Zheng <qi.zheng@linux.dev>
---
v2:
- Collect Reviewed-by from Lance Yang and Qi Zheng
---
mm/hugetlb_vmemmap.c | 37 +++++++++++++------------------------
1 file changed, 13 insertions(+), 24 deletions(-)
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index 14390ee59ba2..465a907620ab 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -363,28 +363,29 @@ static int vmemmap_optimize_folio(const struct folio *folio,
}
/**
- * vmemmap_remap_alloc - remap the vmemmap virtual address range [@start, end)
- * to the page which is from the @vmemmap_pages
- * respectively.
- * @start: start address of the vmemmap virtual address range that we want
- * to remap.
- * @end: end address of the vmemmap virtual address range that we want to
- * remap.
- * @flags: modifications to vmemmap_remap_walk flags
+ * vmemmap_restore_folio - restore the vmemmap backing of a folio
+ * @folio: folio whose vmemmap pages should be restored.
+ * @flags: vmemmap remap control flags passed to vmemmap_remap_walk.
+ *
+ * Remap @folio's vmemmap so optimizable entries are restored from the shared
+ * tail page back to distinct backing pages allocated for @folio, while
+ * non-optimizable entries remain unchanged.
*
* Return: %0 on success, negative error code otherwise.
*/
-static int vmemmap_remap_alloc(unsigned long start, unsigned long end,
- unsigned long flags)
+static int vmemmap_restore_folio(const struct folio *folio, unsigned long flags)
{
LIST_HEAD(vmemmap_pages);
- int nid = page_to_nid((struct page *)start);
+ int nid = folio_nid(folio);
+ unsigned long start = (unsigned long)folio_page(folio, 0);
+ unsigned long end = start + folio_vmemmap_size(folio);
struct vmemmap_remap_walk walk = {
.remap_pte = vmemmap_restore_pte,
.vmemmap_pages = &vmemmap_pages,
.flags = flags,
};
+ start += VMEMMAP_OPTIMIZATION_SIZE;
if (alloc_vmemmap_page_list(&vmemmap_pages, (end - start) / PAGE_SIZE, nid))
return -ENOMEM;
@@ -402,7 +403,6 @@ static int __hugetlb_vmemmap_restore_folio(const struct hstate *h,
struct folio *folio, unsigned long flags)
{
int ret;
- unsigned long vmemmap_start, vmemmap_end;
VM_WARN_ON_ONCE_FOLIO(!folio_test_hugetlb(folio), folio);
VM_WARN_ON_ONCE_FOLIO(folio_ref_count(folio), folio);
@@ -410,18 +410,7 @@ static int __hugetlb_vmemmap_restore_folio(const struct hstate *h,
if (!folio_test_hugetlb_vmemmap_optimized(folio))
return 0;
- vmemmap_start = (unsigned long)&folio->page;
- vmemmap_end = vmemmap_start + hugetlb_vmemmap_size(h);
-
- vmemmap_start += VMEMMAP_OPTIMIZATION_SIZE;
-
- /*
- * The pages which the vmemmap virtual address range [@vmemmap_start,
- * @vmemmap_end) are mapped to are freed to the buddy allocator.
- * When a HugeTLB page is freed to the buddy allocator, previously
- * discarded vmemmap pages must be allocated and remapping.
- */
- ret = vmemmap_remap_alloc(vmemmap_start, vmemmap_end, flags);
+ ret = vmemmap_restore_folio(folio, flags);
if (!ret)
folio_clear_hugetlb_vmemmap_optimized(folio);
--
2.54.0
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v2 11/12] mm: hugetlb_vmemmap: unify vmemmap range restoration
2026-11-02 3:25 [PATCH v2 00/12] mm: Decouple folio-based vmemmap optimization from HugeTLB Muchun Song
` (9 preceding siblings ...)
2026-11-02 3:25 ` [PATCH v2 10/12] mm: hugetlb_vmemmap: make vmemmap restoration folio-oriented Muchun Song
@ 2026-11-02 3:25 ` Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 12/12] mm: hugetlb_vmemmap: make vmemmap PTE conversion folio-oriented Muchun Song
11 siblings, 1 reply; 28+ messages in thread
From: Muchun Song @ 2026-11-02 3:25 UTC (permalink / raw)
To: Oscar Salvador, Andrew Morton, Mike Rapoport
Cc: Qi Zheng, Lance Yang, James Houghton, David Hildenbrand,
Muchun Song, linux-mm, linux-kernel, Muchun Song
Restoring an optimized folio and rolling back a partial optimization
both restore a vmemmap range, but the operation is currently implemented
separately in each path.
Use a single range-restoration path for both cases, making rollback an
explicit bounded form of normal restoration.
No functional change intended.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Reviewed-by: Lance Yang <lance.yang@linux.dev>
Reviewed-by: Qi Zheng <qi.zheng@linux.dev>
---
v2:
- Collect Reviewed-by from Lance Yang and Qi Zheng
---
mm/hugetlb_vmemmap.c | 33 ++++++++++++++-------------------
1 file changed, 14 insertions(+), 19 deletions(-)
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index 465a907620ab..370e604c8056 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -287,6 +287,18 @@ static int vmemmap_restore_pte(pte_t *pte, unsigned long addr,
return 0;
}
+static inline int vmemmap_restore_range(unsigned long start, unsigned long end,
+ struct list_head *vmemmap_pages, unsigned long flags)
+{
+ struct vmemmap_remap_walk walk = {
+ .remap_pte = vmemmap_restore_pte,
+ .vmemmap_pages = vmemmap_pages,
+ .flags = flags,
+ };
+
+ return vmemmap_remap_range(start, end, &walk);
+}
+
/**
* vmemmap_remap_split - split the vmemmap virtual address range [@start, @end)
* backing PMDs of the directmap into PTEs
@@ -345,19 +357,7 @@ static int vmemmap_optimize_folio(const struct folio *folio,
return ret;
end = start + walk.nr_walked * PAGE_SIZE;
-
- /*
- * vmemmap_pages contains pages from the previous vmemmap_remap_range()
- * call which failed. These are pages which were removed from
- * the vmemmap. They will be restored in the following call.
- */
- walk = (struct vmemmap_remap_walk) {
- .remap_pte = vmemmap_restore_pte,
- .vmemmap_pages = vmemmap_pages,
- .flags = 0,
- };
-
- vmemmap_remap_range(start, end, &walk);
+ vmemmap_restore_range(start, end, vmemmap_pages, 0);
return ret;
}
@@ -379,17 +379,12 @@ static int vmemmap_restore_folio(const struct folio *folio, unsigned long flags)
int nid = folio_nid(folio);
unsigned long start = (unsigned long)folio_page(folio, 0);
unsigned long end = start + folio_vmemmap_size(folio);
- struct vmemmap_remap_walk walk = {
- .remap_pte = vmemmap_restore_pte,
- .vmemmap_pages = &vmemmap_pages,
- .flags = flags,
- };
start += VMEMMAP_OPTIMIZATION_SIZE;
if (alloc_vmemmap_page_list(&vmemmap_pages, (end - start) / PAGE_SIZE, nid))
return -ENOMEM;
- return vmemmap_remap_range(start, end, &walk);
+ return vmemmap_restore_range(start, end, &vmemmap_pages, flags);
}
static bool vmemmap_optimize_enabled = IS_ENABLED(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON);
--
2.54.0
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH v2 12/12] mm: hugetlb_vmemmap: make vmemmap PTE conversion folio-oriented
2026-11-02 3:25 [PATCH v2 00/12] mm: Decouple folio-based vmemmap optimization from HugeTLB Muchun Song
` (10 preceding siblings ...)
2026-11-02 3:25 ` [PATCH v2 11/12] mm: hugetlb_vmemmap: unify vmemmap range restoration Muchun Song
@ 2026-11-02 3:25 ` Muchun Song
2026-11-02 10:18 ` Mike Rapoport
11 siblings, 1 reply; 28+ messages in thread
From: Muchun Song @ 2026-11-02 3:25 UTC (permalink / raw)
To: Oscar Salvador, Andrew Morton, Mike Rapoport
Cc: Qi Zheng, Lance Yang, James Houghton, David Hildenbrand,
Muchun Song, linux-mm, linux-kernel, Muchun Song
Converting a folio's vmemmap mappings from PMDs to PTEs is a per-folio
operation, but the current interface exposes a raw address range and
hardcodes deferred TLB flushing. This splits the common mechanism
across layers and couples it to the HugeTLB batching policy.
Make the folio the unit of PTE conversion and let callers select the TLB
flush policy through the folio-oriented interface. This keeps
HugeTLB-specific eligibility and batching outside the common operation.
No functional change intended.
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Reviewed-by: Lance Yang <lance.yang@linux.dev>
Reviewed-by: Qi Zheng <qi.zheng@linux.dev>
---
v2:
- Collect Reviewed-by from Lance Yang and Qi Zheng
---
mm/hugetlb_vmemmap.c | 40 +++++++++++++++++++---------------------
1 file changed, 19 insertions(+), 21 deletions(-)
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index 370e604c8056..c04f53f708d2 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -300,20 +300,26 @@ static inline int vmemmap_restore_range(unsigned long start, unsigned long end,
}
/**
- * vmemmap_remap_split - split the vmemmap virtual address range [@start, @end)
- * backing PMDs of the directmap into PTEs
- * @start: start address of the vmemmap virtual address range that we want
- * to remap.
- * @end: end address of the vmemmap virtual address range that we want to
- * remap.
+ * vmemmap_pteify_folio - convert the vmemmap backing of a folio to PTE mappings
+ * @folio: folio whose vmemmap range should be converted to PTE mappings.
+ * @flags: vmemmap remap control flags passed to vmemmap_remap_walk.
+ *
+ * Convert mappings covering @folio's vmemmap into PTE mappings. If TLB flushing
+ * is skipped, the caller must flush TLBs after all pteify operations are complete
+ * before remapping the folio's vmemmap pages.
+ *
* Return: %0 on success, negative error code otherwise.
*/
-static int vmemmap_remap_split(unsigned long start, unsigned long end)
+static int vmemmap_pteify_folio(const struct folio *folio, unsigned long flags)
{
struct vmemmap_remap_walk walk = {
- .remap_pte = NULL,
- .flags = VMEMMAP_DEFER_TLB_FLUSH,
+ .flags = flags,
};
+ unsigned long start = (unsigned long)folio_page(folio, 0);
+ unsigned long end = start + folio_vmemmap_size(folio);
+
+ if (!vmemmap_optimizable_order(folio_order(folio)))
+ return -EOPNOTSUPP;
return vmemmap_remap_range(start, end, &walk);
}
@@ -532,21 +538,13 @@ void hugetlb_vmemmap_optimize_folio(const struct hstate *h, struct folio *folio)
free_vmemmap_page_list(&vmemmap_pages);
}
-static int hugetlb_vmemmap_split_folio(const struct hstate *h, struct folio *folio)
+static int hugetlb_vmemmap_pteify_folio(const struct hstate *h, struct folio *folio,
+ unsigned long flags)
{
- unsigned long vmemmap_start, vmemmap_end;
-
if (!vmemmap_should_optimize_folio(h, folio))
return 0;
- vmemmap_start = (unsigned long)&folio->page;
- vmemmap_end = vmemmap_start + hugetlb_vmemmap_size(h);
-
- /*
- * Split PMDs on the vmemmap virtual address range [@vmemmap_start,
- * @vmemmap_end]
- */
- return vmemmap_remap_split(vmemmap_start, vmemmap_end);
+ return vmemmap_pteify_folio(folio, flags);
}
void hugetlb_vmemmap_optimize_folios(struct hstate *h, struct list_head *folio_list)
@@ -571,7 +569,7 @@ void hugetlb_vmemmap_optimize_folios(struct hstate *h, struct list_head *folio_l
nr_to_optimize++;
- ret = hugetlb_vmemmap_split_folio(h, folio);
+ ret = hugetlb_vmemmap_pteify_folio(h, folio, flags);
/*
* Splitting the PMD requires allocating a page, thus let's fail
--
2.54.0
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v2 01/12] mm: hugetlb_vmemmap: remove HugeTLB-specific vmemmap optimization macros
2026-11-02 3:25 ` [PATCH v2 01/12] mm: hugetlb_vmemmap: remove HugeTLB-specific vmemmap optimization macros Muchun Song
@ 2026-11-02 10:18 ` Mike Rapoport
0 siblings, 0 replies; 28+ messages in thread
From: Mike Rapoport @ 2026-11-02 10:18 UTC (permalink / raw)
To: Muchun Song
Cc: Oscar Salvador, Andrew Morton, Mike Rapoport, Qi Zheng,
Lance Yang, James Houghton, David Hildenbrand, Muchun Song,
linux-mm, linux-kernel
On Mon, 02 Nov 2026 11:25:14 +0800, Muchun Song <songmuchun@bytedance.com> wrote:
> Vmemmap optimization is no longer HugeTLB-specific, so the remaining
> HugeTLB-local reserve macros are redundant.
>
> Replace them with the generic definitions to remove duplication and keep
> the HugeTLB vmemmap code aligned with the common optimization macros.
>
>
> [...]
Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v2 03/12] mm: hugetlb_vmemmap: simplify vmemmap head page handling
2026-11-02 3:25 ` [PATCH v2 03/12] mm: hugetlb_vmemmap: simplify vmemmap head page handling Muchun Song
@ 2026-11-02 10:18 ` Mike Rapoport
0 siblings, 0 replies; 28+ messages in thread
From: Mike Rapoport @ 2026-11-02 10:18 UTC (permalink / raw)
To: Muchun Song
Cc: Oscar Salvador, Andrew Morton, Mike Rapoport, Qi Zheng,
Lance Yang, James Houghton, David Hildenbrand, Muchun Song,
linux-mm, linux-kernel
On Mon, 02 Nov 2026 11:25:16 +0800, Muchun Song <songmuchun@bytedance.com> wrote:
> The new head page is already tracked on the vmemmap page list before
> remapping starts. Passing it separately gives the remap walker two
> ways to refer to the same page and unnecessarily complicates its
> interface.
>
> Use the page list as the single handoff mechanism for the head page,
> removing the redundant state from the walker.
>
> [...]
Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v2 02/12] mm: hugetlb_vmemmap: unify VMEMMAP_{SPLIT,REMAP}_NO_TLB_FLUSH
2026-11-02 3:25 ` [PATCH v2 02/12] mm: hugetlb_vmemmap: unify VMEMMAP_{SPLIT,REMAP}_NO_TLB_FLUSH Muchun Song
@ 2026-11-02 10:18 ` Mike Rapoport
2026-11-02 12:28 ` Muchun Song
0 siblings, 1 reply; 28+ messages in thread
From: Mike Rapoport @ 2026-11-02 10:18 UTC (permalink / raw)
To: Muchun Song
Cc: Oscar Salvador, Andrew Morton, Mike Rapoport, Qi Zheng,
Lance Yang, James Houghton, David Hildenbrand, Muchun Song,
linux-mm, linux-kernel
Hi Muchun,
> VMEMMAP_SPLIT_NO_TLB_FLUSH and VMEMMAP_REMAP_NO_TLB_FLUSH do not
> describe fully independent choices. A split-only walk can defer the
> PMD-split flush to a later global flush, and a remap-only walk can
> defer the final remap flush. However, when a PMD split is followed
> by PTE remapping, the split flush must happen before the PTEs are
> changed, because stale large-page translations must not overlap with
> new small-page translations that map different physical pages.
>
> Replace the two flags with a single VMEMMAP_DEFER_TLB_FLUSH flag. The
> new name reflects that only safely batchable flushes are deferred. Keep
> split-only walks able to defer the flush, but force the split flush
> whenever a remap_pte callback is present. Callers that batch HVO work
> can still defer the final remap flush and issue their own global flush.
>
> Current callers keep the same flushing behavior.
>
> Signed-off-by: Muchun Song <songmuchun@bytedance.com>
> Reviewed-by: Lance Yang <lance.yang@linux.dev>
> Reviewed-by: Qi Zheng <qi.zheng@linux.dev>
> ---
> v2:
> - Rename the unified flag to VMEMMAP_DEFER_TLB_FLUSH and update its
> comment (suggested by Qi Zheng)
> - Update the commit message for the new flag name
> - Collect Reviewed-by from Lance Yang and Qi Zheng
> ---
> mm/hugetlb_vmemmap.c | 23 +++++++++++++----------
> 1 file changed, 13 insertions(+), 10 deletions(-)
>
> diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
> index 4b73d6ec8dcea..63e899cfaac3a 100644
> --- a/mm/hugetlb_vmemmap.c
> +++ b/mm/hugetlb_vmemmap.c
> @@ -42,10 +42,8 @@ struct vmemmap_remap_walk {
> struct list_head *vmemmap_pages;
>
>
> -/* Skip the TLB flush when we split the PMD */
> -#define VMEMMAP_SPLIT_NO_TLB_FLUSH BIT(0)
> -/* Skip the TLB flush when we remap the PTE */
> -#define VMEMMAP_REMAP_NO_TLB_FLUSH BIT(1)
> +/* Defer safely batchable TLB flushes to the caller. */
> +#define VMEMMAP_DEFER_TLB_FLUSH BIT(0)
> unsigned long flags;
> };
>
> @@ -85,7 +83,12 @@ static int vmemmap_split_pmd(pmd_t *pmd, struct page *head, unsigned long start,
> /* Make pte visible before pmd. See comment in pmd_install(). */
> smp_wmb();
> pmd_populate_kernel(&init_mm, pmd, pgtable);
> - if (!(walk->flags & VMEMMAP_SPLIT_NO_TLB_FLUSH))
> + /*
> + * Modern MMUs allow overlapping TLB entries with different mapping
> + * granularities only if they map the same physical address. So
> + * remapping must not proceed without first flushing the TLB.
Are we certain that *all* modern MMUs for *all* architecures that
support hugetlb are fine with skipping TLB flush after a split?
AFAIU, x86::set_memory always flushes TLBs after a large leaf is split.
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v2 04/12] mm: hugetlb_vmemmap: generalize vmemmap page allocation
2026-11-02 3:25 ` [PATCH v2 04/12] mm: hugetlb_vmemmap: generalize vmemmap page allocation Muchun Song
@ 2026-11-02 10:18 ` Mike Rapoport
2026-11-02 12:34 ` Muchun Song
0 siblings, 1 reply; 28+ messages in thread
From: Mike Rapoport @ 2026-11-02 10:18 UTC (permalink / raw)
To: Muchun Song
Cc: Oscar Salvador, Andrew Morton, Mike Rapoport, Qi Zheng,
Lance Yang, James Houghton, David Hildenbrand, Muchun Song,
linux-mm, linux-kernel
Hi Muchun,
> Vmemmap page allocation is currently tied to restoring a virtual range
> and assumes an empty destination list. These constraints prevent it from
> being reused when pages are requested independently of a vmemmap range or
> appended to an existing list.
>
> Generalize the allocator and give it an all-or-nothing list contract so
> the restore and optimization paths can share the same allocation policy.
> This also allows HVO head-page allocation to fail gracefully under memory
> pressure without triggering the OOM killer.
>
> Signed-off-by: Muchun Song <songmuchun@bytedance.com>
> Reviewed-by: Lance Yang <lance.yang@linux.dev>
> Reviewed-by: Qi Zheng <qi.zheng@linux.dev>
> ---
> v2:
> - Collect Reviewed-by from Lance Yang and Qi Zheng
> ---
> mm/hugetlb_vmemmap.c | 39 ++++++++++++++++++---------------------
> 1 file changed, 18 insertions(+), 21 deletions(-)
>
> diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
> index 0e5b2b182d1bf..4b20a8efc0678 100644
> --- a/mm/hugetlb_vmemmap.c
> +++ b/mm/hugetlb_vmemmap.c
> @@ -334,28 +334,26 @@ static int vmemmap_remap_free(unsigned long start, unsigned long end,
> return ret;
> }
>
> -static int alloc_vmemmap_page_list(unsigned long start, unsigned long end,
> - struct list_head *list)
> +static int alloc_vmemmap_page_list(struct list_head *head, unsigned int nr_pages,
> + int nid)
Please indent with two tabs, like David likes :)
Applies throughout the series.
> {
> - gfp_t gfp_mask = GFP_KERNEL | __GFP_RETRY_MAYFAIL;
> - unsigned long nr_pages = (end - start) >> PAGE_SHIFT;
> - int nid = page_to_nid((struct page *)start);
> - struct page *page, *next;
> - int i;
> + LIST_HEAD(local);
Can we use a better name here please? E.g new_pages?
>
> - for (i = 0; i < nr_pages; i++) {
> - page = alloc_pages_node(nid, gfp_mask, 0);
> - if (!page)
> - goto out;
> - list_add(&page->lru, list);
> + for (unsigned int i = 0; i < nr_pages; i++) {
> + struct page *page;
> +
> + page = alloc_pages_node(nid, GFP_KERNEL | __GFP_RETRY_MAYFAIL, 0);
> + if (!page) {
I think goto err_do_cleanup is cleaner than ad-hok cleanups
> + memmap_pages_add(i);
This is confusing. Updating counters only to use
free_vmemmap_page_list() seems worse choice than walking the list and
__free_page().
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v2 05/12] mm: hugetlb_vmemmap: unify writable vmemmap page preparation
2026-11-02 3:25 ` [PATCH v2 05/12] mm: hugetlb_vmemmap: unify writable vmemmap page preparation Muchun Song
@ 2026-11-02 10:18 ` Mike Rapoport
0 siblings, 0 replies; 28+ messages in thread
From: Mike Rapoport @ 2026-11-02 10:18 UTC (permalink / raw)
To: Muchun Song
Cc: Oscar Salvador, Andrew Morton, Mike Rapoport, Qi Zheng,
Lance Yang, James Houghton, David Hildenbrand, Muchun Song,
linux-mm, linux-kernel
On Mon, 02 Nov 2026 11:25:18 +0800, Muchun Song <songmuchun@bytedance.com> wrote:
> Installing a writable vmemmap page requires consuming a replacement page,
> copying the current contents into it and making the copy visible before
> publishing the new mapping. HVO currently splits this sequence between
> the page-table walker and its caller depending on the remap path.
>
> Centralize the sequence in the walker so all writable replacements follow
> the same copy-and-publish protocol. This keeps page preparation with the
> mapping update and removes path-specific handling from higher-level HVO
> code.
>
> [...]
Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v2 06/12] mm: hugetlb_vmemmap: rename vmemmap_remap_pte() to vmemmap_optimize_pte()
2026-11-02 3:25 ` [PATCH v2 06/12] mm: hugetlb_vmemmap: rename vmemmap_remap_pte() to vmemmap_optimize_pte() Muchun Song
@ 2026-11-02 10:18 ` Mike Rapoport
0 siblings, 0 replies; 28+ messages in thread
From: Mike Rapoport @ 2026-11-02 10:18 UTC (permalink / raw)
To: Muchun Song
Cc: Oscar Salvador, Andrew Morton, Mike Rapoport, Qi Zheng,
Lance Yang, James Houghton, David Hildenbrand, Muchun Song,
linux-mm, linux-kernel
On Mon, 02 Nov 2026 11:25:19 +0800, Muchun Song <songmuchun@bytedance.com> wrote:
> Both optimization and restoration update vmemmap PTEs by remapping them,
> so "remap" does not identify the callback's role. Use "optimize" for the
> optimization callback to match "restore" and make the two directions
> explicit and consistent.
>
> No functional change intended.
>
> [...]
Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v2 07/12] mm: hugetlb_vmemmap: simplify shared tail page handling
2026-11-02 3:25 ` [PATCH v2 07/12] mm: hugetlb_vmemmap: simplify shared tail page handling Muchun Song
@ 2026-11-02 10:18 ` Mike Rapoport
0 siblings, 0 replies; 28+ messages in thread
From: Mike Rapoport @ 2026-11-02 10:18 UTC (permalink / raw)
To: Muchun Song
Cc: Oscar Salvador, Andrew Morton, Mike Rapoport, Qi Zheng,
Lance Yang, James Houghton, David Hildenbrand, Muchun Song,
linux-mm, linux-kernel
> The remap walker currently carries a shared tail page selected by its
> caller, even though the optimize and restore callbacks can derive the
> same information from the folio being processed. This adds redundant
> state to the walker and couples its interface to caller-side preparation.
>
> Derive the shared mapping state in the callbacks instead, keeping the
> mapping policy with the PTE being processed and simplifying the remap
> interface.
>
> No functional change intended.
>
> Signed-off-by: Muchun Song <songmuchun@bytedance.com>
> Reviewed-by: Lance Yang <lance.yang@linux.dev>
> Reviewed-by: Qi Zheng <qi.zheng@linux.dev>
Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v2 08/12] mm: hugetlb_vmemmap: move alloc_vmemmap_page_list() next to its free helper
2026-11-02 3:25 ` [PATCH v2 08/12] mm: hugetlb_vmemmap: move alloc_vmemmap_page_list() next to its free helper Muchun Song
@ 2026-11-02 10:18 ` Mike Rapoport
0 siblings, 0 replies; 28+ messages in thread
From: Mike Rapoport @ 2026-11-02 10:18 UTC (permalink / raw)
To: Muchun Song
Cc: Oscar Salvador, Andrew Morton, Mike Rapoport, Qi Zheng,
Lance Yang, James Houghton, David Hildenbrand, Muchun Song,
linux-mm, linux-kernel
> Keep alloc_vmemmap_page_list() together with
> free_vmemmap_page_list(). This also allows the folio-oriented
> optimization helper introduced in a subsequent change to reuse the
> allocator without a forward declaration.
>
> No functional change intended.
>
> Signed-off-by: Muchun Song <songmuchun@bytedance.com>
> Reviewed-by: Lance Yang <lance.yang@linux.dev>
> Reviewed-by: Qi Zheng <qi.zheng@linux.dev>
With the comments for patch 4 addressed
Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v2 09/12] mm: hugetlb_vmemmap: make vmemmap optimization folio-oriented
2026-11-02 3:25 ` [PATCH v2 09/12] mm: hugetlb_vmemmap: make vmemmap optimization folio-oriented Muchun Song
@ 2026-11-02 10:18 ` Mike Rapoport
0 siblings, 0 replies; 28+ messages in thread
From: Mike Rapoport @ 2026-11-02 10:18 UTC (permalink / raw)
To: Muchun Song
Cc: Oscar Salvador, Andrew Morton, Mike Rapoport, Qi Zheng,
Lance Yang, James Houghton, David Hildenbrand, Muchun Song,
linux-mm, linux-kernel
On Mon, 02 Nov 2026 11:25:22 +0800, Muchun Song <songmuchun@bytedance.com> wrote:
> Vmemmap optimization operates on one folio at a time, but the current
> interface exposes a raw address range and requires the HugeTLB caller to
> prepare part of the operation. This splits the common mechanism across
> layers and obscures its natural unit of work.
>
> Make the folio the unit of vmemmap optimization and move the common
> mechanics behind a folio-oriented interface. This keeps HugeTLB-specific
> eligibility and state in the caller while making the remapping code
> applicable to any eligible folio.
>
> [...]
Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v2 10/12] mm: hugetlb_vmemmap: make vmemmap restoration folio-oriented
2026-11-02 3:25 ` [PATCH v2 10/12] mm: hugetlb_vmemmap: make vmemmap restoration folio-oriented Muchun Song
@ 2026-11-02 10:18 ` Mike Rapoport
0 siblings, 0 replies; 28+ messages in thread
From: Mike Rapoport @ 2026-11-02 10:18 UTC (permalink / raw)
To: Muchun Song
Cc: Oscar Salvador, Andrew Morton, Mike Rapoport, Qi Zheng,
Lance Yang, James Houghton, David Hildenbrand, Muchun Song,
linux-mm, linux-kernel
On Mon, 02 Nov 2026 11:25:23 +0800, Muchun Song <songmuchun@bytedance.com> wrote:
> Like optimization, vmemmap restoration operates on one folio at a time,
> but the current interface exposes a raw address range and requires the
> HugeTLB caller to prepare part of the operation. This splits the common
> mechanism across layers and obscures its natural unit of work.
>
> Make the folio the unit of vmemmap restoration and move the common
> mechanics behind a folio-oriented interface. This keeps HugeTLB-specific
> eligibility and state in the caller and gives both directions of HVO
> consistent folio-level interfaces.
>
> [...]
Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v2 12/12] mm: hugetlb_vmemmap: make vmemmap PTE conversion folio-oriented
2026-11-02 3:25 ` [PATCH v2 12/12] mm: hugetlb_vmemmap: make vmemmap PTE conversion folio-oriented Muchun Song
@ 2026-11-02 10:18 ` Mike Rapoport
0 siblings, 0 replies; 28+ messages in thread
From: Mike Rapoport @ 2026-11-02 10:18 UTC (permalink / raw)
To: Muchun Song
Cc: Oscar Salvador, Andrew Morton, Mike Rapoport, Qi Zheng,
Lance Yang, James Houghton, David Hildenbrand, Muchun Song,
linux-mm, linux-kernel
On Mon, 02 Nov 2026 11:25:25 +0800, Muchun Song <songmuchun@bytedance.com> wrote:
> Converting a folio's vmemmap mappings from PMDs to PTEs is a per-folio
> operation, but the current interface exposes a raw address range and
> hardcodes deferred TLB flushing. This splits the common mechanism
> across layers and couples it to the HugeTLB batching policy.
>
> Make the folio the unit of PTE conversion and let callers select the TLB
> flush policy through the folio-oriented interface. This keeps
> HugeTLB-specific eligibility and batching outside the common operation.
>
> [...]
Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v2 11/12] mm: hugetlb_vmemmap: unify vmemmap range restoration
2026-11-02 3:25 ` [PATCH v2 11/12] mm: hugetlb_vmemmap: unify vmemmap range restoration Muchun Song
@ 2026-11-02 10:18 ` Mike Rapoport
0 siblings, 0 replies; 28+ messages in thread
From: Mike Rapoport @ 2026-11-02 10:18 UTC (permalink / raw)
To: Muchun Song
Cc: Oscar Salvador, Andrew Morton, Mike Rapoport, Qi Zheng,
Lance Yang, James Houghton, David Hildenbrand, Muchun Song,
linux-mm, linux-kernel
On Mon, 02 Nov 2026 11:25:24 +0800, Muchun Song <songmuchun@bytedance.com> wrote:
> Restoring an optimized folio and rolling back a partial optimization
> both restore a vmemmap range, but the operation is currently implemented
> separately in each path.
>
> Use a single range-restoration path for both cases, making rollback an
> explicit bounded form of normal restoration.
>
> [...]
Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v2 02/12] mm: hugetlb_vmemmap: unify VMEMMAP_{SPLIT,REMAP}_NO_TLB_FLUSH
2026-11-02 10:18 ` Mike Rapoport
@ 2026-11-02 12:28 ` Muchun Song
0 siblings, 0 replies; 28+ messages in thread
From: Muchun Song @ 2026-11-02 12:28 UTC (permalink / raw)
To: Mike Rapoport
Cc: Muchun Song, Oscar Salvador, Andrew Morton, Qi Zheng, Lance Yang,
James Houghton, David Hildenbrand, linux-mm, linux-kernel
> On Nov 2, 2026, at 18:18, Mike Rapoport <rppt@kernel.org> wrote:
>
> Hi Muchun,
Hi,
I didn't realized that I'm not using a plain text in my last mail.
It’s my email client's fault. Let me email it again.
>
>> VMEMMAP_SPLIT_NO_TLB_FLUSH and VMEMMAP_REMAP_NO_TLB_FLUSH do not
>> describe fully independent choices. A split-only walk can defer the
>> PMD-split flush to a later global flush, and a remap-only walk can
>> defer the final remap flush. However, when a PMD split is followed
>> by PTE remapping, the split flush must happen before the PTEs are
>> changed, because stale large-page translations must not overlap with
>> new small-page translations that map different physical pages.
>>
>> Replace the two flags with a single VMEMMAP_DEFER_TLB_FLUSH flag. The
>> new name reflects that only safely batchable flushes are deferred. Keep
>> split-only walks able to defer the flush, but force the split flush
>> whenever a remap_pte callback is present. Callers that batch HVO work
>> can still defer the final remap flush and issue their own global flush.
>>
>> Current callers keep the same flushing behavior.
>>
>> Signed-off-by: Muchun Song <songmuchun@bytedance.com>
>> Reviewed-by: Lance Yang <lance.yang@linux.dev>
>> Reviewed-by: Qi Zheng <qi.zheng@linux.dev>
>> ---
>> v2:
>> - Rename the unified flag to VMEMMAP_DEFER_TLB_FLUSH and update its
>> comment (suggested by Qi Zheng)
>> - Update the commit message for the new flag name
>> - Collect Reviewed-by from Lance Yang and Qi Zheng
>> ---
>> mm/hugetlb_vmemmap.c | 23 +++++++++++++----------
>> 1 file changed, 13 insertions(+), 10 deletions(-)
>>
>> diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
>> index 4b73d6ec8dcea..63e899cfaac3a 100644
>> --- a/mm/hugetlb_vmemmap.c
>> +++ b/mm/hugetlb_vmemmap.c
>> @@ -42,10 +42,8 @@ struct vmemmap_remap_walk {
>> struct list_head *vmemmap_pages;
>>
>>
>> -/* Skip the TLB flush when we split the PMD */
>> -#define VMEMMAP_SPLIT_NO_TLB_FLUSH BIT(0)
>> -/* Skip the TLB flush when we remap the PTE */
>> -#define VMEMMAP_REMAP_NO_TLB_FLUSH BIT(1)
>> +/* Defer safely batchable TLB flushes to the caller. */
>> +#define VMEMMAP_DEFER_TLB_FLUSH BIT(0)
>> unsigned long flags;
>> };
>>
>> @@ -85,7 +83,12 @@ static int vmemmap_split_pmd(pmd_t *pmd, struct page *head, unsigned long start,
>> /* Make pte visible before pmd. See comment in pmd_install(). */
>> smp_wmb();
>> pmd_populate_kernel(&init_mm, pmd, pgtable);
>> - if (!(walk->flags & VMEMMAP_SPLIT_NO_TLB_FLUSH))
>> + /*
>> + * Modern MMUs allow overlapping TLB entries with different mapping
>> + * granularities only if they map the same physical address. So
>> + * remapping must not proceed without first flushing the TLB.
>
> Are we certain that *all* modern MMUs for *all* architecures that
> support hugetlb are fine with skipping TLB flush after a split?
I think this is the case on architectures that currently enable
ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP. Of course, the point I want to
further clarify is that the "skip" action here is not really a skip,
but rather a "defer"—the TLB is subsequently flushed in a single batch.
Thanks for your review.
Muchun.
>
> AFAIU, x86::set_memory always flushes TLBs after a large leaf is split.
>
> --
> Sincerely yours,
> Mike.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v2 04/12] mm: hugetlb_vmemmap: generalize vmemmap page allocation
2026-11-02 10:18 ` Mike Rapoport
@ 2026-11-02 12:34 ` Muchun Song
2026-11-02 17:57 ` Mike Rapoport
0 siblings, 1 reply; 28+ messages in thread
From: Muchun Song @ 2026-11-02 12:34 UTC (permalink / raw)
To: Mike Rapoport
Cc: Muchun Song, Oscar Salvador, Andrew Morton, Qi Zheng, Lance Yang,
James Houghton, David Hildenbrand, linux-mm, linux-kernel
> On Nov 2, 2026, at 18:18, Mike Rapoport <rppt@kernel.org> wrote:
>
> Hi Muchun,
Hi,
It's my email client's fault. Let me email it again.
>
>> Vmemmap page allocation is currently tied to restoring a virtual range
>> and assumes an empty destination list. These constraints prevent it from
>> being reused when pages are requested independently of a vmemmap range or
>> appended to an existing list.
>>
>> Generalize the allocator and give it an all-or-nothing list contract so
>> the restore and optimization paths can share the same allocation policy.
>> This also allows HVO head-page allocation to fail gracefully under memory
>> pressure without triggering the OOM killer.
>>
>> Signed-off-by: Muchun Song <songmuchun@bytedance.com>
>> Reviewed-by: Lance Yang <lance.yang@linux.dev>
>> Reviewed-by: Qi Zheng <qi.zheng@linux.dev>
>> ---
>> v2:
>> - Collect Reviewed-by from Lance Yang and Qi Zheng
>> ---
>> mm/hugetlb_vmemmap.c | 39 ++++++++++++++++++---------------------
>> 1 file changed, 18 insertions(+), 21 deletions(-)
>>
>> diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
>> index 0e5b2b182d1bf..4b20a8efc0678 100644
>> --- a/mm/hugetlb_vmemmap.c
>> +++ b/mm/hugetlb_vmemmap.c
>> @@ -334,28 +334,26 @@ static int vmemmap_remap_free(unsigned long start, unsigned long end,
>> return ret;
>> }
>>
>> -static int alloc_vmemmap_page_list(unsigned long start, unsigned long end,
>> - struct list_head *list)
>> +static int alloc_vmemmap_page_list(struct list_head *head, unsigned int nr_pages,
>> + int nid)
>
> Please indent with two tabs, like David likes :)
> Applies throughout the series.
Sure. Should we update scripts/checkpatch.pl in the future for MM,
since this script seems to favor the alignment approach?
>
>> {
>> - gfp_t gfp_mask = GFP_KERNEL | __GFP_RETRY_MAYFAIL;
>> - unsigned long nr_pages = (end - start) >> PAGE_SHIFT;
>> - int nid = page_to_nid((struct page *)start);
>> - struct page *page, *next;
>> - int i;
>> + LIST_HEAD(local);
>
> Can we use a better name here please? E.g new_pages?
Yahs, make sense.
>
>>
>> - for (i = 0; i < nr_pages; i++) {
>> - page = alloc_pages_node(nid, gfp_mask, 0);
>> - if (!page)
>> - goto out;
>> - list_add(&page->lru, list);
>> + for (unsigned int i = 0; i < nr_pages; i++) {
>> + struct page *page;
>> +
>> + page = alloc_pages_node(nid, GFP_KERNEL | __GFP_RETRY_MAYFAIL, 0);
>> + if (!page) {
>
> I think goto err_do_cleanup is cleaner than ad-hok cleanups
I’ll use goto style, either is OK for me.
>
>> + memmap_pages_add(i);
>
> This is confusing. Updating counters only to use
> free_vmemmap_page_list() seems worse choice than walking the list and
> __free_page().
I think the memmap accounting should be done right after each successful
allocation — that way, using free_vmemmap_page_list() becomes more natural
and simple.
Somthing like:
diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
index c04f53f708d2..cab9ec14873d 100644
--- a/mm/hugetlb_vmemmap.c
+++ b/mm/hugetlb_vmemmap.c
@@ -207,14 +207,13 @@ static int alloc_vmemmap_page_list(struct list_head *head, unsigned int nr_pages
page = alloc_pages_node(nid, GFP_KERNEL | __GFP_RETRY_MAYFAIL, 0);
if (!page) {
- memmap_pages_add(i);
free_vmemmap_page_list(&local);
return -ENOMEM;
}
+ memmap_pages_add(1);
list_add(&page->lru, &local);
}
list_splice(&local, head);
- memmap_pages_add(nr_pages);
return 0;
}
Thanks for your review, Mike.
Muchun
>
> --
> Sincerely yours,
> Mike.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH v2 04/12] mm: hugetlb_vmemmap: generalize vmemmap page allocation
2026-11-02 12:34 ` Muchun Song
@ 2026-11-02 17:57 ` Mike Rapoport
0 siblings, 0 replies; 28+ messages in thread
From: Mike Rapoport @ 2026-11-02 17:57 UTC (permalink / raw)
To: Muchun Song
Cc: Muchun Song, Oscar Salvador, Andrew Morton, Qi Zheng, Lance Yang,
James Houghton, David Hildenbrand, linux-mm, linux-kernel
On Mon, Nov 02, 2026 at 08:34:03PM +0800, Muchun Song wrote:
> > On Nov 2, 2026, at 18:18, Mike Rapoport <rppt@kernel.org> wrote:
> >
> > Hi Muchun,
>
> Hi,
>
> It's my email client's fault. Let me email it again.
Things happen :)
> >> Vmemmap page allocation is currently tied to restoring a virtual range
> >> and assumes an empty destination list. These constraints prevent it from
> >> being reused when pages are requested independently of a vmemmap range or
> >> appended to an existing list.
> >>
> >> Generalize the allocator and give it an all-or-nothing list contract so
> >> the restore and optimization paths can share the same allocation policy.
> >> This also allows HVO head-page allocation to fail gracefully under memory
> >> pressure without triggering the OOM killer.
> >>
> >> Signed-off-by: Muchun Song <songmuchun@bytedance.com>
> >> Reviewed-by: Lance Yang <lance.yang@linux.dev>
> >> Reviewed-by: Qi Zheng <qi.zheng@linux.dev>
> >> ---
> >> v2:
> >> - Collect Reviewed-by from Lance Yang and Qi Zheng
> >> ---
> >> mm/hugetlb_vmemmap.c | 39 ++++++++++++++++++---------------------
> >> 1 file changed, 18 insertions(+), 21 deletions(-)
> >>
> >> diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
> >> index 0e5b2b182d1bf..4b20a8efc0678 100644
> >> --- a/mm/hugetlb_vmemmap.c
> >> +++ b/mm/hugetlb_vmemmap.c
> >> @@ -334,28 +334,26 @@ static int vmemmap_remap_free(unsigned long start, unsigned long end,
> >> return ret;
> >> }
> >>
> >> -static int alloc_vmemmap_page_list(unsigned long start, unsigned long end,
> >> - struct list_head *list)
> >> +static int alloc_vmemmap_page_list(struct list_head *head, unsigned int nr_pages,
> >> + int nid)
> >
> > Please indent with two tabs, like David likes :)
> > Applies throughout the series.
>
> Sure. Should we update scripts/checkpatch.pl in the future for MM,
> since this script seems to favor the alignment approach?
Yes, makes sense to me.
> >> - for (i = 0; i < nr_pages; i++) {
> >> - page = alloc_pages_node(nid, gfp_mask, 0);
> >> - if (!page)
> >> - goto out;
> >> - list_add(&page->lru, list);
> >> + for (unsigned int i = 0; i < nr_pages; i++) {
> >> + struct page *page;
> >> +
> >> + page = alloc_pages_node(nid, GFP_KERNEL | __GFP_RETRY_MAYFAIL, 0);
> >> + if (!page) {
> >
> > I think goto err_do_cleanup is cleaner than ad-hok cleanups
>
> I’ll use goto style, either is OK for me.
>
> >
> >> + memmap_pages_add(i);
> >
> > This is confusing. Updating counters only to use
> > free_vmemmap_page_list() seems worse choice than walking the list and
> > __free_page().
>
> I think the memmap accounting should be done right after each successful
> allocation — that way, using free_vmemmap_page_list() becomes more natural
> and simple.
Why are you against using a loop with a plain __free_page()?
We know for sure it's not a reserved page and there is no need in
free_vmemmap_page().
And with memmap_pages_add() per page do a bunch of atomic_long_add()s
instead of one.
BTW, it would have been nice to avoid per-page counters update in
free_vmemmap_page_list() too, but that's not related to this series.
> Somthing like:
>
> diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c
> index c04f53f708d2..cab9ec14873d 100644
> --- a/mm/hugetlb_vmemmap.c
> +++ b/mm/hugetlb_vmemmap.c
> @@ -207,14 +207,13 @@ static int alloc_vmemmap_page_list(struct list_head *head, unsigned int nr_pages
>
> page = alloc_pages_node(nid, GFP_KERNEL | __GFP_RETRY_MAYFAIL, 0);
> if (!page) {
> - memmap_pages_add(i);
> free_vmemmap_page_list(&local);
> return -ENOMEM;
> }
> + memmap_pages_add(1);
> list_add(&page->lru, &local);
> }
> list_splice(&local, head);
> - memmap_pages_add(nr_pages);
>
> return 0;
> }
>
> Thanks for your review, Mike.
>
> Muchun
>
> >
> > --
> > Sincerely yours,
> > Mike.
>
>
--
Sincerely yours,
Mike.
^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2026-11-02 17:57 UTC | newest]
Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-02 3:25 [PATCH v2 00/12] mm: Decouple folio-based vmemmap optimization from HugeTLB Muchun Song
2026-11-02 3:25 ` [PATCH v2 01/12] mm: hugetlb_vmemmap: remove HugeTLB-specific vmemmap optimization macros Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 02/12] mm: hugetlb_vmemmap: unify VMEMMAP_{SPLIT,REMAP}_NO_TLB_FLUSH Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 12:28 ` Muchun Song
2026-11-02 3:25 ` [PATCH v2 03/12] mm: hugetlb_vmemmap: simplify vmemmap head page handling Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 04/12] mm: hugetlb_vmemmap: generalize vmemmap page allocation Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 12:34 ` Muchun Song
2026-11-02 17:57 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 05/12] mm: hugetlb_vmemmap: unify writable vmemmap page preparation Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 06/12] mm: hugetlb_vmemmap: rename vmemmap_remap_pte() to vmemmap_optimize_pte() Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 07/12] mm: hugetlb_vmemmap: simplify shared tail page handling Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 08/12] mm: hugetlb_vmemmap: move alloc_vmemmap_page_list() next to its free helper Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 09/12] mm: hugetlb_vmemmap: make vmemmap optimization folio-oriented Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 10/12] mm: hugetlb_vmemmap: make vmemmap restoration folio-oriented Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 11/12] mm: hugetlb_vmemmap: unify vmemmap range restoration Muchun Song
2026-11-02 10:18 ` Mike Rapoport
2026-11-02 3:25 ` [PATCH v2 12/12] mm: hugetlb_vmemmap: make vmemmap PTE conversion folio-oriented Muchun Song
2026-11-02 10:18 ` Mike Rapoport
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®