[PATCH 36/49] powerpc/mm: use generic vmemmap_shared_tail_page() in compound vmemmap

Muchun Song
2026-04-05T12:57:19Z

The ultimate goal is to unify the vmemmap optimization logic for both
DAX and HugeTLB. To achieve this, all platforms need to align with the
standard HugeTLB approach of using vmemmap_shared_tail_page() for tail
page mappings.

This patch updates PowerPC to utilize vmemmap_shared_tail_page() to
retrieve the pre-allocated and initialized shared tail page, instead of
dynamically looking up and constructing the tail page mapping via
vmemmap_compound_tail_page().

As a byproduct of this alignment, it greatly simplifies the vmemmap
compound page mapping logic in radix_pgtable by removing
vmemmap_compound_tail_page() entirely.

Signed-off-by: Muchun Song <songmuchun@bytedance.com>
---
arch/powerpc/mm/book3s64/radix_pgtable.c | 81 +++---------------------
1 file changed, 9 insertions(+), 72 deletions(-)

diff --git a/arch/powerpc/mm/book3s64/radix_pgtable.c b/arch/powerpc/mm/book3s64/radix_pgtable.c
index ad44883b1030..5ce3deb464d5 100644
--- a/arch/powerpc/mm/book3s64/radix_pgtable.c
+++ b/arch/powerpc/mm/book3s64/radix_pgtable.c
@@ -1256,59 +1256,6 @@ static pte_t * __meminit radix__vmemmap_populate_address(unsigned long addr, int
return pte;
}

-static pte_t * __meminit vmemmap_compound_tail_page(unsigned long addr,
- unsigned long pfn_offset, int node)
-{
- pgd_t *pgd;
- p4d_t *p4d;
- pud_t *pud;
- pmd_t *pmd;
- pte_t *pte;
- unsigned long map_addr;
-
- /* the second vmemmap page which we use for duplication */
- map_addr = addr - pfn_offset * sizeof(struct page) + PAGE_SIZE;
- pgd = pgd_offset_k(map_addr);
- p4d = p4d_offset(pgd, map_addr);
- pud = vmemmap_pud_alloc(p4d, node, map_addr);
- if (!pud)
- return NULL;
- pmd = vmemmap_pmd_alloc(pud, node, map_addr);
- if (!pmd)
- return NULL;
- if (pmd_leaf(*pmd))
- /*
- * The second page is mapped as a hugepage due to a nearby request.
- * Force our mapping to page size without deduplication
- */
- return NULL;
- pte = vmemmap_pte_alloc(pmd, node, map_addr);
- if (!pte)
- return NULL;
- /*
- * Check if there exist a mapping to the left
- */
- if (pte_none(*pte)) {
- /*
- * Populate the head page vmemmap page.
- * It can fall in different pmd, hence
- * vmemmap_populate_address()
- */
- pte = radix__vmemmap_populate_address(map_addr - PAGE_SIZE, node, NULL, NULL);
- if (!pte)
- return NULL;
- /*
- * Populate the tail pages vmemmap page
- */
- pte = radix__vmemmap_pte_populate(pmd, map_addr, node, NULL, NULL);
- if (!pte)
- return NULL;
- vmemmap_verify(pte, node, map_addr, map_addr + PAGE_SIZE);
- return pte;
- }
- return pte;
-}
-
static int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn,
unsigned long start,
unsigned long end, int node,
@@ -1327,6 +1274,13 @@ static int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn,
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
+ const struct mem_section *ms = __pfn_to_section(start_pfn);
+ struct page *tail_page;
+
+ tail_page = vmemmap_shared_tail_page(section_order(ms),
+ &NODE_DATA(node)->node_zones[section_zone(ms)]);
+ if (!tail_page)
+ return -ENOMEM;

for (addr = start; addr < end; addr = next) {

@@ -1358,9 +1312,8 @@ static int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn,
next = addr + PAGE_SIZE;
continue;
} else {
- unsigned long nr_pages = pgmap_vmemmap_nr(pgmap);
+ unsigned long nr_pages = 1L << section_order(ms);
unsigned long pfn_offset = addr_pfn - ALIGN_DOWN(addr_pfn, nr_pages);
- pte_t *tail_page_pte;

/*
* if the address is aligned to huge page size it is the
@@ -1386,24 +1339,8 @@ static int __meminit vmemmap_populate_compound_pages(unsigned long start_pfn,
next = addr + 2 * PAGE_SIZE;
continue;
}
- /*
- * get the 2nd mapping details
- * Also create it if that doesn't exist
- */
- tail_page_pte = vmemmap_compound_tail_page(addr, pfn_offset, node);
- if (!tail_page_pte) {
-
- pte = radix__vmemmap_pte_populate(pmd, addr, node, NULL, NULL);
- if (!pte)
- return -ENOMEM;
- vmemmap_verify(pte, node, addr, addr + PAGE_SIZE);
-
- addr_pfn += 1;
- next = addr + PAGE_SIZE;
- continue;
- }

- pte = radix__vmemmap_pte_populate(pmd, addr, node, NULL, pte_page(*tail_page_pte));
+ pte = radix__vmemmap_pte_populate(pmd, addr, node, NULL, tail_page);
if (!pte)
return -ENOMEM;
vmemmap_verify(pte, node, addr, addr + PAGE_SIZE);
--
2.20.1