From: Wen Jiang <redacted>
This patchset accelerates ioremap, vmalloc, and vmap when the memory is
physically fully or partially contiguous. Two techniques are used:
1. Avoid page table rewalk when setting PTEs/PMDs for multiple memory
segments
2. Use batched mappings wherever possible in both vmalloc and ARM64
layers
Besides accelerating the mapping path, this also enables large
mappings (PMD and cont-PTE) for vmap, which are currently not
supported.
Patches 1-4 decouple the PTE-level block mapping path from HugeTLB.
Previously vmap_pte_range() installed cont-PTE mappings by reusing
set_huge_pte_at() and huge_ptep_get_and_clear(), which are HugeTLB
helpers gated by CONFIG_HUGETLB_PAGE. This made the feature silently
unavailable on CONFIG_HUGETLB_PAGE=n kernels, and coupled mm/vmalloc.c
to HugeTLB internals. Patches 1 and 2 add the arm64 and powerpc/8xx
implementations of pte_set_huge()/pte_clear_huge(), which join the
existing pmd/pud_set_huge() family. Patch 3 adds the generic fallbacks
and converts mm/vmalloc.c over. Patch 4 then removes the now-dead
init_mm special case from arm64's clear_flush().
Patch 5 extends ARM64 arch_vmap_pte_range_map_size() to batch multiple
CONT_PTE blocks in one call instead of one at a time.
Patch 6 extracts a common helper vmap_set_ptes() that consolidates PTE
mapping logic for the ioremap and vmalloc/vmap paths, handling both
CONT_PTE and regular PTE mappings. This prepares for the next patch.
Patch 7 extends the page table walk path to support page shifts other
than PAGE_SHIFT and eliminates the page table rewalk for huge vmalloc
mappings. The function is renamed from vmap_small_pages_range_noflush()
to vmap_pages_range_noflush_walk().
Patch 8 extracts vm_shift() to consolidate vmalloc mapping shift
selection for reuse in the batching path.
Patches 9-10 add huge vmap support for contiguous pages, including
support for non-compound pages with pfn alignment verification.
On the RK3588 8-core ARM64 SoC, with tasks pinned to a little core and
the performance CPUfreq policy enabled, benchmark results:
* ioremap(1 MB): 1.35x faster (3407 ns -> 2526 ns)
* vmalloc(1 MB) mapping time (excluding allocation) with
VM_ALLOW_HUGE_VMAP: 1.42x faster (5.00 us -> 3.53 us)
* vmap(100MB) with order-8 pages: 8.3x faster (1235 us -> 149 us)
Many thanks to Xueyuan Chen for his testing efforts on RK3588 boards.
Large vmap() mappings were also tested by Leo Yan with ARM trace buffer
units, including TRBE and SPE. These units use the CPU page tables for
address translation when writing trace data to DRAM, so using larger
vmap() mapping granules can reduce TLB pressure on the trace writer.
The TRBE test used a 1G CoreSight ETM AUX buffer. Across five runs on an
isolated CPU, the average results were:
* dtlb_walk: 68.4 -> 59.4 (-13.16%)
* l1d_tlb_refill: 155.8 -> 119.6 (-23.23%)
* l2d_tlb_refill: 161435.8 -> 495.0 (-99.69%)
The SPE test used a 512M ARM SPE AUX buffer. Across five runs on an
isolated CPU, the average results were:
* dtlb_walk: 1710.4 -> 1315.6 (-23.08%)
* l1d_tlb_refill: 16000.0 -> 15950.2 (-0.31%)
* l2d_tlb_refill: 4796.0 -> 2931.2 (-38.88%)
These results show that enabling larger vmap() mappings can materially
reduce page table walks and TLB refills for large trace buffers.
Many thanks to Leo Yan for his testing efforts on ARM trace buffers.
Changes since v7:
- v7's patch 1 (which extended the hugetlb helpers in
arch/arm64/mm/hugetlbpage.c) is replaced by pte_set_huge()/
pte_clear_huge(), split across patches 1-3 so that the arm64,
powerpc/8xx and generic changes can be reviewed and acked
independently: patch 1 is arm64 only, patch 2 is powerpc/8xx only,
patch 3 is the generic fallbacks plus the mm/vmalloc.c conversion.
hugetlbpage.c no longer gains the CONT_PTE batching hooks, and patch 4
additionally removes its now-dead init_mm special case. mm/vmalloc.c
no longer includes <linux/hugetlb.h>.
- The generic pte_set_huge()/pte_clear_huge() fallbacks WARN_ON_ONCE()
instead of silently doing nothing. They only exist to keep the build
working on architectures without PTE-level block mappings, where they
are unreachable (patch 3).
- Patch 5 (v7 patch 2): use round_down(size, CONT_PTE_SIZE) instead of
rounddown_pow_of_two(size), since pte_set_huge() takes the size directly
without an ilog2() roundtrip. This lets a single call span several
CONT_PTE blocks: a 768K (512K + 256K) ioremap is now 1.25x faster than
v7.
- Patch 6 (v7 patch 3): vmap_set_ptes() now calls pte_set_huge() instead
of the hugetlb path.
- Patch 9 (v7 patch 6): get_vmap_batch_order() takes pages+i instead of a
separate idx argument, and applies pfn alignment limit to scan length
rather than to the resulting order. Renamed idx/map_addr to
batch_idx/batch_start. Dropped Dev's Reviewed-by.
Changes since v6:
- Add a clarifying comment about the reuse of hugetlb helpers
by non-hugetlbfs(vmalloc) mm code (patch 1)
- Expand the arm64/vmalloc commit message and comment to clarify that
multi-CONT_PTE_SIZE values are vmalloc mapping spans, not HugeTLB
hstate sizes (patch 2)
- Move the local steps variable change in vmap_pte_range() into the
vmap_set_ptes() extraction patch (patch 3)
- Propagate vmap_pages_pte_range() errors through the upper
vmap_pages_*() levels instead of returning -ENOMEM for all failures
(patch 4)
- Add a preparatory vm_shift() helper patch before the batching patch
(patch 5)
- Guard the PFN alignment clamp in get_vmap_batch_order() against PFN 0
before calling __ffs() (patch 6)
- Fix kmsan_vmap_pages_range_noflush() indentation in the batching path
(patch 6)
Changes since v5:
- No code changes.
- Pick up Reviewed-by and Tested-by tags from Dev, Leo and Uladzislau.
Many thanks!
- Add TRBE/SPE large vmap() test results from Leo Yan to the cover
letter.
Changes since v4:
- Move pgsize update before contig_ptes check (patch 1)
- Use rounddown_pow_of_two instead of __fls in
arch_vmap_pte_range_map_size (patch 2)
- Reword comment to avoid mentioning cont_pte and remove if in
vmap_set_ptes (patch 3)
- Rename vmap_batched() to vmap_pages_range_batched() (patch 5)
- Use batch_end as the batching cursor to avoid an unused start variable
(patch 5)
- Check arch_vmap_pmd_supported before PMD mapping (patch 6)
Changes since v3:
- Squash vmap_pte_range() loop variable fix into patch 4 (patch 3, 4)
- Use shift >= PMD_SHIFT and fix *nr increment in
vmap_pages_pmd_range() (patch 4)
- Pass page_shift directly without capping at PMD_SHIFT (patch 4, 5)
- Add vm_shift() helper and pass pgprot_t to get_vmap_batch_order()
(patch 5)
- Use min(order, __ffs(pfn)) for graceful pfn alignment degradation,
replacing IS_ALIGNED check (patch 5)
- Remove irrelevant ioremap_max_page_shift early-exit (patch 5)
- Add __get_vm_area_node_aligned_caller() wrapper, rename to
vmap_get_aligned_vm_area() (patch 6)
Changes since v2:
- Use __fls instead of fls in arch_vmap_pte_range_map_size (patch 2)
- Add WARN_ON checks in vmap_pages_pmd_range (patch 4)
- Fix flush_cache_vmap to use saved start address instead of the
already-advanced addr (patch 5)
- Rename __vmap_huge() to vmap_batched() (patch 5)
- Add caller parameter and unroll while(1) loop (patch 5)
- Squash patch 7 into patch 5 (stop scanning for compound pages after
encountering small pages)
Changes since v1:
- Fix condition order and use PMD_SIZE instead of CONT_PMD_SIZE in
patch 1 (Dev Jain)
- Squash patch 3+4 and patch 5+7 (Dev Jain)
- Replace "zigzag" with "page table rewalk" in commit messages
(Dev Jain)
- Rename vmap_small_pages_range_noflush() to
vmap_pages_range_noflush_walk() (Dev Jain)
- Extract vmap_set_ptes() as a new patch to consolidate PTE mapping
logic between vmap_pte_range() and vmap_pages_pte_range(), handling
both CONT_PTE and regular mappings (Mike Rapoport)
- Support non-compound pages in get_vmap_batch_order() by falling
back to physical contiguity scanning with pfn alignment check
(Dev Jain, Uladzislau Rezki)
- In get_vmap_batch_order(), filter out orders that the architecture
cannot batch by checking arch_vmap_pte_supported_shift() directly.
This avoids overhead for orders 1-3 on ARM64 CONT_PTE with 4K
pages. (patch 5)
Barry Song (Xiaomi) (4):
arm64/vmalloc: allow arch_vmap_pte_range_map_size() to batch multiple
CONT_PTE
mm/vmalloc: extend page table walk to support larger page_shift sizes
and eliminate page table rewalk
mm/vmalloc: map contiguous pages in batches for vmap() if possible
mm/vmalloc: align vm_area so vmap() can batch mappings
Wen Jiang (6):
arm64/mm: add pte_set_huge() and pte_clear_huge()
powerpc/8xx: add pte_set_huge()
mm/vmalloc: use pte_set_huge()/pte_clear_huge() for PTE-level block
mappings
arm64/hugetlb: drop the init_mm special case in clear_flush()
mm/vmalloc: extract vmap_set_ptes() to consolidate PTE mapping logic
mm/vmalloc: extract vm_shift() to consolidate mapping shift selection
arch/arm64/include/asm/pgtable.h | 6 +
arch/arm64/include/asm/vmalloc.h | 8 +-
arch/arm64/mm/hugetlbpage.c | 5 +-
arch/arm64/mm/mmu.c | 19 ++
arch/powerpc/include/asm/nohash/32/pte-8xx.h | 4 +
arch/powerpc/mm/nohash/8xx.c | 29 ++
include/linux/pgtable.h | 29 ++
mm/vmalloc.c | 268 ++++++++++++++-----
8 files changed, 300 insertions(+), 68 deletions(-)
--
2.34.1
From: Wen Jiang <redacted>
vmap installs PTE-level block mappings (PTE_CONT on arm64) by reusing
set_huge_pte_at() and huge_ptep_get_and_clear(), which are HugeTLB
helpers gated by CONFIG_HUGETLB_PAGE. This makes the feature silently
unavailable on CONFIG_HUGETLB_PAGE=n kernels and couples mm/vmalloc.c to
HugeTLB internals it does not otherwise need.
Add pte_set_huge()/pte_clear_huge() to arm64, joining the existing
pmd_set_huge()/pud_set_huge() family in mm/mmu.c. They build on
__set_ptes() and __get_and_clear_full_ptes() directly, so they do not
depend on CONFIG_HUGETLB_PAGE.
There is no caller yet: mm/vmalloc.c is converted later in this series,
once the generic fallbacks are in place.
Signed-off-by: Wen Jiang <redacted>
---
arch/arm64/include/asm/pgtable.h | 6 ++++++
arch/arm64/mm/mmu.c | 19 +++++++++++++++++++
2 files changed, 25 insertions(+)
From: Wen Jiang <redacted>
vmap installs PTE-level block mappings (SZ_16K/SZ_512K on powerpc/8xx)
by reusing set_huge_pte_at(), a HugeTLB helper gated by
CONFIG_HUGETLB_PAGE. This makes the feature silently unavailable on
CONFIG_HUGETLB_PAGE=n kernels and couples mm/vmalloc.c to HugeTLB
internals it does not otherwise need.
Add pte_set_huge() to powerpc/8xx, next to the existing
pmd_clear_huge()/pud_clear_huge() in mm/nohash/8xx.c. It builds the huge
PTE and writes it into the backing cells directly, without going through
set_huge_pte_at(), so it does not depend on CONFIG_HUGETLB_PAGE.
No pte_clear_huge() is needed: 8xx does not implement
arch_vmap_pte_range_unmap_size(), so the vmap unmap path never takes the
block-mapping branch.
In practice PPC_8xx selects HUGETLBFS unconditionally, so
CONFIG_HUGETLB_PAGE=n does not occur there today; this is a decoupling
cleanup rather than a new configuration.
There is no caller yet: mm/vmalloc.c is converted later in this series,
once the generic fallbacks are in place.
Signed-off-by: Wen Jiang <redacted>
---
arch/powerpc/include/asm/nohash/32/pte-8xx.h | 4 +++
arch/powerpc/mm/nohash/8xx.c | 29 ++++++++++++++++++++
2 files changed, 33 insertions(+)
From: Wen Jiang <redacted>
vmap installs PTE-level block mappings by reusing set_huge_pte_at() and
huge_ptep_get_and_clear() under #ifdef CONFIG_HUGETLB_PAGE. This makes
the feature silently unavailable on CONFIG_HUGETLB_PAGE=n kernels and
couples mm/vmalloc.c to HugeTLB internals it does not otherwise need.
Now that arm64 and powerpc/8xx provide pte_set_huge() and
pte_clear_huge(), add the generic fallbacks next to the existing
pmd/pud_set_huge() family and convert vmap_pte_range() and
vunmap_pte_range() to the new helpers. The CONFIG_HUGETLB_PAGE guards
around the block-mapping paths are dropped, so PTE-level block mappings
now also work on CONFIG_HUGETLB_PAGE=n kernels, and mm/vmalloc.c no
longer includes <linux/hugetlb.h>.
The fallbacks exist only to keep the build working on architectures
without PTE-level block mapping support. They are unreachable there:
the callers only run when arch_vmap_pte_range_map_size() or
arch_vmap_pte_range_unmap_size() return a size other than PAGE_SIZE,
which requires an arch implementation. WARN_ON_ONCE() makes that
explicit rather than silently doing nothing.
Signed-off-by: Wen Jiang <redacted>
---
include/linux/pgtable.h | 29 +++++++++++++++++++++++++++++
mm/vmalloc.c | 19 ++++++-------------
2 files changed, 35 insertions(+), 13 deletions(-)
@@ -98,7 +97,7 @@ static int vmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,pte_t*pte;u64pfn;structpage*page;-unsignedlongsize=PAGE_SIZE;+unsignedlongsize;if(WARN_ON_ONCE(!PAGE_ALIGNED(end-addr)))return-EINVAL;
@@ -119,17 +118,12 @@ static int vmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,BUG();}-#ifdef CONFIG_HUGETLB_PAGEsize=arch_vmap_pte_range_map_size(addr,end,pfn,max_page_shift);if(size!=PAGE_SIZE){-pte_tentry=pfn_pte(pfn,prot);--entry=arch_make_huge_pte(entry,ilog2(size),0);-set_huge_pte_at(&init_mm,addr,pte,entry,size);+pte_set_huge(pte,addr,PFN_PHYS(pfn),prot,size);pfn+=PFN_DOWN(size);continue;}-#endifset_pte_at(&init_mm,addr,pte,pfn_pte(pfn,prot));pfn++;}while(pte+=PFN_DOWN(size),addr+=size,addr!=end);
@@ -368,25 +362,24 @@ static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,{pte_t*pte;pte_tptent;-unsignedlongsize=PAGE_SIZE;+unsignedlongsize;pte=pte_offset_kernel(pmd,addr);lazy_mmu_mode_enable();do{-#ifdef CONFIG_HUGETLB_PAGEsize=arch_vmap_pte_range_unmap_size(addr,pte);if(size!=PAGE_SIZE){if(WARN_ON(!IS_ALIGNED(addr,size))){addr=ALIGN_DOWN(addr,size);pte=PTR_ALIGN_DOWN(pte,sizeof(*pte)*(size>>PAGE_SHIFT));}-ptent=huge_ptep_get_and_clear(&init_mm,addr,pte,size);+ptent=pte_clear_huge(pte,addr,size);if(WARN_ON(end-addr<size))size=end-addr;-}else-#endif+}else{ptent=ptep_get_and_clear(&init_mm,addr,pte);+}WARN_ON(!pte_none(ptent)&&!pte_present(ptent));}while(pte+=(size>>PAGE_SHIFT),addr+=size,addr!=end);
From: Wen Jiang <redacted>
clear_flush() calls flush_tlb_kernel_range() when mm is &init_mm. This
was added by commit 06fc959fcff7 ("arm64/mm: Support huge pte-mapped
pages in vmap") because vmap called set_huge_pte_at(&init_mm, ...).
Now that vmap uses pte_set_huge()/pte_clear_huge(), mm/vmalloc.c is no
longer a caller of set_huge_pte_at() on arm64. The only remaining
callers passing &init_mm are in arch/powerpc, which uses its own
implementation. Remove the dead branch.
No functional change.
Signed-off-by: Wen Jiang <redacted>
---
arch/arm64/mm/hugetlbpage.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
From: "Barry Song (Xiaomi)" <baohua@kernel.org>
Allow arch_vmap_pte_range_map_size to batch across multiple CONT_PTE
blocks, reducing both PTE setup and TLB flush iterations.
For CONT_PTE_SIZE-aligned ranges, return a mapping size that may cover
multiple CONT_PTE blocks, capped below PMD_SIZE. These sizes are vmalloc
mapping spans, not HugeTLB hstate sizes.
Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
Signed-off-by: Wen Jiang <redacted>
Tested-by: Xueyuan Chen <redacted>
Tested-by: Leo Yan <leo.yan@arm.com>
---
arch/arm64/include/asm/vmalloc.h | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
From: Wen Jiang <redacted>
Extract the common PTE mapping logic from vmap_pte_range() into a
shared helper vmap_set_ptes(). This handles both CONT_PTE and regular
PTE mappings in a single function, preparing for the next patch which
will extend vmap_pages_pte_range() to also use this helper.
No functional change.
Signed-off-by: Wen Jiang <redacted>
Tested-by: Xueyuan Chen <redacted>
Tested-by: Leo Yan <leo.yan@arm.com>
---
mm/vmalloc.c | 36 +++++++++++++++++++++++++++---------
1 file changed, 27 insertions(+), 9 deletions(-)
@@ -98,6 +121,7 @@ static int vmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,u64pfn;structpage*page;unsignedlongsize;+unsignedintsteps;if(WARN_ON_ONCE(!PAGE_ALIGNED(end-addr)))return-EINVAL;
@@ -118,15 +142,9 @@ static int vmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,BUG();}-size=arch_vmap_pte_range_map_size(addr,end,pfn,max_page_shift);-if(size!=PAGE_SIZE){-pte_set_huge(pte,addr,PFN_PHYS(pfn),prot,size);-pfn+=PFN_DOWN(size);-continue;-}-set_pte_at(&init_mm,addr,pte,pfn_pte(pfn,prot));-pfn++;-}while(pte+=PFN_DOWN(size),addr+=size,addr!=end);+size=vmap_set_ptes(pte,addr,end,pfn,prot,max_page_shift);+steps=PFN_DOWN(size);+}while(pte+=steps,pfn+=steps,addr+=size,addr!=end);lazy_mmu_mode_disable();*mask|=PGTBL_PTE_MODIFIED;
From: "Barry Song (Xiaomi)" <baohua@kernel.org>
vmap_pages_range_noflush_walk() (formerly vmap_small_pages_range_noflush())
provides a clean interface by taking struct page **pages and mapping them
via direct PTE iteration. This avoids the page table rewalk seen when
using vmap_range_noflush() for page_shift values other than PAGE_SHIFT.
Extend it to support larger page_shift values, and add PMD- and
contiguous-PTE mappings as well.
Rename it to vmap_pages_range_noflush_walk() since it now handles
more than just small pages.
For vmalloc() allocations with VM_ALLOW_HUGE_VMAP, we no longer need to
iterate over pages one by one via vmap_range_noflush(), which would
otherwise lead to page table rewalk. The code is now unified with the
PAGE_SHIFT case by simply calling vmap_pages_range_noflush_walk().
Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
Signed-off-by: Wen Jiang <redacted>
Tested-by: Xueyuan Chen <redacted>
Tested-by: Leo Yan <leo.yan@arm.com>
Reviewed-by: Dev Jain <dev.jain@arm.com>
---
mm/vmalloc.c | 84 +++++++++++++++++++++++++++++++---------------------
1 file changed, 50 insertions(+), 34 deletions(-)
@@ -535,8 +535,10 @@ void vunmap_range(unsigned long addr, unsigned long end)staticintvmap_pages_pte_range(pmd_t*pmd,unsignedlongaddr,unsignedlongend,pgprot_tprot,structpage**pages,int*nr,-pgtbl_mod_mask*mask)+pgtbl_mod_mask*mask,unsignedintshift){+unsignedlongpfn,size;+unsignedintsteps;interr=0;pte_t*pte;
@@ -567,9 +569,10 @@ static int vmap_pages_pte_range(pmd_t *pmd, unsigned long addr,break;}-set_pte_at(&init_mm,addr,pte,mk_pte(page,prot));-(*nr)++;-}while(pte++,addr+=PAGE_SIZE,addr!=end);+pfn=page_to_pfn(page);+size=vmap_set_ptes(pte,addr,end,pfn,prot,shift);+steps=PFN_DOWN(size);+}while(pte+=steps,*nr+=steps,addr+=size,addr!=end);lazy_mmu_mode_disable();*mask|=PGTBL_PTE_MODIFIED;
@@ -579,60 +582,88 @@ static int vmap_pages_pte_range(pmd_t *pmd, unsigned long addr,staticintvmap_pages_pmd_range(pud_t*pud,unsignedlongaddr,unsignedlongend,pgprot_tprot,structpage**pages,int*nr,-pgtbl_mod_mask*mask)+pgtbl_mod_mask*mask,unsignedintshift){pmd_t*pmd;unsignedlongnext;+interr;pmd=pmd_alloc_track(&init_mm,pud,addr,mask);if(!pmd)return-ENOMEM;do{next=pmd_addr_end(addr,end);-if(vmap_pages_pte_range(pmd,addr,next,prot,pages,nr,mask))-return-ENOMEM;++if(shift>=PMD_SHIFT){+structpage*page=pages[*nr];+phys_addr_tphys_addr;++if(WARN_ON(!page))+return-ENOMEM;+if(WARN_ON(!pfn_valid(page_to_pfn(page))))+return-EINVAL;++phys_addr=page_to_phys(page);++if(vmap_try_huge_pmd(pmd,addr,next,phys_addr,prot,shift)){+*mask|=PGTBL_PMD_MODIFIED;+*nr+=1<<(PMD_SHIFT-PAGE_SHIFT);+continue;+}+}+err=vmap_pages_pte_range(pmd,addr,next,prot,pages,nr,mask,shift);+if(err)+returnerr;}while(pmd++,addr=next,addr!=end);return0;}staticintvmap_pages_pud_range(p4d_t*p4d,unsignedlongaddr,unsignedlongend,pgprot_tprot,structpage**pages,int*nr,-pgtbl_mod_mask*mask)+pgtbl_mod_mask*mask,unsignedintshift){pud_t*pud;unsignedlongnext;+interr;pud=pud_alloc_track(&init_mm,p4d,addr,mask);if(!pud)return-ENOMEM;do{next=pud_addr_end(addr,end);-if(vmap_pages_pmd_range(pud,addr,next,prot,pages,nr,mask))-return-ENOMEM;+err=vmap_pages_pmd_range(pud,addr,next,prot,pages,nr,mask,shift);+if(err)+returnerr;}while(pud++,addr=next,addr!=end);return0;}staticintvmap_pages_p4d_range(pgd_t*pgd,unsignedlongaddr,unsignedlongend,pgprot_tprot,structpage**pages,int*nr,-pgtbl_mod_mask*mask)+pgtbl_mod_mask*mask,unsignedintshift){p4d_t*p4d;unsignedlongnext;+interr;p4d=p4d_alloc_track(&init_mm,pgd,addr,mask);if(!p4d)return-ENOMEM;do{next=p4d_addr_end(addr,end);-if(vmap_pages_pud_range(p4d,addr,next,prot,pages,nr,mask))-return-ENOMEM;+err=vmap_pages_pud_range(p4d,addr,next,prot,pages,nr,mask,shift);+if(err)+returnerr;}while(p4d++,addr=next,addr!=end);return0;}-staticintvmap_small_pages_range_noflush(unsignedlongaddr,unsignedlongend,-pgprot_tprot,structpage**pages)+/*+*Itcantakeanarrayofpageswhicharenotallcontiguous,butit+*mayhavecontiguouschunks,ashintedby@shift.+*/+staticintvmap_pages_range_noflush_walk(unsignedlongaddr,unsignedlongend,+pgprot_tprot,structpage**pages,unsignedintshift){unsignedlongstart=addr;pgd_t*pgd;
@@ -647,7 +678,7 @@ static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,next=pgd_addr_end(addr,end);if(pgd_bad(*pgd))mask|=PGTBL_PGD_MODIFIED;-err=vmap_pages_p4d_range(pgd,addr,next,prot,pages,&nr,&mask);+err=vmap_pages_p4d_range(pgd,addr,next,prot,pages,&nr,&mask,shift);if(err)break;}while(pgd++,addr=next,addr!=end);
@@ -670,27 +701,12 @@ static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,int__vmap_pages_range_noflush(unsignedlongaddr,unsignedlongend,pgprot_tprot,structpage**pages,unsignedintpage_shift){-unsignedinti,nr=(end-addr)>>PAGE_SHIFT;-WARN_ON(page_shift<PAGE_SHIFT);-if(!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMALLOC)||-page_shift==PAGE_SHIFT)-returnvmap_small_pages_range_noflush(addr,end,prot,pages);+if(!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMALLOC))+page_shift=PAGE_SHIFT;-for(i=0;i<nr;i+=1U<<(page_shift-PAGE_SHIFT)){-interr;--err=vmap_range_noflush(addr,addr+(1UL<<page_shift),-page_to_phys(pages[i]),prot,-page_shift);-if(err)-returnerr;--addr+=1UL<<page_shift;-}--return0;+returnvmap_pages_range_noflush_walk(addr,end,prot,pages,page_shift);}intvmap_pages_range_noflush(unsignedlongaddr,unsignedlongend,
From: "Barry Song (Xiaomi)" <baohua@kernel.org>
In many cases, the pages passed to vmap() may include high-order
pages. For example, the systemheap often allocates pages in descending
order: order 8, then 4, then 0. Currently, vmap() iterates over every
page individually—even pages inside a high-order block are handled
one by one.
This patch detects physically contiguous pages (regardless of whether
they are compound or non-compound) by scanning with
num_pages_contiguous(), and maps them as a single contiguous block
whenever possible. The mapping order is determined by taking the
minimum of the contiguous page count and the pfn alignment, allowing
graceful degradation when pfn alignment is less than the contiguous
range.
Pages with the same page_shift are coalesced and mapped via
vmap_pages_range_noflush_walk() to avoid page table rewalk.
As users typically allocate memory in descending orders (e.g.
8 → 4 → 0), once an order-0 page is encountered, we stop scanning
for contiguous pages since subsequent pages are likely order-0 as well.
Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
Co-developed-by: Dev Jain <dev.jain@arm.com>
Signed-off-by: Dev Jain <dev.jain@arm.com>
Signed-off-by: Wen Jiang <redacted>
Tested-by: Xueyuan Chen <redacted>
Tested-by: Leo Yan <leo.yan@arm.com>
---
mm/vmalloc.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 80 insertions(+), 2 deletions(-)
From: "Barry Song (Xiaomi)" <baohua@kernel.org>
Try to align the vmap virtual address to PMD_SHIFT or a
larger PTE mapping size hinted by the architecture, so
contiguous pages can be batch-mapped when setting PMD or
PTE entries. We do not expect any significant overhead for
this.
Add __get_vm_area_node_aligned_caller() as a wrapper over
__get_vm_area_node() to simplify repeated calls with fixed
arguments.
Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
Signed-off-by: Wen Jiang <redacted>
Tested-by: Xueyuan Chen <redacted>
Tested-by: Leo Yan <leo.yan@arm.com>
Reviewed-by: Uladzislau Rezki (Sony) <urezki@gmail.com>
Reviewed-by: Dev Jain <dev.jain@arm.com>
---
mm/vmalloc.c | 35 ++++++++++++++++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)
@@ -3306,6 +3306,14 @@ struct vm_struct *get_vm_area_caller(unsigned long size, unsigned long flags,NUMA_NO_NODE,GFP_KERNEL,caller);}+staticstructvm_struct*__get_vm_area_node_aligned_caller(unsignedlongsize,+unsignedlongalign,unsignedlongflags,constvoid*caller)+{+return__get_vm_area_node(size,align,PAGE_SHIFT,flags,+VMALLOC_START,VMALLOC_END,+NUMA_NO_NODE,GFP_KERNEL,caller);+}+/***find_vm_area-findacontinuouskernelvirtualarea*@addr:baseaddress
@@ -3619,6 +3627,30 @@ static int vmap_pages_range_batched(unsigned long addr, unsigned long end,returnerr;}+staticstructvm_struct*vmap_get_aligned_vm_area(unsignedlongsize,+unsignedlongflags,pgprot_tprot,constvoid*caller)+{+structvm_struct*vm_area;+unsignedintshift;++if(arch_vmap_pmd_supported(prot)&&size>=PMD_SIZE){+vm_area=__get_vm_area_node_aligned_caller(size,PMD_SIZE,+flags,caller);+if(vm_area)+returnvm_area;+}++shift=arch_vmap_pte_supported_shift(size);+if(shift>PAGE_SHIFT){+vm_area=__get_vm_area_node_aligned_caller(size,1UL<<shift,+flags,caller);+if(vm_area)+returnvm_area;+}++return__get_vm_area_node_aligned_caller(size,PAGE_SIZE,flags,caller);+}+/***vmap-mapanarrayofpagesintovirtuallycontiguousspace*@pages:arrayofpagepointers
From: Wen Jiang <redacted>
This patchset accelerates ioremap, vmalloc, and vmap when the memory is
physically fully or partially contiguous. Two techniques are used:
1. Avoid page table rewalk when setting PTEs/PMDs for multiple memory
segments
2. Use batched mappings wherever possible in both vmalloc and ARM64
layers
Besides accelerating the mapping path, this also enables large
mappings (PMD and cont-PTE) for vmap, which are currently not
supported.
This series doesn't apply. I tried to apply it on top of v7.2, v7.3-rc3 and next-20260916
Can you tell how to apply it ?
$ LANG= b4 shazam -l 20260917052933.188679-3-jiangwenxiaomi@gmail.com
Grabbing thread from lore.kernel.org/all/20260917052933.188679-3-jiangwenxiaomi@gmail.com/t.mbox.gz
Checking for newer revisions
Grabbing search results from lore.kernel.org
Analyzing 11 messages in the thread
Analyzing 41 code-review messages
Checking attestation on all messages, may take a moment...
---
✓ [PATCH v8 1/10] arm64/mm: add pte_set_huge() and pte_clear_huge()
+ Link: https://lore.kernel.org/r/20260917052933.188679-2-jiangwenxiaomi@gmail.com
✓ [PATCH v8 2/10] powerpc/8xx: add pte_set_huge()
+ Link: https://lore.kernel.org/r/20260917052933.188679-3-jiangwenxiaomi@gmail.com
✓ [PATCH v8 3/10] mm/vmalloc: use pte_set_huge()/pte_clear_huge() for PTE-level block mappings
+ Link: https://lore.kernel.org/r/20260917052933.188679-4-jiangwenxiaomi@gmail.com
✓ [PATCH v8 4/10] arm64/hugetlb: drop the init_mm special case in clear_flush()
+ Link: https://lore.kernel.org/r/20260917052933.188679-5-jiangwenxiaomi@gmail.com
✓ [PATCH v8 5/10] arm64/vmalloc: allow arch_vmap_pte_range_map_size() to batch multiple CONT_PTE
+ Link: https://lore.kernel.org/r/20260917052933.188679-6-jiangwenxiaomi@gmail.com
✓ [PATCH v8 6/10] mm/vmalloc: extract vmap_set_ptes() to consolidate PTE mapping logic
+ Link: https://lore.kernel.org/r/20260917052933.188679-7-jiangwenxiaomi@gmail.com
✓ [PATCH v8 7/10] mm/vmalloc: extend page table walk to support larger page_shift sizes and eliminate page table rewalk
+ Link: https://lore.kernel.org/r/20260917052933.188679-8-jiangwenxiaomi@gmail.com
✓ [PATCH v8 8/10] mm/vmalloc: extract vm_shift() to consolidate mapping shift selection
+ Link: https://lore.kernel.org/r/20260917052933.188679-9-jiangwenxiaomi@gmail.com
✓ [PATCH v8 9/10] mm/vmalloc: map contiguous pages in batches for vmap() if possible
+ Link: https://lore.kernel.org/r/20260917052933.188679-10-jiangwenxiaomi@gmail.com
✓ [PATCH v8 10/10] mm/vmalloc: align vm_area so vmap() can batch mappings
+ Link: https://lore.kernel.org/r/20260917052933.188679-11-jiangwenxiaomi@gmail.com
---
✓ Signed: DKIM/gmail.com
---
Total patches: 10
---
NOTE: some trailers ignored due to from/email mismatches:
! Trailer: Reviewed-by: Uladzislau Rezki (Sony) [off-list ref]
Msg From: Barry Song [off-list ref]
NOTE: Rerun with -S to apply them anyway
---
Applying: arm64/mm: add pte_set_huge() and pte_clear_huge()
Patch failed at 0001 arm64/mm: add pte_set_huge() and pte_clear_huge()
error: patch failed: arch/arm64/mm/mmu.c:1872
error: arch/arm64/mm/mmu.c: patch does not apply
hint: Use 'git am --show-current-patch=diff' to see the failed patch
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip" instead.
hint: To restore the original branch and stop patching, run "git am --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Thanks
Christophe
Patches 1-4 decouple the PTE-level block mapping path from HugeTLB.
Previously vmap_pte_range() installed cont-PTE mappings by reusing
set_huge_pte_at() and huge_ptep_get_and_clear(), which are HugeTLB
helpers gated by CONFIG_HUGETLB_PAGE. This made the feature silently
unavailable on CONFIG_HUGETLB_PAGE=n kernels, and coupled mm/vmalloc.c
to HugeTLB internals. Patches 1 and 2 add the arm64 and powerpc/8xx
implementations of pte_set_huge()/pte_clear_huge(), which join the
existing pmd/pud_set_huge() family. Patch 3 adds the generic fallbacks
and converts mm/vmalloc.c over. Patch 4 then removes the now-dead
init_mm special case from arm64's clear_flush().
Patch 5 extends ARM64 arch_vmap_pte_range_map_size() to batch multiple
CONT_PTE blocks in one call instead of one at a time.
Patch 6 extracts a common helper vmap_set_ptes() that consolidates PTE
mapping logic for the ioremap and vmalloc/vmap paths, handling both
CONT_PTE and regular PTE mappings. This prepares for the next patch.
Patch 7 extends the page table walk path to support page shifts other
than PAGE_SHIFT and eliminates the page table rewalk for huge vmalloc
mappings. The function is renamed from vmap_small_pages_range_noflush()
to vmap_pages_range_noflush_walk().
Patch 8 extracts vm_shift() to consolidate vmalloc mapping shift
selection for reuse in the batching path.
Patches 9-10 add huge vmap support for contiguous pages, including
support for non-compound pages with pfn alignment verification.
On the RK3588 8-core ARM64 SoC, with tasks pinned to a little core and
the performance CPUfreq policy enabled, benchmark results:
* ioremap(1 MB): 1.35x faster (3407 ns -> 2526 ns)
* vmalloc(1 MB) mapping time (excluding allocation) with
VM_ALLOW_HUGE_VMAP: 1.42x faster (5.00 us -> 3.53 us)
* vmap(100MB) with order-8 pages: 8.3x faster (1235 us -> 149 us)
Many thanks to Xueyuan Chen for his testing efforts on RK3588 boards.
Large vmap() mappings were also tested by Leo Yan with ARM trace buffer
units, including TRBE and SPE. These units use the CPU page tables for
address translation when writing trace data to DRAM, so using larger
vmap() mapping granules can reduce TLB pressure on the trace writer.
The TRBE test used a 1G CoreSight ETM AUX buffer. Across five runs on an
isolated CPU, the average results were:
* dtlb_walk: 68.4 -> 59.4 (-13.16%)
* l1d_tlb_refill: 155.8 -> 119.6 (-23.23%)
* l2d_tlb_refill: 161435.8 -> 495.0 (-99.69%)
The SPE test used a 512M ARM SPE AUX buffer. Across five runs on an
isolated CPU, the average results were:
* dtlb_walk: 1710.4 -> 1315.6 (-23.08%)
* l1d_tlb_refill: 16000.0 -> 15950.2 (-0.31%)
* l2d_tlb_refill: 4796.0 -> 2931.2 (-38.88%)
These results show that enabling larger vmap() mappings can materially
reduce page table walks and TLB refills for large trace buffers.
Many thanks to Leo Yan for his testing efforts on ARM trace buffers.
Changes since v7:
- v7's patch 1 (which extended the hugetlb helpers in
arch/arm64/mm/hugetlbpage.c) is replaced by pte_set_huge()/
pte_clear_huge(), split across patches 1-3 so that the arm64,
powerpc/8xx and generic changes can be reviewed and acked
independently: patch 1 is arm64 only, patch 2 is powerpc/8xx only,
patch 3 is the generic fallbacks plus the mm/vmalloc.c conversion.
hugetlbpage.c no longer gains the CONT_PTE batching hooks, and patch 4
additionally removes its now-dead init_mm special case. mm/vmalloc.c
no longer includes <linux/hugetlb.h>.
- The generic pte_set_huge()/pte_clear_huge() fallbacks WARN_ON_ONCE()
instead of silently doing nothing. They only exist to keep the build
working on architectures without PTE-level block mappings, where they
are unreachable (patch 3).
- Patch 5 (v7 patch 2): use round_down(size, CONT_PTE_SIZE) instead of
rounddown_pow_of_two(size), since pte_set_huge() takes the size directly
without an ilog2() roundtrip. This lets a single call span several
CONT_PTE blocks: a 768K (512K + 256K) ioremap is now 1.25x faster than
v7.
- Patch 6 (v7 patch 3): vmap_set_ptes() now calls pte_set_huge() instead
of the hugetlb path.
- Patch 9 (v7 patch 6): get_vmap_batch_order() takes pages+i instead of a
separate idx argument, and applies pfn alignment limit to scan length
rather than to the resulting order. Renamed idx/map_addr to
batch_idx/batch_start. Dropped Dev's Reviewed-by.
Changes since v6:
- Add a clarifying comment about the reuse of hugetlb helpers
by non-hugetlbfs(vmalloc) mm code (patch 1)
- Expand the arm64/vmalloc commit message and comment to clarify that
multi-CONT_PTE_SIZE values are vmalloc mapping spans, not HugeTLB
hstate sizes (patch 2)
- Move the local steps variable change in vmap_pte_range() into the
vmap_set_ptes() extraction patch (patch 3)
- Propagate vmap_pages_pte_range() errors through the upper
vmap_pages_*() levels instead of returning -ENOMEM for all failures
(patch 4)
- Add a preparatory vm_shift() helper patch before the batching patch
(patch 5)
- Guard the PFN alignment clamp in get_vmap_batch_order() against PFN 0
before calling __ffs() (patch 6)
- Fix kmsan_vmap_pages_range_noflush() indentation in the batching path
(patch 6)
Changes since v5:
- No code changes.
- Pick up Reviewed-by and Tested-by tags from Dev, Leo and Uladzislau.
Many thanks!
- Add TRBE/SPE large vmap() test results from Leo Yan to the cover
letter.
Changes since v4:
- Move pgsize update before contig_ptes check (patch 1)
- Use rounddown_pow_of_two instead of __fls in
arch_vmap_pte_range_map_size (patch 2)
- Reword comment to avoid mentioning cont_pte and remove if in
vmap_set_ptes (patch 3)
- Rename vmap_batched() to vmap_pages_range_batched() (patch 5)
- Use batch_end as the batching cursor to avoid an unused start variable
(patch 5)
- Check arch_vmap_pmd_supported before PMD mapping (patch 6)
Changes since v3:
- Squash vmap_pte_range() loop variable fix into patch 4 (patch 3, 4)
- Use shift >= PMD_SHIFT and fix *nr increment in
vmap_pages_pmd_range() (patch 4)
- Pass page_shift directly without capping at PMD_SHIFT (patch 4, 5)
- Add vm_shift() helper and pass pgprot_t to get_vmap_batch_order()
(patch 5)
- Use min(order, __ffs(pfn)) for graceful pfn alignment degradation,
replacing IS_ALIGNED check (patch 5)
- Remove irrelevant ioremap_max_page_shift early-exit (patch 5)
- Add __get_vm_area_node_aligned_caller() wrapper, rename to
vmap_get_aligned_vm_area() (patch 6)
Changes since v2:
- Use __fls instead of fls in arch_vmap_pte_range_map_size (patch 2)
- Add WARN_ON checks in vmap_pages_pmd_range (patch 4)
- Fix flush_cache_vmap to use saved start address instead of the
already-advanced addr (patch 5)
- Rename __vmap_huge() to vmap_batched() (patch 5)
- Add caller parameter and unroll while(1) loop (patch 5)
- Squash patch 7 into patch 5 (stop scanning for compound pages after
encountering small pages)
Changes since v1:
- Fix condition order and use PMD_SIZE instead of CONT_PMD_SIZE in
patch 1 (Dev Jain)
- Squash patch 3+4 and patch 5+7 (Dev Jain)
- Replace "zigzag" with "page table rewalk" in commit messages
(Dev Jain)
- Rename vmap_small_pages_range_noflush() to
vmap_pages_range_noflush_walk() (Dev Jain)
- Extract vmap_set_ptes() as a new patch to consolidate PTE mapping
logic between vmap_pte_range() and vmap_pages_pte_range(), handling
both CONT_PTE and regular mappings (Mike Rapoport)
- Support non-compound pages in get_vmap_batch_order() by falling
back to physical contiguity scanning with pfn alignment check
(Dev Jain, Uladzislau Rezki)
- In get_vmap_batch_order(), filter out orders that the architecture
cannot batch by checking arch_vmap_pte_supported_shift() directly.
This avoids overhead for orders 1-3 on ARM64 CONT_PTE with 4K
pages. (patch 5)
Barry Song (Xiaomi) (4):
arm64/vmalloc: allow arch_vmap_pte_range_map_size() to batch multiple
CONT_PTE
mm/vmalloc: extend page table walk to support larger page_shift sizes
and eliminate page table rewalk
mm/vmalloc: map contiguous pages in batches for vmap() if possible
mm/vmalloc: align vm_area so vmap() can batch mappings
Wen Jiang (6):
arm64/mm: add pte_set_huge() and pte_clear_huge()
powerpc/8xx: add pte_set_huge()
mm/vmalloc: use pte_set_huge()/pte_clear_huge() for PTE-level block
mappings
arm64/hugetlb: drop the init_mm special case in clear_flush()
mm/vmalloc: extract vmap_set_ptes() to consolidate PTE mapping logic
mm/vmalloc: extract vm_shift() to consolidate mapping shift selection
arch/arm64/include/asm/pgtable.h | 6 +
arch/arm64/include/asm/vmalloc.h | 8 +-
arch/arm64/mm/hugetlbpage.c | 5 +-
arch/arm64/mm/mmu.c | 19 ++
arch/powerpc/include/asm/nohash/32/pte-8xx.h | 4 +
arch/powerpc/mm/nohash/8xx.c | 29 ++
include/linux/pgtable.h | 29 ++
mm/vmalloc.c | 268 ++++++++++++++-----
8 files changed, 300 insertions(+), 68 deletions(-)
Le 17/09/2026 à 14:17, Christophe Leroy (CS GROUP) a écrit :
Hi,
Le 17/09/2026 à 07:29, Wen Jiang a écrit :
quoted
From: Wen Jiang <redacted>
This patchset accelerates ioremap, vmalloc, and vmap when the memory is
physically fully or partially contiguous. Two techniques are used:
1. Avoid page table rewalk when setting PTEs/PMDs for multiple memory
segments
2. Use batched mappings wherever possible in both vmalloc and ARM64
layers
Besides accelerating the mapping path, this also enables large
mappings (PMD and cont-PTE) for vmap, which are currently not
supported.
This series doesn't apply. I tried to apply it on top of v7.2, v7.3-rc3 and next-20260916
Can you tell how to apply it ?
Finaly I was able to apply it on top of v7.1
Christophe
From: Wen Jiang <redacted>
vmap installs PTE-level block mappings by reusing set_huge_pte_at() and
huge_ptep_get_and_clear() under #ifdef CONFIG_HUGETLB_PAGE. This makes
the feature silently unavailable on CONFIG_HUGETLB_PAGE=n kernels and
couples mm/vmalloc.c to HugeTLB internals it does not otherwise need.
Now that arm64 and powerpc/8xx provide pte_set_huge() and
pte_clear_huge(), add the generic fallbacks next to the existing
pmd/pud_set_huge() family and convert vmap_pte_range() and
vunmap_pte_range() to the new helpers. The CONFIG_HUGETLB_PAGE guards
around the block-mapping paths are dropped, so PTE-level block mappings
now also work on CONFIG_HUGETLB_PAGE=n kernels, and mm/vmalloc.c no
longer includes <linux/hugetlb.h>.
The fallbacks exist only to keep the build working on architectures
without PTE-level block mapping support. They are unreachable there:
the callers only run when arch_vmap_pte_range_map_size() or
arch_vmap_pte_range_unmap_size() return a size other than PAGE_SIZE,
which requires an arch implementation. WARN_ON_ONCE() makes that
explicit rather than silently doing nothing.
Signed-off-by: Wen Jiang <redacted>
---
include/linux/pgtable.h | 29 +++++++++++++++++++++++++++++
mm/vmalloc.c | 19 ++++++-------------
2 files changed, 35 insertions(+), 13 deletions(-)
@@ -2134,6 +2134,35 @@ static inline int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)}#endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */+/*+*PTE-levelblockmappingsforvmap.+*+*pte_set_huge()onlyhastobeimplementedbyarchitectureswhose+*arch_vmap_pte_range_map_size()canreturnasizeotherthanPAGE_SIZE.+*/+#ifndef __HAVE_ARCH_PTE_SET_HUGE+staticinlinevoidpte_set_huge(pte_t*ptep,unsignedlongaddr,+phys_addr_tphys,pgprot_tprot,+unsignedlongsize)+{+WARN_ON_ONCE(1);
BUILD_BUG_ON() would be better here.
It should be possible because fallback arch_vmap_pte_range_map_size() will constant-fold PAGE_SIZE so pte_set_huge() will never be called.
quoted hunk
+}+#endif++/*+ * Likewise, pte_clear_huge() only has to be implemented by architectures+ * whose arch_vmap_pte_range_unmap_size() can return a size other than+ * PAGE_SIZE.+ */+#ifndef __HAVE_ARCH_PTE_CLEAR_HUGE+static inline pte_t pte_clear_huge(pte_t *ptep, unsigned long addr,+ unsigned long size)+{+ WARN_ON_ONCE(1);+ return __pte(0);
On Thu, 17 Sept 2026 at 21:56, Christophe Leroy (CS GROUP)
[off-list ref] wrote:
Le 17/09/2026 à 14:17, Christophe Leroy (CS GROUP) a écrit :
quoted
Hi,
Hi Christophe,
Thanks for reviewing,
quoted
Le 17/09/2026 à 07:29, Wen Jiang a écrit :
quoted
From: Wen Jiang <redacted>
This patchset accelerates ioremap, vmalloc, and vmap when the memory is
physically fully or partially contiguous. Two techniques are used:
1. Avoid page table rewalk when setting PTEs/PMDs for multiple memory
segments
2. Use batched mappings wherever possible in both vmalloc and ARM64
layers
Besides accelerating the mapping path, this also enables large
mappings (PMD and cont-PTE) for vmap, which are currently not
supported.
This series doesn't apply. I tried to apply it on top of v7.2, v7.3-rc3
and next-20260916
Can you tell how to apply it ?
Finaly I was able to apply it on top of v7.1
Yes. This series was based on v7.1. I'll rebase it onto the mainline
in the next version.
On Thu, 17 Sept 2026 at 22:00, Christophe Leroy (CS GROUP)
[off-list ref] wrote:
Le 17/09/2026 à 07:29, Wen Jiang a écrit :
quoted
From: Wen Jiang <redacted>
vmap installs PTE-level block mappings by reusing set_huge_pte_at() and
huge_ptep_get_and_clear() under #ifdef CONFIG_HUGETLB_PAGE. This makes
the feature silently unavailable on CONFIG_HUGETLB_PAGE=n kernels and
couples mm/vmalloc.c to HugeTLB internals it does not otherwise need.
Now that arm64 and powerpc/8xx provide pte_set_huge() and
pte_clear_huge(), add the generic fallbacks next to the existing
pmd/pud_set_huge() family and convert vmap_pte_range() and
vunmap_pte_range() to the new helpers. The CONFIG_HUGETLB_PAGE guards
around the block-mapping paths are dropped, so PTE-level block mappings
now also work on CONFIG_HUGETLB_PAGE=n kernels, and mm/vmalloc.c no
longer includes <linux/hugetlb.h>.
The fallbacks exist only to keep the build working on architectures
without PTE-level block mapping support. They are unreachable there:
the callers only run when arch_vmap_pte_range_map_size() or
arch_vmap_pte_range_unmap_size() return a size other than PAGE_SIZE,
which requires an arch implementation. WARN_ON_ONCE() makes that
explicit rather than silently doing nothing.
Signed-off-by: Wen Jiang <redacted>
---
include/linux/pgtable.h | 29 +++++++++++++++++++++++++++++
mm/vmalloc.c | 19 ++++++-------------
2 files changed, 35 insertions(+), 13 deletions(-)
@@ -2134,6 +2134,35 @@ static inline int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)}#endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */+/*+*PTE-levelblockmappingsforvmap.+*+*pte_set_huge()onlyhastobeimplementedbyarchitectureswhose+*arch_vmap_pte_range_map_size()canreturnasizeotherthanPAGE_SIZE.+*/+#ifndef __HAVE_ARCH_PTE_SET_HUGE+staticinlinevoidpte_set_huge(pte_t*ptep,unsignedlongaddr,+phys_addr_tphys,pgprot_tprot,+unsignedlongsize)+{+WARN_ON_ONCE(1);
BUILD_BUG_ON() would be better here.
It should be possible because fallback arch_vmap_pte_range_map_size()
will constant-fold PAGE_SIZE so pte_set_huge() will never be called.
Agreed. These fallbacks exist only to keep the build working on
architectures with PTE-level block mappings and should never actually
be reached, so BUILD_BUG_ON() is right. I'll make that change in v9.
Thanks,
Wen
quoted
+}+#endif++/*+ * Likewise, pte_clear_huge() only has to be implemented by architectures+ * whose arch_vmap_pte_range_unmap_size() can return a size other than+ * PAGE_SIZE.+ */+#ifndef __HAVE_ARCH_PTE_CLEAR_HUGE+static inline pte_t pte_clear_huge(pte_t *ptep, unsigned long addr,+ unsigned long size)+{+ WARN_ON_ONCE(1);+ return __pte(0);
From: Barry Song <baohua@kernel.org> Date: 2026-09-17 21:45:00
On Thu, Sep 17, 2026 at 10:41 PM Wen Jiang [off-list ref] wrote:
On Thu, 17 Sept 2026 at 22:00, Christophe Leroy (CS GROUP)
[off-list ref] wrote:
quoted
Le 17/09/2026 à 07:29, Wen Jiang a écrit :
quoted
From: Wen Jiang <redacted>
vmap installs PTE-level block mappings by reusing set_huge_pte_at() and
huge_ptep_get_and_clear() under #ifdef CONFIG_HUGETLB_PAGE. This makes
the feature silently unavailable on CONFIG_HUGETLB_PAGE=n kernels and
couples mm/vmalloc.c to HugeTLB internals it does not otherwise need.
Now that arm64 and powerpc/8xx provide pte_set_huge() and
pte_clear_huge(), add the generic fallbacks next to the existing
pmd/pud_set_huge() family and convert vmap_pte_range() and
vunmap_pte_range() to the new helpers. The CONFIG_HUGETLB_PAGE guards
around the block-mapping paths are dropped, so PTE-level block mappings
now also work on CONFIG_HUGETLB_PAGE=n kernels, and mm/vmalloc.c no
longer includes <linux/hugetlb.h>.
The fallbacks exist only to keep the build working on architectures
without PTE-level block mapping support. They are unreachable there:
the callers only run when arch_vmap_pte_range_map_size() or
arch_vmap_pte_range_unmap_size() return a size other than PAGE_SIZE,
which requires an arch implementation. WARN_ON_ONCE() makes that
explicit rather than silently doing nothing.
Signed-off-by: Wen Jiang <redacted>
---
include/linux/pgtable.h | 29 +++++++++++++++++++++++++++++
mm/vmalloc.c | 19 ++++++-------------
2 files changed, 35 insertions(+), 13 deletions(-)
@@ -2134,6 +2134,35 @@ static inline int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)}#endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */+/*+*PTE-levelblockmappingsforvmap.+*+*pte_set_huge()onlyhastobeimplementedbyarchitectureswhose+*arch_vmap_pte_range_map_size()canreturnasizeotherthanPAGE_SIZE.+*/+#ifndef __HAVE_ARCH_PTE_SET_HUGE+staticinlinevoidpte_set_huge(pte_t*ptep,unsignedlongaddr,+phys_addr_tphys,pgprot_tprot,+unsignedlongsize)+{+WARN_ON_ONCE(1);
BUILD_BUG_ON() would be better here.
It should be possible because fallback arch_vmap_pte_range_map_size()
will constant-fold PAGE_SIZE so pte_set_huge() will never be called.
Agreed. These fallbacks exist only to keep the build working on
architectures with PTE-level block mappings and should never actually
be reached, so BUILD_BUG_ON() is right. I'll make that change in v9.
I am not quite sure. It won't be called at runtime because
`vmap size`/`unmap size` return `PAGE_SIZE`, so the code won't
reach this branch. But it will still be built.
So, would a `BUILD_BUG_ON()` trigger a build failure here?
Best Regards
Barry
From: Barry Song <baohua@kernel.org> Date: 2026-09-17 23:22:30
On Thu, Sep 17, 2026 at 1:29 PM Wen Jiang [off-list ref] wrote:
From: Wen Jiang <redacted>
vmap installs PTE-level block mappings (PTE_CONT on arm64) by reusing
set_huge_pte_at() and huge_ptep_get_and_clear(), which are HugeTLB
helpers gated by CONFIG_HUGETLB_PAGE. This makes the feature silently
unavailable on CONFIG_HUGETLB_PAGE=n kernels and couples mm/vmalloc.c to
HugeTLB internals it does not otherwise need.
Add pte_set_huge()/pte_clear_huge() to arm64, joining the existing
pmd_set_huge()/pud_set_huge() family in mm/mmu.c. They build on
__set_ptes() and __get_and_clear_full_ptes() directly, so they do not
depend on CONFIG_HUGETLB_PAGE.
There is no caller yet: mm/vmalloc.c is converted later in this series,
once the generic fallbacks are in place.
Signed-off-by: Wen Jiang <redacted>
I tried to apply the patch to mm-new, but it doesn't apply.
% git am raw
Applying: arm64/mm: add pte_set_huge() and pte_clear_huge()
error: patch failed: arch/arm64/mm/mmu.c:1872
error: arch/arm64/mm/mmu.c: patch does not apply
Patch failed at 0001 arm64/mm: add pte_set_huge() and pte_clear_huge()
hint: Use 'git am --show-current-patch=diff' to see the failed patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
% patch -p1 < raw
patching file 'arch/arm64/include/asm/pgtable.h'
patching file 'arch/arm64/mm/mmu.c'
1 out of 1 hunks failed--saving rejects to 'arch/arm64/mm/mmu.c.rej'
[...]
quoted hunk
+void pte_set_huge(pte_t *ptep, unsigned long addr, phys_addr_t phys,+ pgprot_t prot, unsigned long size)+{+ unsigned long pfn = __phys_to_pfn(phys);+ pte_t new_pte = pte_mkcont(pfn_pte(pfn, prot));
Maybe we can drop the `new_` prefix.
quoted hunk
+ unsigned int nr = size >> PAGE_SHIFT;++ VM_WARN_ON(!IS_ALIGNED(pfn, CONT_PTES));+ VM_WARN_ON(!IS_ALIGNED(size, CONT_PTE_SIZE));
Do we also need to check that `addr` is aligned?
Best Regards
Barry
From: Barry Song <baohua@kernel.org> Date: 2026-09-17 23:28:34
On Thu, Sep 17, 2026 at 1:30 PM Wen Jiang [off-list ref] wrote:
From: Wen Jiang <redacted>
vmap installs PTE-level block mappings (SZ_16K/SZ_512K on powerpc/8xx)
by reusing set_huge_pte_at(), a HugeTLB helper gated by
CONFIG_HUGETLB_PAGE. This makes the feature silently unavailable on
CONFIG_HUGETLB_PAGE=n kernels and couples mm/vmalloc.c to HugeTLB
internals it does not otherwise need.
Add pte_set_huge() to powerpc/8xx, next to the existing
pmd_clear_huge()/pud_clear_huge() in mm/nohash/8xx.c. It builds the huge
PTE and writes it into the backing cells directly, without going through
set_huge_pte_at(), so it does not depend on CONFIG_HUGETLB_PAGE.
No pte_clear_huge() is needed: 8xx does not implement
arch_vmap_pte_range_unmap_size(), so the vmap unmap path never takes the
block-mapping branch.
I assume you mean the block-unmapping (or PTE-clearing) branch.
Please make this clear in the changelog.
In practice PPC_8xx selects HUGETLBFS unconditionally, so
CONFIG_HUGETLB_PAGE=n does not occur there today; this is a decoupling
cleanup rather than a new configuration.
There is no caller yet: mm/vmalloc.c is converted later in this series,
once the generic fallbacks are in place.
Signed-off-by: Wen Jiang <redacted>
---
BTW, this patch applies cleanly to the mm-new branch.
Best Regards
Barry
On Thu, Sep 17, 2026 at 10:41 PM Wen Jiang [off-list ref] wrote:
quoted
On Thu, 17 Sept 2026 at 22:00, Christophe Leroy (CS GROUP)
[off-list ref] wrote:
quoted
Le 17/09/2026 à 07:29, Wen Jiang a écrit :
quoted
From: Wen Jiang <redacted>
vmap installs PTE-level block mappings by reusing set_huge_pte_at() and
huge_ptep_get_and_clear() under #ifdef CONFIG_HUGETLB_PAGE. This makes
the feature silently unavailable on CONFIG_HUGETLB_PAGE=n kernels and
couples mm/vmalloc.c to HugeTLB internals it does not otherwise need.
Now that arm64 and powerpc/8xx provide pte_set_huge() and
pte_clear_huge(), add the generic fallbacks next to the existing
pmd/pud_set_huge() family and convert vmap_pte_range() and
vunmap_pte_range() to the new helpers. The CONFIG_HUGETLB_PAGE guards
around the block-mapping paths are dropped, so PTE-level block mappings
now also work on CONFIG_HUGETLB_PAGE=n kernels, and mm/vmalloc.c no
longer includes <linux/hugetlb.h>.
The fallbacks exist only to keep the build working on architectures
without PTE-level block mapping support. They are unreachable there:
the callers only run when arch_vmap_pte_range_map_size() or
arch_vmap_pte_range_unmap_size() return a size other than PAGE_SIZE,
which requires an arch implementation. WARN_ON_ONCE() makes that
explicit rather than silently doing nothing.
Signed-off-by: Wen Jiang <redacted>
---
include/linux/pgtable.h | 29 +++++++++++++++++++++++++++++
mm/vmalloc.c | 19 ++++++-------------
2 files changed, 35 insertions(+), 13 deletions(-)
@@ -2134,6 +2134,35 @@ static inline int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)}#endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */+/*+*PTE-levelblockmappingsforvmap.+*+*pte_set_huge()onlyhastobeimplementedbyarchitectureswhose+*arch_vmap_pte_range_map_size()canreturnasizeotherthanPAGE_SIZE.+*/+#ifndef __HAVE_ARCH_PTE_SET_HUGE+staticinlinevoidpte_set_huge(pte_t*ptep,unsignedlongaddr,+phys_addr_tphys,pgprot_tprot,+unsignedlongsize)+{+WARN_ON_ONCE(1);
BUILD_BUG_ON() would be better here.
It should be possible because fallback arch_vmap_pte_range_map_size()
will constant-fold PAGE_SIZE so pte_set_huge() will never be called.
Agreed. These fallbacks exist only to keep the build working on
architectures with PTE-level block mappings and should never actually
be reached, so BUILD_BUG_ON() is right. I'll make that change in v9.
I am not quite sure. It won't be called at runtime because
`vmap size`/`unmap size` return `PAGE_SIZE`, so the code won't
reach this branch. But it will still be built.
The fallbacks are defined as:
#ifndef arch_vmap_pte_range_map_size
static inline unsigned long arch_vmap_pte_range_map_size(unsigned long addr, unsigned long end,
u64 pfn, unsigned int max_page_shift)
{
return PAGE_SIZE;
}
#endif
#ifndef arch_vmap_pte_range_unmap_size
static inline unsigned long arch_vmap_pte_range_unmap_size(unsigned long addr,
pte_t *ptep)
{
return PAGE_SIZE;
}
#endif
Therefore in:
size = arch_vmap_pte_range_unmap_size(addr, pte);
if (size != PAGE_SIZE) {
GCC knows 'size' is const and its value is PAGE_SIZE, so it won't emit the branch at all.
It is call constant folding, some explanation here: https://en.wikipedia.org/wiki/Constant_folding
So, would a `BUILD_BUG_ON()` trigger a build failure here?
It shouldn't, if it does it is a compiled bug or this is because someone has redefined arch_vmap_pte_range_map_size() and not pte_set_huge() which we'd better know at build time rather than at runtime.
From: Wen Jiang <redacted>
vmap installs PTE-level block mappings (SZ_16K/SZ_512K on powerpc/8xx)
by reusing set_huge_pte_at(), a HugeTLB helper gated by
CONFIG_HUGETLB_PAGE. This makes the feature silently unavailable on
CONFIG_HUGETLB_PAGE=n kernels and couples mm/vmalloc.c to HugeTLB
internals it does not otherwise need.
Add pte_set_huge() to powerpc/8xx, next to the existing
pmd_clear_huge()/pud_clear_huge() in mm/nohash/8xx.c. It builds the huge
PTE and writes it into the backing cells directly, without going through
set_huge_pte_at(), so it does not depend on CONFIG_HUGETLB_PAGE.
No pte_clear_huge() is needed: 8xx does not implement
arch_vmap_pte_range_unmap_size(), so the vmap unmap path never takes the
block-mapping branch.
In practice PPC_8xx selects HUGETLBFS unconditionally, so
CONFIG_HUGETLB_PAGE=n does not occur there today; this is a decoupling
cleanup rather than a new configuration.
8xx selects CONFIG_HUGETLB_PAGE because it uses set_huge_pte_at() to map kernel text with large pages. That was added by commit 34536d780683 ("powerpc/8xx: Add a function to early map kernel via huge pages")
But now pte_set_huge() could be used instead in __early_map_kernel_hugepage() and then CONFIG_HUGETLB_PAGE could be removed.
There is no caller yet: mm/vmalloc.c is converted later in this series,
once the generic fallbacks are in place.
Signed-off-by: Wen Jiang <redacted>
@@ -2134,6 +2134,35 @@ static inline int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)}#endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */+/*+*PTE-levelblockmappingsforvmap.+*+*pte_set_huge()onlyhastobeimplementedbyarchitectureswhose+*arch_vmap_pte_range_map_size()canreturnasizeotherthanPAGE_SIZE.+*/+#ifndef __HAVE_ARCH_PTE_SET_HUGE+staticinlinevoidpte_set_huge(pte_t*ptep,unsignedlongaddr,+phys_addr_tphys,pgprot_tprot,+unsignedlongsize)+{+WARN_ON_ONCE(1);
BUILD_BUG_ON() would be better here.
It should be possible because fallback arch_vmap_pte_range_map_size()
will constant-fold PAGE_SIZE so pte_set_huge() will never be called.
Agreed. These fallbacks exist only to keep the build working on
architectures with PTE-level block mappings and should never actually
be reached, so BUILD_BUG_ON() is right. I'll make that change in v9.
I am not quite sure. It won't be called at runtime because
`vmap size`/`unmap size` return `PAGE_SIZE`, so the code won't
reach this branch. But it will still be built.
The fallbacks are defined as:
#ifndef arch_vmap_pte_range_map_size
static inline unsigned long arch_vmap_pte_range_map_size(unsigned long
addr, unsigned long end,
u64 pfn, unsigned int max_page_shift)
{
return PAGE_SIZE;
}
#endif
#ifndef arch_vmap_pte_range_unmap_size
static inline unsigned long arch_vmap_pte_range_unmap_size(unsigned long
addr,
pte_t *ptep)
{
return PAGE_SIZE;
}
#endif
Therefore in:
size = arch_vmap_pte_range_unmap_size(addr, pte);
if (size != PAGE_SIZE) {
GCC knows 'size' is const and its value is PAGE_SIZE, so it won't emit
the branch at all.
It is call constant folding, some explanation here:
https://en.wikipedia.org/wiki/Constant_folding
quoted
So, would a `BUILD_BUG_ON()` trigger a build failure here?
It shouldn't, if it does it is a compiled bug or this is because someone
has redefined arch_vmap_pte_range_map_size() and not pte_set_huge()
which we'd better know at build time rather than at runtime.
Thanks, Christophe. I was also thinking about compiler optimization. I
was just a bit worried that we're touching the common MM code, which
affects almost all architectures, so I'm not quite sure whether this is
supported by all GCC versions used by those architectures.
If it is supported by all of them, I agree that `BUILD_BUG_ON()` is a
perfect approach.
Best Regards
Barry
@@ -2134,6 +2134,35 @@ static inline int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)}#endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */+/*+*PTE-levelblockmappingsforvmap.+*+*pte_set_huge()onlyhastobeimplementedbyarchitectureswhose+*arch_vmap_pte_range_map_size()canreturnasizeotherthanPAGE_SIZE.+*/+#ifndef __HAVE_ARCH_PTE_SET_HUGE+staticinlinevoidpte_set_huge(pte_t*ptep,unsignedlongaddr,+phys_addr_tphys,pgprot_tprot,+unsignedlongsize)+{+WARN_ON_ONCE(1);
BUILD_BUG_ON() would be better here.
It should be possible because fallback arch_vmap_pte_range_map_size()
will constant-fold PAGE_SIZE so pte_set_huge() will never be called.
Agreed. These fallbacks exist only to keep the build working on
architectures with PTE-level block mappings and should never actually
be reached, so BUILD_BUG_ON() is right. I'll make that change in v9.
I am not quite sure. It won't be called at runtime because
`vmap size`/`unmap size` return `PAGE_SIZE`, so the code won't
reach this branch. But it will still be built.
So, would a `BUILD_BUG_ON()` trigger a build failure here?
It shouldn't, if it does it is a compiled bug or this is because someone
has redefined arch_vmap_pte_range_map_size() and not pte_set_huge()
which we'd better know at build time rather than at runtime.
Thanks, Christophe. I was also thinking about compiler optimization. I
was just a bit worried that we're touching the common MM code, which
affects almost all architectures, so I'm not quite sure whether this is
supported by all GCC versions used by those architectures.
If it is supported by all of them, I agree that `BUILD_BUG_ON()` is a
perfect approach.
On Fri, 18 Sept 2026 at 07:22, Barry Song [off-list ref] wrote:
On Thu, Sep 17, 2026 at 1:29 PM Wen Jiang [off-list ref] wrote:
quoted
From: Wen Jiang <redacted>
vmap installs PTE-level block mappings (PTE_CONT on arm64) by reusing
set_huge_pte_at() and huge_ptep_get_and_clear(), which are HugeTLB
helpers gated by CONFIG_HUGETLB_PAGE. This makes the feature silently
unavailable on CONFIG_HUGETLB_PAGE=n kernels and couples mm/vmalloc.c to
HugeTLB internals it does not otherwise need.
Add pte_set_huge()/pte_clear_huge() to arm64, joining the existing
pmd_set_huge()/pud_set_huge() family in mm/mmu.c. They build on
__set_ptes() and __get_and_clear_full_ptes() directly, so they do not
depend on CONFIG_HUGETLB_PAGE.
There is no caller yet: mm/vmalloc.c is converted later in this series,
once the generic fallbacks are in place.
Signed-off-by: Wen Jiang <redacted>
I tried to apply the patch to mm-new, but it doesn't apply.
% git am raw
Applying: arm64/mm: add pte_set_huge() and pte_clear_huge()
error: patch failed: arch/arm64/mm/mmu.c:1872
error: arch/arm64/mm/mmu.c: patch does not apply
Patch failed at 0001 arm64/mm: add pte_set_huge() and pte_clear_huge()
hint: Use 'git am --show-current-patch=diff' to see the failed patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
% patch -p1 < raw
patching file 'arch/arm64/include/asm/pgtable.h'
patching file 'arch/arm64/mm/mmu.c'
1 out of 1 hunks failed--saving rejects to 'arch/arm64/mm/mmu.c.rej'
[...]
quoted
+void pte_set_huge(pte_t *ptep, unsigned long addr, phys_addr_t phys,+ pgprot_t prot, unsigned long size)+{+ unsigned long pfn = __phys_to_pfn(phys);+ pte_t new_pte = pte_mkcont(pfn_pte(pfn, prot));
Maybe we can drop the `new_` prefix.
Will do.
quoted
+ unsigned int nr = size >> PAGE_SHIFT;++ VM_WARN_ON(!IS_ALIGNED(pfn, CONT_PTES));+ VM_WARN_ON(!IS_ALIGNED(size, CONT_PTE_SIZE));
Do we also need to check that `addr` is aligned?
Yes. arch_vmap_pte_range_map_size() already guarantees all three,
since we are already checking two of them here, we should complete the
set. I'll add it in v9.
Thanks,
Wen
On Fri, 18 Sept 2026 at 07:28, Barry Song [off-list ref] wrote:
On Thu, Sep 17, 2026 at 1:30 PM Wen Jiang [off-list ref] wrote:
quoted
From: Wen Jiang <redacted>
vmap installs PTE-level block mappings (SZ_16K/SZ_512K on powerpc/8xx)
by reusing set_huge_pte_at(), a HugeTLB helper gated by
CONFIG_HUGETLB_PAGE. This makes the feature silently unavailable on
CONFIG_HUGETLB_PAGE=n kernels and couples mm/vmalloc.c to HugeTLB
internals it does not otherwise need.
Add pte_set_huge() to powerpc/8xx, next to the existing
pmd_clear_huge()/pud_clear_huge() in mm/nohash/8xx.c. It builds the huge
PTE and writes it into the backing cells directly, without going through
set_huge_pte_at(), so it does not depend on CONFIG_HUGETLB_PAGE.
No pte_clear_huge() is needed: 8xx does not implement
arch_vmap_pte_range_unmap_size(), so the vmap unmap path never takes the
block-mapping branch.
I assume you mean the block-unmapping (or PTE-clearing) branch.
Please make this clear in the changelog.
Sure. Will fix it.
Thanks,
Wen
quoted
In practice PPC_8xx selects HUGETLBFS unconditionally, so
CONFIG_HUGETLB_PAGE=n does not occur there today; this is a decoupling
cleanup rather than a new configuration.
There is no caller yet: mm/vmalloc.c is converted later in this series,
once the generic fallbacks are in place.
Signed-off-by: Wen Jiang <redacted>
---
BTW, this patch applies cleanly to the mm-new branch.
Best Regards
Barry
@@ -2134,6 +2134,35 @@ static inline int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)}#endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */+/*+*PTE-levelblockmappingsforvmap.+*+*pte_set_huge()onlyhastobeimplementedbyarchitectureswhose+*arch_vmap_pte_range_map_size()canreturnasizeotherthanPAGE_SIZE.+*/+#ifndef __HAVE_ARCH_PTE_SET_HUGE+staticinlinevoidpte_set_huge(pte_t*ptep,unsignedlongaddr,+phys_addr_tphys,pgprot_tprot,+unsignedlongsize)+{+WARN_ON_ONCE(1);
BUILD_BUG_ON() would be better here.
It should be possible because fallback arch_vmap_pte_range_map_size()
will constant-fold PAGE_SIZE so pte_set_huge() will never be called.
Agreed. These fallbacks exist only to keep the build working on
architectures with PTE-level block mappings and should never actually
be reached, so BUILD_BUG_ON() is right. I'll make that change in v9.
I am not quite sure. It won't be called at runtime because
`vmap size`/`unmap size` return `PAGE_SIZE`, so the code won't
reach this branch. But it will still be built.
So, would a `BUILD_BUG_ON()` trigger a build failure here?
It shouldn't, if it does it is a compiled bug or this is because someone
has redefined arch_vmap_pte_range_map_size() and not pte_set_huge()
which we'd better know at build time rather than at runtime.
Thanks, Christophe. I was also thinking about compiler optimization. I
was just a bit worried that we're touching the common MM code, which
affects almost all architectures, so I'm not quite sure whether this is
supported by all GCC versions used by those architectures.
If it is supported by all of them, I agree that `BUILD_BUG_ON()` is a
perfect approach.
Hi Barry and Christophe,
pgtable.h already does this in a few places, the !THP fallback of
pmdp_clear_flush_young() is just BUILD_BUG(), and its caller in mm/rmap.c,
a file that is built unconditionally, is guarded by
IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) rather than #ifdef. So on every
THP=n build, on every architecture, that call has to be eliminated by
the compiler or the build breaks.
I built it with BUILD_BUG() on x86_64, arm64 and powerpc (8xx) to be sure,
and all three are clean.
It will be BUILD_BUG() rather than BUILD_BUG_ON(1): BUILD_BUG_ON() expects a
condition the compiler should know is false, while BUILD_BUG() is documented
as the way to flag code that is expected to be eliminated at build time.
Thanks,
Wen
From: Barry Song <baohua@kernel.org> Date: 2026-09-18 08:44:34
On Fri, Sep 18, 2026 at 2:14 PM Christophe Leroy (CS GROUP)
[off-list ref] wrote:
Le 17/09/2026 à 07:29, Wen Jiang a écrit :
quoted
From: Wen Jiang <redacted>
vmap installs PTE-level block mappings (SZ_16K/SZ_512K on powerpc/8xx)
by reusing set_huge_pte_at(), a HugeTLB helper gated by
CONFIG_HUGETLB_PAGE. This makes the feature silently unavailable on
CONFIG_HUGETLB_PAGE=n kernels and couples mm/vmalloc.c to HugeTLB
internals it does not otherwise need.
Add pte_set_huge() to powerpc/8xx, next to the existing
pmd_clear_huge()/pud_clear_huge() in mm/nohash/8xx.c. It builds the huge
PTE and writes it into the backing cells directly, without going through
set_huge_pte_at(), so it does not depend on CONFIG_HUGETLB_PAGE.
No pte_clear_huge() is needed: 8xx does not implement
arch_vmap_pte_range_unmap_size(), so the vmap unmap path never takes the
block-mapping branch.
In practice PPC_8xx selects HUGETLBFS unconditionally, so
CONFIG_HUGETLB_PAGE=n does not occur there today; this is a decoupling
cleanup rather than a new configuration.
8xx selects CONFIG_HUGETLB_PAGE because it uses set_huge_pte_at() to map
kernel text with large pages. That was added by commit 34536d780683
("powerpc/8xx: Add a function to early map kernel via huge pages")
But now pte_set_huge() could be used instead in
__early_map_kernel_hugepage() and then CONFIG_HUGETLB_PAGE could be removed.
Hi Christophe,
Do you mean the code below? If so, it sounds like a good place to
clean this up for powerpc as a separate cleanup patch.
On Fri, Sep 18, 2026 at 2:14 PM Christophe Leroy (CS GROUP)
[off-list ref] wrote:
quoted
Le 17/09/2026 à 07:29, Wen Jiang a écrit :
quoted
From: Wen Jiang <redacted>
vmap installs PTE-level block mappings (SZ_16K/SZ_512K on powerpc/8xx)
by reusing set_huge_pte_at(), a HugeTLB helper gated by
CONFIG_HUGETLB_PAGE. This makes the feature silently unavailable on
CONFIG_HUGETLB_PAGE=n kernels and couples mm/vmalloc.c to HugeTLB
internals it does not otherwise need.
Add pte_set_huge() to powerpc/8xx, next to the existing
pmd_clear_huge()/pud_clear_huge() in mm/nohash/8xx.c. It builds the huge
PTE and writes it into the backing cells directly, without going through
set_huge_pte_at(), so it does not depend on CONFIG_HUGETLB_PAGE.
No pte_clear_huge() is needed: 8xx does not implement
arch_vmap_pte_range_unmap_size(), so the vmap unmap path never takes the
block-mapping branch.
In practice PPC_8xx selects HUGETLBFS unconditionally, so
CONFIG_HUGETLB_PAGE=n does not occur there today; this is a decoupling
cleanup rather than a new configuration.
8xx selects CONFIG_HUGETLB_PAGE because it uses set_huge_pte_at() to map
kernel text with large pages. That was added by commit 34536d780683
("powerpc/8xx: Add a function to early map kernel via huge pages")
But now pte_set_huge() could be used instead in
__early_map_kernel_hugepage() and then CONFIG_HUGETLB_PAGE could be removed.
Hi Christophe,
Do you mean the code below? If so, it sounds like a good place to
clean this up for powerpc as a separate cleanup patch.
Yes this is what I have in mind allthough I have not studied it in details.