Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory
@ 2026-07-09  7:38 Wen Jiang
  2026-07-09  7:38 ` [PATCH v6 1/6] arm64/hugetlb: Extend batching of multiple CONT_PTE in a single PTE setup Wen Jiang
                   ` (6 more replies)
  0 siblings, 7 replies; 27+ messages in thread
From: Wen Jiang @ 2026-07-09  7:38 UTC (permalink / raw)
  To: akpm, catalin.marinas, linux-mm, urezki, will
  Cc: Xueyuan.chen21, ajd, anshuman.khandual, david, linux-arm-kernel,
	linux-kernel, rppt, ryan.roberts, dev.jain, Wen Jiang

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-2 extend ARM64 vmalloc CONT-PTE mapping to support multiple
CONT-PTE regions instead of just one.

Patch 3 extracts a common helper vmap_set_ptes() that consolidates PTE
mapping logic between the ioremap and vmalloc/vmap paths, handling both
CONT_PTE and regular PTE mappings. This prepares for the next patch.

Patch 4 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().

Patches 5-6 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.53us)
* 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 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) (5):
  arm64/hugetlb: Extend batching of multiple CONT_PTE in a single PTE
    setup
  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 (1):
  mm/vmalloc: Extract vmap_set_ptes() to consolidate PTE mapping logic

 arch/arm64/include/asm/vmalloc.h |   6 +-
 arch/arm64/mm/hugetlbpage.c      |  10 ++
 mm/vmalloc.c                     | 243 ++++++++++++++++++++++++-------
 3 files changed, 209 insertions(+), 50 deletions(-)

-- 
2.34.1



^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v6 1/6] arm64/hugetlb: Extend batching of multiple CONT_PTE in a single PTE setup
  2026-07-09  7:38 [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory Wen Jiang
@ 2026-07-09  7:38 ` Wen Jiang
  2026-07-14  6:54   ` Anshuman Khandual
  2026-07-09  7:38 ` [PATCH v6 2/6] arm64/vmalloc: Allow arch_vmap_pte_range_map_size to batch multiple CONT_PTE Wen Jiang
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 27+ messages in thread
From: Wen Jiang @ 2026-07-09  7:38 UTC (permalink / raw)
  To: akpm, catalin.marinas, linux-mm, urezki, will
  Cc: Xueyuan.chen21, ajd, anshuman.khandual, david, linux-arm-kernel,
	linux-kernel, rppt, ryan.roberts, dev.jain, Barry Song (Xiaomi),
	Wen Jiang, Xueyuan Chen, Leo Yan

From: "Barry Song (Xiaomi)" <baohua@kernel.org>

For sizes aligned to CONT_PTE_SIZE and smaller than PMD_SIZE,
we can handle CONT_PTE_SIZE groups together.

Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
Signed-off-by: Wen Jiang <jiangwen6@xiaomi.com>
Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
Tested-by: Leo Yan <leo.yan@arm.com>
Reviewed-by: Dev Jain <dev.jain@arm.com>
---
 arch/arm64/mm/hugetlbpage.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c
index a42c05cf56408..0da746f729388 100644
--- a/arch/arm64/mm/hugetlbpage.c
+++ b/arch/arm64/mm/hugetlbpage.c
@@ -110,6 +110,12 @@ static inline int num_contig_ptes(unsigned long size, size_t *pgsize)
 		contig_ptes = CONT_PTES;
 		break;
 	default:
+		if (size > 0 && size < PMD_SIZE &&
+				IS_ALIGNED(size, CONT_PTE_SIZE)) {
+			*pgsize = PAGE_SIZE;
+			contig_ptes = size >> PAGE_SHIFT;
+			break;
+		}
 		WARN_ON(!__hugetlb_valid_size(size));
 	}
 
@@ -359,6 +365,10 @@ pte_t arch_make_huge_pte(pte_t entry, unsigned int shift, vm_flags_t flags)
 	case CONT_PTE_SIZE:
 		return pte_mkcont(entry);
 	default:
+		if (pagesize > 0 && pagesize < PMD_SIZE &&
+				IS_ALIGNED(pagesize, CONT_PTE_SIZE))
+			return pte_mkcont(entry);
+
 		break;
 	}
 	pr_warn("%s: unrecognized huge page size 0x%lx\n",
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v6 2/6] arm64/vmalloc: Allow arch_vmap_pte_range_map_size to batch multiple CONT_PTE
  2026-07-09  7:38 [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory Wen Jiang
  2026-07-09  7:38 ` [PATCH v6 1/6] arm64/hugetlb: Extend batching of multiple CONT_PTE in a single PTE setup Wen Jiang
@ 2026-07-09  7:38 ` Wen Jiang
  2026-07-14  7:13   ` Anshuman Khandual
  2026-07-09  7:38 ` [PATCH v6 3/6] mm/vmalloc: Extract vmap_set_ptes() to consolidate PTE mapping logic Wen Jiang
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 27+ messages in thread
From: Wen Jiang @ 2026-07-09  7:38 UTC (permalink / raw)
  To: akpm, catalin.marinas, linux-mm, urezki, will
  Cc: Xueyuan.chen21, ajd, anshuman.khandual, david, linux-arm-kernel,
	linux-kernel, rppt, ryan.roberts, dev.jain, Barry Song (Xiaomi),
	Wen Jiang, Xueyuan Chen, Leo Yan

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.

Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
Signed-off-by: Wen Jiang <jiangwen6@xiaomi.com>
Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
Tested-by: Leo Yan <leo.yan@arm.com>
Reviewed-by: Dev Jain <dev.jain@arm.com>
---
 arch/arm64/include/asm/vmalloc.h | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/include/asm/vmalloc.h b/arch/arm64/include/asm/vmalloc.h
index 4ec1acd3c1b34..7d9c7dc795c42 100644
--- a/arch/arm64/include/asm/vmalloc.h
+++ b/arch/arm64/include/asm/vmalloc.h
@@ -23,6 +23,8 @@ static inline unsigned long arch_vmap_pte_range_map_size(unsigned long addr,
 						unsigned long end, u64 pfn,
 						unsigned int max_page_shift)
 {
+	unsigned long size;
+
 	/*
 	 * If the block is at least CONT_PTE_SIZE in size, and is naturally
 	 * aligned in both virtual and physical space, then we can pte-map the
@@ -40,7 +42,9 @@ static inline unsigned long arch_vmap_pte_range_map_size(unsigned long addr,
 	if (!IS_ALIGNED(PFN_PHYS(pfn), CONT_PTE_SIZE))
 		return PAGE_SIZE;
 
-	return CONT_PTE_SIZE;
+	size = min3(end - addr, 1UL << max_page_shift, PMD_SIZE >> 1);
+	size = rounddown_pow_of_two(size);
+	return size;
 }
 
 #define arch_vmap_pte_range_unmap_size arch_vmap_pte_range_unmap_size
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v6 3/6] mm/vmalloc: Extract vmap_set_ptes() to consolidate PTE mapping logic
  2026-07-09  7:38 [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory Wen Jiang
  2026-07-09  7:38 ` [PATCH v6 1/6] arm64/hugetlb: Extend batching of multiple CONT_PTE in a single PTE setup Wen Jiang
  2026-07-09  7:38 ` [PATCH v6 2/6] arm64/vmalloc: Allow arch_vmap_pte_range_map_size to batch multiple CONT_PTE Wen Jiang
@ 2026-07-09  7:38 ` Wen Jiang
  2026-07-09  7:38 ` [PATCH v6 4/6] mm/vmalloc: Extend page table walk to support larger page_shift sizes and eliminate page table rewalk Wen Jiang
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 27+ messages in thread
From: Wen Jiang @ 2026-07-09  7:38 UTC (permalink / raw)
  To: akpm, catalin.marinas, linux-mm, urezki, will
  Cc: Xueyuan.chen21, ajd, anshuman.khandual, david, linux-arm-kernel,
	linux-kernel, rppt, ryan.roberts, dev.jain, Wen Jiang,
	Xueyuan Chen, Leo Yan

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.

The #ifdef CONFIG_HUGETLB_PAGE guard is moved inside vmap_set_ptes(),
so callers no longer need to handle the conditional compilation.

No functional change.

Signed-off-by: Wen Jiang <jiangwen6@xiaomi.com>
Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
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 | 42 +++++++++++++++++++++++++++++-------------
 1 file changed, 29 insertions(+), 13 deletions(-)

diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 2c2f74a07f396..d7fef85b241c4 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -91,6 +91,33 @@ struct vfree_deferred {
 static DEFINE_PER_CPU(struct vfree_deferred, vfree_deferred);
 
 /*** Page table manipulation functions ***/
+
+/*
+ * Try contiguous mappings at the PTE level for arches which support them, and if
+ * requested by the caller. Fall back to PAGE_SIZE mappings otherwise.
+ *
+ * Return: mapping size.
+ */
+static __always_inline unsigned long vmap_set_ptes(pte_t *pte,
+		unsigned long addr, unsigned long end, u64 pfn,
+		pgprot_t prot, unsigned int max_page_shift)
+{
+#ifdef CONFIG_HUGETLB_PAGE
+	unsigned long size;
+
+	size = arch_vmap_pte_range_map_size(addr, end, pfn, max_page_shift);
+	if (size != PAGE_SIZE) {
+		pte_t entry = pfn_pte(pfn, prot);
+
+		entry = arch_make_huge_pte(entry, ilog2(size), 0);
+		set_huge_pte_at(&init_mm, addr, pte, entry, size);
+		return size;
+	}
+#endif
+	set_pte_at(&init_mm, addr, pte, pfn_pte(pfn, prot));
+	return PAGE_SIZE;
+}
+
 static int vmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
 			phys_addr_t phys_addr, pgprot_t prot,
 			unsigned int max_page_shift, pgtbl_mod_mask *mask)
@@ -119,19 +146,8 @@ static int vmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
 			BUG();
 		}
 
-#ifdef CONFIG_HUGETLB_PAGE
-		size = arch_vmap_pte_range_map_size(addr, end, pfn, max_page_shift);
-		if (size != PAGE_SIZE) {
-			pte_t entry = pfn_pte(pfn, prot);
-
-			entry = arch_make_huge_pte(entry, ilog2(size), 0);
-			set_huge_pte_at(&init_mm, addr, pte, entry, size);
-			pfn += PFN_DOWN(size);
-			continue;
-		}
-#endif
-		set_pte_at(&init_mm, addr, pte, pfn_pte(pfn, prot));
-		pfn++;
+		size = vmap_set_ptes(pte, addr, end, pfn, prot, max_page_shift);
+		pfn += PFN_DOWN(size);
 	} while (pte += PFN_DOWN(size), addr += size, addr != end);
 
 	lazy_mmu_mode_disable();
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v6 4/6] mm/vmalloc: Extend page table walk to support larger page_shift sizes and eliminate page table rewalk
  2026-07-09  7:38 [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory Wen Jiang
                   ` (2 preceding siblings ...)
  2026-07-09  7:38 ` [PATCH v6 3/6] mm/vmalloc: Extract vmap_set_ptes() to consolidate PTE mapping logic Wen Jiang
@ 2026-07-09  7:38 ` Wen Jiang
  2026-07-13 13:49   ` Dev Jain
  2026-07-09  7:38 ` [PATCH v6 5/6] mm/vmalloc: map contiguous pages in batches for vmap() if possible Wen Jiang
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 27+ messages in thread
From: Wen Jiang @ 2026-07-09  7:38 UTC (permalink / raw)
  To: akpm, catalin.marinas, linux-mm, urezki, will
  Cc: Xueyuan.chen21, ajd, anshuman.khandual, david, linux-arm-kernel,
	linux-kernel, rppt, ryan.roberts, dev.jain, Barry Song (Xiaomi),
	Wen Jiang, Xueyuan Chen, Leo Yan

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 <jiangwen6@xiaomi.com>
Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
Tested-by: Leo Yan <leo.yan@arm.com>
Reviewed-by: Dev Jain <dev.jain@arm.com>
---
 mm/vmalloc.c | 81 ++++++++++++++++++++++++++++++----------------------
 1 file changed, 47 insertions(+), 34 deletions(-)

diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index d7fef85b241c4..d2a4d649af549 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -125,7 +125,8 @@ static int vmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
 	pte_t *pte;
 	u64 pfn;
 	struct page *page;
-	unsigned long size = PAGE_SIZE;
+	unsigned long size;
+	unsigned int steps;
 
 	if (WARN_ON_ONCE(!PAGE_ALIGNED(end - addr)))
 		return -EINVAL;
@@ -147,8 +148,8 @@ static int vmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
 		}
 
 		size = vmap_set_ptes(pte, addr, end, pfn, prot, max_page_shift);
-		pfn += PFN_DOWN(size);
-	} while (pte += PFN_DOWN(size), addr += size, addr != end);
+		steps = PFN_DOWN(size);
+	} while (pte += steps, pfn += steps, addr += size, addr != end);
 
 	lazy_mmu_mode_disable();
 	*mask |= PGTBL_PTE_MODIFIED;
@@ -540,8 +541,10 @@ void vunmap_range(unsigned long addr, unsigned long end)
 
 static int vmap_pages_pte_range(pmd_t *pmd, unsigned long addr,
 		unsigned long end, pgprot_t prot, struct page **pages, int *nr,
-		pgtbl_mod_mask *mask)
+		pgtbl_mod_mask *mask, unsigned int shift)
 {
+	unsigned long pfn, size;
+	unsigned int steps;
 	int err = 0;
 	pte_t *pte;
 
@@ -572,9 +575,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;
@@ -584,7 +588,7 @@ static int vmap_pages_pte_range(pmd_t *pmd, unsigned long addr,
 
 static int vmap_pages_pmd_range(pud_t *pud, unsigned long addr,
 		unsigned long end, pgprot_t prot, struct page **pages, int *nr,
-		pgtbl_mod_mask *mask)
+		pgtbl_mod_mask *mask, unsigned int shift)
 {
 	pmd_t *pmd;
 	unsigned long next;
@@ -594,7 +598,27 @@ static int vmap_pages_pmd_range(pud_t *pud, unsigned long addr,
 		return -ENOMEM;
 	do {
 		next = pmd_addr_end(addr, end);
-		if (vmap_pages_pte_range(pmd, addr, next, prot, pages, nr, mask))
+
+		if (shift >= PMD_SHIFT) {
+			struct page *page = pages[*nr];
+			phys_addr_t phys_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;
+			}
+		}
+
+		if (vmap_pages_pte_range(pmd, addr, next, prot, pages, nr, mask, shift))
 			return -ENOMEM;
 	} while (pmd++, addr = next, addr != end);
 	return 0;
@@ -602,7 +626,7 @@ static int vmap_pages_pmd_range(pud_t *pud, unsigned long addr,
 
 static int vmap_pages_pud_range(p4d_t *p4d, unsigned long addr,
 		unsigned long end, pgprot_t prot, struct page **pages, int *nr,
-		pgtbl_mod_mask *mask)
+		pgtbl_mod_mask *mask, unsigned int shift)
 {
 	pud_t *pud;
 	unsigned long next;
@@ -612,7 +636,7 @@ static int vmap_pages_pud_range(p4d_t *p4d, unsigned long addr,
 		return -ENOMEM;
 	do {
 		next = pud_addr_end(addr, end);
-		if (vmap_pages_pmd_range(pud, addr, next, prot, pages, nr, mask))
+		if (vmap_pages_pmd_range(pud, addr, next, prot, pages, nr, mask, shift))
 			return -ENOMEM;
 	} while (pud++, addr = next, addr != end);
 	return 0;
@@ -620,7 +644,7 @@ static int vmap_pages_pud_range(p4d_t *p4d, unsigned long addr,
 
 static int vmap_pages_p4d_range(pgd_t *pgd, unsigned long addr,
 		unsigned long end, pgprot_t prot, struct page **pages, int *nr,
-		pgtbl_mod_mask *mask)
+		pgtbl_mod_mask *mask, unsigned int shift)
 {
 	p4d_t *p4d;
 	unsigned long next;
@@ -630,14 +654,18 @@ static int vmap_pages_p4d_range(pgd_t *pgd, unsigned long addr,
 		return -ENOMEM;
 	do {
 		next = p4d_addr_end(addr, end);
-		if (vmap_pages_pud_range(p4d, addr, next, prot, pages, nr, mask))
+		if (vmap_pages_pud_range(p4d, addr, next, prot, pages, nr, mask, shift))
 			return -ENOMEM;
 	} while (p4d++, addr = next, addr != end);
 	return 0;
 }
 
-static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,
-		pgprot_t prot, struct page **pages)
+/*
+ * It can take an array of pages which are not all contiguous, but it
+ * may have contiguous chunks, as hinted by @shift.
+ */
+static int vmap_pages_range_noflush_walk(unsigned long addr, unsigned long end,
+		pgprot_t prot, struct page **pages, unsigned int shift)
 {
 	unsigned long start = addr;
 	pgd_t *pgd;
@@ -652,7 +680,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);
@@ -675,27 +703,12 @@ static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,
 int __vmap_pages_range_noflush(unsigned long addr, unsigned long end,
 		pgprot_t prot, struct page **pages, unsigned int page_shift)
 {
-	unsigned int i, nr = (end - addr) >> PAGE_SHIFT;
-
 	WARN_ON(page_shift < PAGE_SHIFT);
 
-	if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMALLOC) ||
-			page_shift == PAGE_SHIFT)
-		return vmap_small_pages_range_noflush(addr, end, prot, pages);
-
-	for (i = 0; i < nr; i += 1U << (page_shift - PAGE_SHIFT)) {
-		int err;
+	if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMALLOC))
+		page_shift = PAGE_SHIFT;
 
-		err = vmap_range_noflush(addr, addr + (1UL << page_shift),
-					page_to_phys(pages[i]), prot,
-					page_shift);
-		if (err)
-			return err;
-
-		addr += 1UL << page_shift;
-	}
-
-	return 0;
+	return vmap_pages_range_noflush_walk(addr, end, prot, pages, page_shift);
 }
 
 int vmap_pages_range_noflush(unsigned long addr, unsigned long end,
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v6 5/6] mm/vmalloc: map contiguous pages in batches for vmap() if possible
  2026-07-09  7:38 [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory Wen Jiang
                   ` (3 preceding siblings ...)
  2026-07-09  7:38 ` [PATCH v6 4/6] mm/vmalloc: Extend page table walk to support larger page_shift sizes and eliminate page table rewalk Wen Jiang
@ 2026-07-09  7:38 ` Wen Jiang
  2026-07-13 15:19   ` Dev Jain
  2026-07-14  4:59   ` Dev Jain
  2026-07-09  7:38 ` [PATCH v6 6/6] mm/vmalloc: align vm_area so vmap() can batch mappings Wen Jiang
  2026-07-09 23:08 ` [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory Andrew Morton
  6 siblings, 2 replies; 27+ messages in thread
From: Wen Jiang @ 2026-07-09  7:38 UTC (permalink / raw)
  To: akpm, catalin.marinas, linux-mm, urezki, will
  Cc: Xueyuan.chen21, ajd, anshuman.khandual, david, linux-arm-kernel,
	linux-kernel, rppt, ryan.roberts, dev.jain, Barry Song (Xiaomi),
	Wen Jiang, Xueyuan Chen, Leo Yan

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 <jiangwen6@xiaomi.com>
Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
Tested-by: Leo Yan <leo.yan@arm.com>
---
 mm/vmalloc.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 85 insertions(+), 2 deletions(-)

diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index d2a4d649af549..db0492151ad08 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -3543,6 +3543,89 @@ void vunmap(const void *addr)
 }
 EXPORT_SYMBOL(vunmap);
 
+static inline unsigned int vm_shift(pgprot_t prot, unsigned long size)
+{
+	if (arch_vmap_pmd_supported(prot) && size >= PMD_SIZE)
+		return PMD_SHIFT;
+
+	return arch_vmap_pte_supported_shift(size);
+}
+
+static inline int get_vmap_batch_order(struct page **pages,
+		pgprot_t prot, unsigned int max_steps, unsigned int idx)
+{
+	unsigned int nr_contig;
+	int order;
+
+	if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMAP))
+		return 0;
+
+	nr_contig = num_pages_contiguous(&pages[idx], max_steps);
+	if (nr_contig < 2)
+		return 0;
+
+	order = ilog2(nr_contig);
+
+	/* Limit order by pfn alignment */
+	order = min_t(int, order, __ffs(page_to_pfn(pages[idx])));
+
+	if (vm_shift(prot, PAGE_SIZE << order) == PAGE_SHIFT)
+		return 0;
+
+	return order;
+}
+
+static int vmap_pages_range_batched(unsigned long addr, unsigned long end,
+		pgprot_t prot, struct page **pages)
+{
+	unsigned int count = (end - addr) >> PAGE_SHIFT;
+	unsigned int prev_shift = 0, idx = 0;
+	unsigned long map_addr = addr, batch_end = addr;
+	int err;
+
+	err = kmsan_vmap_pages_range_noflush(addr, end, prot, pages,
+						PAGE_SHIFT, GFP_KERNEL);
+	if (err)
+		goto out;
+
+	for (unsigned int i = 0; i < count; ) {
+		unsigned int shift = PAGE_SHIFT +
+			get_vmap_batch_order(pages, prot, count - i, i);
+
+		if (!i)
+			prev_shift = shift;
+
+		if (shift != prev_shift) {
+			err = vmap_pages_range_noflush_walk(map_addr, batch_end,
+					prot, pages + idx, prev_shift);
+			if (err)
+				goto out;
+			prev_shift = shift;
+			map_addr = batch_end;
+			idx = i;
+		}
+
+		/*
+		 * Once small pages are encountered, the remaining pages
+		 * are likely small as well.
+		 */
+		if (shift == PAGE_SHIFT)
+			break;
+
+		batch_end += 1UL << shift;
+		i += 1U << (shift - PAGE_SHIFT);
+	}
+
+	/* Remaining */
+	if (map_addr < end)
+		err = vmap_pages_range_noflush_walk(map_addr, end,
+				prot, pages + idx, prev_shift);
+
+out:
+	flush_cache_vmap(addr, end);
+	return err;
+}
+
 /**
  * vmap - map an array of pages into virtually contiguous space
  * @pages: array of page pointers
@@ -3586,8 +3669,8 @@ void *vmap(struct page **pages, unsigned int count,
 		return NULL;
 
 	addr = (unsigned long)area->addr;
-	if (vmap_pages_range(addr, addr + size, pgprot_nx(prot),
-				pages, PAGE_SHIFT) < 0) {
+	if (vmap_pages_range_batched(addr, addr + size, pgprot_nx(prot),
+				pages) < 0) {
 		vunmap(area->addr);
 		return NULL;
 	}
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v6 6/6] mm/vmalloc: align vm_area so vmap() can batch mappings
  2026-07-09  7:38 [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory Wen Jiang
                   ` (4 preceding siblings ...)
  2026-07-09  7:38 ` [PATCH v6 5/6] mm/vmalloc: map contiguous pages in batches for vmap() if possible Wen Jiang
@ 2026-07-09  7:38 ` Wen Jiang
  2026-07-14  5:05   ` Dev Jain
  2026-07-09 23:08 ` [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory Andrew Morton
  6 siblings, 1 reply; 27+ messages in thread
From: Wen Jiang @ 2026-07-09  7:38 UTC (permalink / raw)
  To: akpm, catalin.marinas, linux-mm, urezki, will
  Cc: Xueyuan.chen21, ajd, anshuman.khandual, david, linux-arm-kernel,
	linux-kernel, rppt, ryan.roberts, dev.jain, Barry Song (Xiaomi),
	Wen Jiang, Xueyuan Chen, Leo Yan

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 <jiangwen6@xiaomi.com>
Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
Tested-by: Leo Yan <leo.yan@arm.com>
Reviewed-by: Uladzislau Rezki (Sony) <urezki@gmail.com>
---
 mm/vmalloc.c | 35 ++++++++++++++++++++++++++++++++++-
 1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index db0492151ad08..2ac805cdf18ad 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -3314,6 +3314,14 @@ struct vm_struct *get_vm_area_caller(unsigned long size, unsigned long flags,
 				  NUMA_NO_NODE, GFP_KERNEL, caller);
 }
 
+static struct vm_struct *__get_vm_area_node_aligned_caller(unsigned long size,
+		unsigned long align, unsigned long flags, const void *caller)
+{
+	return __get_vm_area_node(size, align, PAGE_SHIFT, flags,
+				  VMALLOC_START, VMALLOC_END,
+				  NUMA_NO_NODE, GFP_KERNEL, caller);
+}
+
 /**
  * find_vm_area - find a continuous kernel virtual area
  * @addr:	  base address
@@ -3626,6 +3634,30 @@ static int vmap_pages_range_batched(unsigned long addr, unsigned long end,
 	return err;
 }
 
+static struct vm_struct *vmap_get_aligned_vm_area(unsigned long size,
+		unsigned long flags, pgprot_t prot, const void *caller)
+{
+	struct vm_struct *vm_area;
+	unsigned int shift;
+
+	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)
+			return vm_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)
+			return vm_area;
+	}
+
+	return __get_vm_area_node_aligned_caller(size, PAGE_SIZE, flags, caller);
+}
+
 /**
  * vmap - map an array of pages into virtually contiguous space
  * @pages: array of page pointers
@@ -3664,7 +3696,8 @@ void *vmap(struct page **pages, unsigned int count,
 		return NULL;
 
 	size = (unsigned long)count << PAGE_SHIFT;
-	area = get_vm_area_caller(size, flags, __builtin_return_address(0));
+	area = vmap_get_aligned_vm_area(size, flags, prot,
+				__builtin_return_address(0));
 	if (!area)
 		return NULL;
 
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory
  2026-07-09  7:38 [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory Wen Jiang
                   ` (5 preceding siblings ...)
  2026-07-09  7:38 ` [PATCH v6 6/6] mm/vmalloc: align vm_area so vmap() can batch mappings Wen Jiang
@ 2026-07-09 23:08 ` Andrew Morton
  2026-07-10  8:54   ` Wen Jiang
  6 siblings, 1 reply; 27+ messages in thread
From: Andrew Morton @ 2026-07-09 23:08 UTC (permalink / raw)
  To: Wen Jiang
  Cc: catalin.marinas, linux-mm, urezki, will, Xueyuan.chen21, ajd,
	anshuman.khandual, david, linux-arm-kernel, linux-kernel, rppt,
	ryan.roberts, dev.jain, Wen Jiang

On Thu,  9 Jul 2026 15:38:17 +0800 Wen Jiang <jiangwenxiaomi@gmail.com> wrote:

> This patchset accelerates ioremap, vmalloc, and vmap when the memory
> is physically fully or partially contiguous.

Thanks, I added this to mm.git's mm-new branch for wider testing.

AI review asked some questions, and some of them are new since the v5
series:
	https://sashiko.dev/#/patchset/20260709073823.6643-1-jiangwen6@xiaomi.com


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory
  2026-07-09 23:08 ` [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory Andrew Morton
@ 2026-07-10  8:54   ` Wen Jiang
  2026-07-10  8:59     ` Wen Jiang
                       ` (2 more replies)
  0 siblings, 3 replies; 27+ messages in thread
From: Wen Jiang @ 2026-07-10  8:54 UTC (permalink / raw)
  To: Andrew Morton
  Cc: catalin.marinas, linux-mm, urezki, will, Xueyuan.chen21, ajd,
	anshuman.khandual, david, linux-arm-kernel, linux-kernel, rppt,
	ryan.roberts, dev.jain, Wen Jiang

On Fri, 10 Jul 2026 at 07:08, Andrew Morton <akpm@linux-foundation.org> wrote:
>
> On Thu,  9 Jul 2026 15:38:17 +0800 Wen Jiang <jiangwenxiaomi@gmail.com> wrote:
>
> > This patchset accelerates ioremap, vmalloc, and vmap when the memory
> > is physically fully or partially contiguous.
>
> Thanks, I added this to mm.git's mm-new branch for wider testing.
>
> AI review asked some questions, and some of them are new since the v5
> series:
>         https://sashiko.dev/#/patchset/20260709073823.6643-1-jiangwen6@xiaomi.com

Hi Andrew,

I've gone through the Sashiko findings:

- Patch 1 (find_num_contig): Over-interpretation. No new hugetlbfs hstate
  is added. The extra sizes are only used by init_mm kernel mappings via.

- Patch 5/6 (NULL page): Invalid input. vmap() expects a fully populated
    array of valid struct page pointers.

- Patch 6 (32-bit count << PAGE_SHIFT overflow): Pre-existing. This was
    already discussed in the V3 thread, and a separate fix was proposed
    there.

Thanks,
Wen


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory
  2026-07-10  8:54   ` Wen Jiang
@ 2026-07-10  8:59     ` Wen Jiang
  2026-07-13 15:13     ` Dev Jain
  2026-07-14  8:36     ` Anshuman Khandual
  2 siblings, 0 replies; 27+ messages in thread
From: Wen Jiang @ 2026-07-10  8:59 UTC (permalink / raw)
  To: Andrew Morton
  Cc: catalin.marinas, linux-mm, urezki, will, Xueyuan.chen21, ajd,
	anshuman.khandual, david, linux-arm-kernel, linux-kernel, rppt,
	ryan.roberts, dev.jain, Wen Jiang

On Fri, 10 Jul 2026 at 16:54, Wen Jiang <jiangwenxiaomi@gmail.com> wrote:
>
> - Patch 6 (32-bit count << PAGE_SHIFT overflow): Pre-existing. This was
>     already discussed in the V3 thread, and a separate fix was proposed
>     there.
>

Small correction: I meant that I plan to send a separate fix after this
  series is merged.

> Thanks,
> Wen


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 4/6] mm/vmalloc: Extend page table walk to support larger page_shift sizes and eliminate page table rewalk
  2026-07-09  7:38 ` [PATCH v6 4/6] mm/vmalloc: Extend page table walk to support larger page_shift sizes and eliminate page table rewalk Wen Jiang
@ 2026-07-13 13:49   ` Dev Jain
  2026-07-14  6:10     ` Wen Jiang
  0 siblings, 1 reply; 27+ messages in thread
From: Dev Jain @ 2026-07-13 13:49 UTC (permalink / raw)
  To: Wen Jiang, akpm, catalin.marinas, linux-mm, urezki, will
  Cc: Xueyuan.chen21, ajd, anshuman.khandual, david, linux-arm-kernel,
	linux-kernel, rppt, ryan.roberts, Barry Song (Xiaomi), Wen Jiang,
	Leo Yan



On 09/07/26 1:08 pm, Wen Jiang wrote:
> 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 <jiangwen6@xiaomi.com>
> Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
> Tested-by: Leo Yan <leo.yan@arm.com>
> Reviewed-by: Dev Jain <dev.jain@arm.com>
> ---
>  mm/vmalloc.c | 81 ++++++++++++++++++++++++++++++----------------------
>  1 file changed, 47 insertions(+), 34 deletions(-)
> 
> diff --git a/mm/vmalloc.c b/mm/vmalloc.c
> index d7fef85b241c4..d2a4d649af549 100644
> --- a/mm/vmalloc.c
> +++ b/mm/vmalloc.c
> @@ -125,7 +125,8 @@ static int vmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
>  	pte_t *pte;
>  	u64 pfn;
>  	struct page *page;
> -	unsigned long size = PAGE_SIZE;
> +	unsigned long size;
> +	unsigned int steps;
>  
>  	if (WARN_ON_ONCE(!PAGE_ALIGNED(end - addr)))
>  		return -EINVAL;
> @@ -147,8 +148,8 @@ static int vmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
>  		}
>  
>  		size = vmap_set_ptes(pte, addr, end, pfn, prot, max_page_shift);
> -		pfn += PFN_DOWN(size);
> -	} while (pte += PFN_DOWN(size), addr += size, addr != end);
> +		steps = PFN_DOWN(size);
> +	} while (pte += steps, pfn += steps, addr += size, addr != end);

I think I had also recommended to put these vmap_pte_range() changes
into the previous patch.

>  
>  	lazy_mmu_mode_disable();
>  	*mask |= PGTBL_PTE_MODIFIED;
> @@ -540,8 +541,10 @@ void vunmap_range(unsigned long addr, unsigned long end)
>  
>  static int vmap_pages_pte_range(pmd_t *pmd, unsigned long addr,
>  		unsigned long end, pgprot_t prot, struct page **pages, int *nr,
> -		pgtbl_mod_mask *mask)
> +		pgtbl_mod_mask *mask, unsigned int shift)
>  {
> +	unsigned long pfn, size;
> +	unsigned int steps;
>  	int err = 0;
>  	pte_t *pte;
>  
> @@ -572,9 +575,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;
> @@ -584,7 +588,7 @@ static int vmap_pages_pte_range(pmd_t *pmd, unsigned long addr,
>  
>  static int vmap_pages_pmd_range(pud_t *pud, unsigned long addr,
>  		unsigned long end, pgprot_t prot, struct page **pages, int *nr,
> -		pgtbl_mod_mask *mask)
> +		pgtbl_mod_mask *mask, unsigned int shift)
>  {
>  	pmd_t *pmd;
>  	unsigned long next;
> @@ -594,7 +598,27 @@ static int vmap_pages_pmd_range(pud_t *pud, unsigned long addr,
>  		return -ENOMEM;
>  	do {
>  		next = pmd_addr_end(addr, end);
> -		if (vmap_pages_pte_range(pmd, addr, next, prot, pages, nr, mask))
> +
> +		if (shift >= PMD_SHIFT) {
> +			struct page *page = pages[*nr];
> +			phys_addr_t phys_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;
> +			}
> +		}
> +
> +		if (vmap_pages_pte_range(pmd, addr, next, prot, pages, nr, mask, shift))
>  			return -ENOMEM;
>  	} while (pmd++, addr = next, addr != end);
>  	return 0;
> @@ -602,7 +626,7 @@ static int vmap_pages_pmd_range(pud_t *pud, unsigned long addr,
>  
>  static int vmap_pages_pud_range(p4d_t *p4d, unsigned long addr,
>  		unsigned long end, pgprot_t prot, struct page **pages, int *nr,
> -		pgtbl_mod_mask *mask)
> +		pgtbl_mod_mask *mask, unsigned int shift)
>  {
>  	pud_t *pud;
>  	unsigned long next;
> @@ -612,7 +636,7 @@ static int vmap_pages_pud_range(p4d_t *p4d, unsigned long addr,
>  		return -ENOMEM;
>  	do {
>  		next = pud_addr_end(addr, end);
> -		if (vmap_pages_pmd_range(pud, addr, next, prot, pages, nr, mask))
> +		if (vmap_pages_pmd_range(pud, addr, next, prot, pages, nr, mask, shift))
>  			return -ENOMEM;
>  	} while (pud++, addr = next, addr != end);
>  	return 0;
> @@ -620,7 +644,7 @@ static int vmap_pages_pud_range(p4d_t *p4d, unsigned long addr,
>  
>  static int vmap_pages_p4d_range(pgd_t *pgd, unsigned long addr,
>  		unsigned long end, pgprot_t prot, struct page **pages, int *nr,
> -		pgtbl_mod_mask *mask)
> +		pgtbl_mod_mask *mask, unsigned int shift)
>  {
>  	p4d_t *p4d;
>  	unsigned long next;
> @@ -630,14 +654,18 @@ static int vmap_pages_p4d_range(pgd_t *pgd, unsigned long addr,
>  		return -ENOMEM;
>  	do {
>  		next = p4d_addr_end(addr, end);
> -		if (vmap_pages_pud_range(p4d, addr, next, prot, pages, nr, mask))
> +		if (vmap_pages_pud_range(p4d, addr, next, prot, pages, nr, mask, shift))
>  			return -ENOMEM;
>  	} while (p4d++, addr = next, addr != end);
>  	return 0;
>  }
>  
> -static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,
> -		pgprot_t prot, struct page **pages)
> +/*
> + * It can take an array of pages which are not all contiguous, but it
> + * may have contiguous chunks, as hinted by @shift.
> + */
> +static int vmap_pages_range_noflush_walk(unsigned long addr, unsigned long end,
> +		pgprot_t prot, struct page **pages, unsigned int shift)
>  {
>  	unsigned long start = addr;
>  	pgd_t *pgd;
> @@ -652,7 +680,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);
> @@ -675,27 +703,12 @@ static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,
>  int __vmap_pages_range_noflush(unsigned long addr, unsigned long end,
>  		pgprot_t prot, struct page **pages, unsigned int page_shift)
>  {
> -	unsigned int i, nr = (end - addr) >> PAGE_SHIFT;
> -
>  	WARN_ON(page_shift < PAGE_SHIFT);
>  
> -	if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMALLOC) ||
> -			page_shift == PAGE_SHIFT)
> -		return vmap_small_pages_range_noflush(addr, end, prot, pages);
> -
> -	for (i = 0; i < nr; i += 1U << (page_shift - PAGE_SHIFT)) {
> -		int err;
> +	if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMALLOC))
> +		page_shift = PAGE_SHIFT;
>  
> -		err = vmap_range_noflush(addr, addr + (1UL << page_shift),
> -					page_to_phys(pages[i]), prot,
> -					page_shift);
> -		if (err)
> -			return err;
> -
> -		addr += 1UL << page_shift;
> -	}
> -
> -	return 0;
> +	return vmap_pages_range_noflush_walk(addr, end, prot, pages, page_shift);
>  }
>  
>  int vmap_pages_range_noflush(unsigned long addr, unsigned long end,



^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory
  2026-07-10  8:54   ` Wen Jiang
  2026-07-10  8:59     ` Wen Jiang
@ 2026-07-13 15:13     ` Dev Jain
  2026-07-13 17:30       ` Uladzislau Rezki
  2026-07-14  8:36     ` Anshuman Khandual
  2 siblings, 1 reply; 27+ messages in thread
From: Dev Jain @ 2026-07-13 15:13 UTC (permalink / raw)
  To: Wen Jiang, Andrew Morton
  Cc: catalin.marinas, linux-mm, urezki, will, Xueyuan.chen21, ajd,
	anshuman.khandual, david, linux-arm-kernel, linux-kernel, rppt,
	ryan.roberts, Wen Jiang



On 10/07/26 2:24 pm, Wen Jiang wrote:
> On Fri, 10 Jul 2026 at 07:08, Andrew Morton <akpm@linux-foundation.org> wrote:
>>
>> On Thu,  9 Jul 2026 15:38:17 +0800 Wen Jiang <jiangwenxiaomi@gmail.com> wrote:
>>
>>> This patchset accelerates ioremap, vmalloc, and vmap when the memory
>>> is physically fully or partially contiguous.
>>
>> Thanks, I added this to mm.git's mm-new branch for wider testing.
>>
>> AI review asked some questions, and some of them are new since the v5
>> series:
>>         https://sashiko.dev/#/patchset/20260709073823.6643-1-jiangwen6@xiaomi.com
> 
> Hi Andrew,
> 
> I've gone through the Sashiko findings:
> 
> - Patch 1 (find_num_contig): Over-interpretation. No new hugetlbfs hstate
>   is added. The extra sizes are only used by init_mm kernel mappings via.
> 
> - Patch 5/6 (NULL page): Invalid input. vmap() expects a fully populated
>     array of valid struct page pointers.

Correct, but vmap_pages_pte_range has !page and !pfn_valid checks.

I really hate those checks - if those checks have any remote possibility of
firing, then we already have a bug at

vm_map_ram -> vmap_pages_range -> vmap_pages_range_noflush -> kmsan_vmap_pages_range_noflush

because the last function dereferences the struct page pointers.

It is painful to do the page array sanity check deep into vmap - it implies
we simply cannot play with the page array before that.

But since vmap is an exported function, doing a sanity check for the page array
in the vmap code makes sense.

So how about the following:

diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index afaa14ebf17bb..0c44bb7a45b5d 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -566,14 +566,6 @@ static int vmap_pages_pte_range(pmd_t *pmd, unsigned long addr,
 			err = -EBUSY;
 			break;
 		}
-		if (WARN_ON(!page)) {
-			err = -ENOMEM;
-			break;
-		}
-		if (WARN_ON(!pfn_valid(page_to_pfn(page)))) {
-			err = -EINVAL;
-			break;
-		}

 		pfn = page_to_pfn(page);
 		size = vmap_set_ptes(pte, addr, end, pfn, prot, shift);
@@ -603,11 +595,6 @@ static int vmap_pages_pmd_range(pud_t *pud, unsigned long addr,
 			struct page *page = pages[*nr];
 			phys_addr_t phys_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,
@@ -3663,6 +3650,19 @@ static struct vm_struct *vmap_get_aligned_vm_area(unsigned long size,
 	return __get_vm_area_node_aligned_caller(size, PAGE_SIZE, flags, caller);
 }

+static inline bool vmap_page_sanity_checks(struct page **pages, unsigned int count)
+{
+	for (int i = 0; i < count; ++i) {
+		if (WARN_ON(!pages[i]))
+			return true;
+
+		if (WARN_ON(!pfn_valid(page_to_pfn(pages[i]))))
+			return true;
+	}
+
+	return false;
+}
+
 /**
  * vmap - map an array of pages into virtually contiguous space
  * @pages: array of page pointers
@@ -3706,6 +3706,9 @@ void *vmap(struct page **pages, unsigned int count,
 	if (!area)
 		return NULL;

+	if (unlikely(vmap_page_sanity_checks(pages, count)))
+		return NULL;
+
 	addr = (unsigned long)area->addr;
 	if (vmap_pages_range_batched(addr, addr + size, pgprot_nx(prot),
 				pages) < 0) {



Reasoning for calling vmap_page_sanity_checks() before vmap_pages_range_batched,
and not at the start of vmap: I am worried that since vmap() is already very
fast, we may cause a regression:

vmap() -> scan page array with linear map pointers -> vmap_get_aligned_vm_area (does
memory allocation, throwing out the linear map VAs from cache and TLB) -> walk
the pgtables and again access cold page array.

Perhaps I am being very pedantic here. What do you think?

> 
> - Patch 6 (32-bit count << PAGE_SHIFT overflow): Pre-existing. This was
>     already discussed in the V3 thread, and a separate fix was proposed
>     there.
> 
> Thanks,
> Wen
> 



^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 5/6] mm/vmalloc: map contiguous pages in batches for vmap() if possible
  2026-07-09  7:38 ` [PATCH v6 5/6] mm/vmalloc: map contiguous pages in batches for vmap() if possible Wen Jiang
@ 2026-07-13 15:19   ` Dev Jain
  2026-07-14  5:16     ` Wen Jiang
  2026-07-14  4:59   ` Dev Jain
  1 sibling, 1 reply; 27+ messages in thread
From: Dev Jain @ 2026-07-13 15:19 UTC (permalink / raw)
  To: Wen Jiang, akpm, catalin.marinas, linux-mm, urezki, will
  Cc: Xueyuan.chen21, ajd, anshuman.khandual, david, linux-arm-kernel,
	linux-kernel, rppt, ryan.roberts, Barry Song (Xiaomi), Wen Jiang,
	Leo Yan



On 09/07/26 1:08 pm, Wen Jiang wrote:
> 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 <jiangwen6@xiaomi.com>
> Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
> Tested-by: Leo Yan <leo.yan@arm.com>
> ---
>  mm/vmalloc.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 85 insertions(+), 2 deletions(-)
> 
> diff --git a/mm/vmalloc.c b/mm/vmalloc.c
> index d2a4d649af549..db0492151ad08 100644
> --- a/mm/vmalloc.c
> +++ b/mm/vmalloc.c
> @@ -3543,6 +3543,89 @@ void vunmap(const void *addr)
>  }
>  EXPORT_SYMBOL(vunmap);
>  
> +static inline unsigned int vm_shift(pgprot_t prot, unsigned long size)
> +{
> +	if (arch_vmap_pmd_supported(prot) && size >= PMD_SIZE)
> +		return PMD_SHIFT;
> +
> +	return arch_vmap_pte_supported_shift(size);
> +}

Need to throw in a preparatory patch for this, which will (in addition to
introduction of the vm_shift() function) do:

diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index afaa14ebf17bb..dac87e1cd484b 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -4175,10 +4175,7 @@ void *__vmalloc_node_range_noprof(unsigned long size, unsigned long align,
 		 * supporting them.
 		 */

-		if (arch_vmap_pmd_supported(prot) && size >= PMD_SIZE)
-			shift = PMD_SHIFT;
-		else
-			shift = arch_vmap_pte_supported_shift(size);
+		shift = vm_shift(prot, size);

 		align = max(original_align, 1UL << shift);
 	}



> +
> +static inline int get_vmap_batch_order(struct page **pages,
> +		pgprot_t prot, unsigned int max_steps, unsigned int idx)
> +{
> +	unsigned int nr_contig;
> +	int order;
> +
> +	if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMAP))
> +		return 0;
> +
> +	nr_contig = num_pages_contiguous(&pages[idx], max_steps);
> +	if (nr_contig < 2)
> +		return 0;
> +
> +	order = ilog2(nr_contig);
> +
> +	/* Limit order by pfn alignment */
> +	order = min_t(int, order, __ffs(page_to_pfn(pages[idx])));

You missed Sashiko's point here :) the pfn may be zero and this
will blow up. You can just first derive the pfn and do the clamping
only when pfn > 0.



> +
> +	if (vm_shift(prot, PAGE_SIZE << order) == PAGE_SHIFT)
> +		return 0;
> +
> +	return order;
> +}
> +


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory
  2026-07-13 15:13     ` Dev Jain
@ 2026-07-13 17:30       ` Uladzislau Rezki
  2026-07-14  4:34         ` Dev Jain
  0 siblings, 1 reply; 27+ messages in thread
From: Uladzislau Rezki @ 2026-07-13 17:30 UTC (permalink / raw)
  To: Dev Jain
  Cc: Wen Jiang, Andrew Morton, catalin.marinas, linux-mm, urezki, will,
	Xueyuan.chen21, ajd, anshuman.khandual, david, linux-arm-kernel,
	linux-kernel, rppt, ryan.roberts, Wen Jiang

On Mon, Jul 13, 2026 at 08:43:14PM +0530, Dev Jain wrote:
> 
> 
> On 10/07/26 2:24 pm, Wen Jiang wrote:
> > On Fri, 10 Jul 2026 at 07:08, Andrew Morton <akpm@linux-foundation.org> wrote:
> >>
> >> On Thu,  9 Jul 2026 15:38:17 +0800 Wen Jiang <jiangwenxiaomi@gmail.com> wrote:
> >>
> >>> This patchset accelerates ioremap, vmalloc, and vmap when the memory
> >>> is physically fully or partially contiguous.
> >>
> >> Thanks, I added this to mm.git's mm-new branch for wider testing.
> >>
> >> AI review asked some questions, and some of them are new since the v5
> >> series:
> >>         https://sashiko.dev/#/patchset/20260709073823.6643-1-jiangwen6@xiaomi.com
> > 
> > Hi Andrew,
> > 
> > I've gone through the Sashiko findings:
> > 
> > - Patch 1 (find_num_contig): Over-interpretation. No new hugetlbfs hstate
> >   is added. The extra sizes are only used by init_mm kernel mappings via.
> > 
> > - Patch 5/6 (NULL page): Invalid input. vmap() expects a fully populated
> >     array of valid struct page pointers.
> 
> Correct, but vmap_pages_pte_range has !page and !pfn_valid checks.
> 
> I really hate those checks - if those checks have any remote possibility of
> firing, then we already have a bug at
> 
> vm_map_ram -> vmap_pages_range -> vmap_pages_range_noflush -> kmsan_vmap_pages_range_noflush
> 
> because the last function dereferences the struct page pointers.
> 
> It is painful to do the page array sanity check deep into vmap - it implies
> we simply cannot play with the page array before that.
> 
> But since vmap is an exported function, doing a sanity check for the page array
> in the vmap code makes sense.
> 
> So how about the following:
> 
> diff --git a/mm/vmalloc.c b/mm/vmalloc.c
> index afaa14ebf17bb..0c44bb7a45b5d 100644
> --- a/mm/vmalloc.c
> +++ b/mm/vmalloc.c
> @@ -566,14 +566,6 @@ static int vmap_pages_pte_range(pmd_t *pmd, unsigned long addr,
>  			err = -EBUSY;
>  			break;
>  		}
> -		if (WARN_ON(!page)) {
> -			err = -ENOMEM;
> -			break;
> -		}
> -		if (WARN_ON(!pfn_valid(page_to_pfn(page)))) {
> -			err = -EINVAL;
> -			break;
> -		}
> 
>  		pfn = page_to_pfn(page);
>  		size = vmap_set_ptes(pte, addr, end, pfn, prot, shift);
> @@ -603,11 +595,6 @@ static int vmap_pages_pmd_range(pud_t *pud, unsigned long addr,
>  			struct page *page = pages[*nr];
>  			phys_addr_t phys_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,
> @@ -3663,6 +3650,19 @@ static struct vm_struct *vmap_get_aligned_vm_area(unsigned long size,
>  	return __get_vm_area_node_aligned_caller(size, PAGE_SIZE, flags, caller);
>  }
> 
> +static inline bool vmap_page_sanity_checks(struct page **pages, unsigned int count)
> +{
> +	for (int i = 0; i < count; ++i) {
> +		if (WARN_ON(!pages[i]))
> +			return true;
> +
> +		if (WARN_ON(!pfn_valid(page_to_pfn(pages[i]))))
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
>  /**
>   * vmap - map an array of pages into virtually contiguous space
>   * @pages: array of page pointers
> @@ -3706,6 +3706,9 @@ void *vmap(struct page **pages, unsigned int count,
>  	if (!area)
>  		return NULL;
> 
> +	if (unlikely(vmap_page_sanity_checks(pages, count)))
> +		return NULL;
> +
>  	addr = (unsigned long)area->addr;
>  	if (vmap_pages_range_batched(addr, addr + size, pgprot_nx(prot),
>  				pages) < 0) {
> 
> 
> 
> Reasoning for calling vmap_page_sanity_checks() before vmap_pages_range_batched,
> and not at the start of vmap: I am worried that since vmap() is already very
> fast, we may cause a regression:
> 
> vmap() -> scan page array with linear map pointers -> vmap_get_aligned_vm_area (does
> memory allocation, throwing out the linear map VAs from cache and TLB) -> walk
> the pgtables and again access cold page array.
> 
> Perhaps I am being very pedantic here. What do you think?
> 
Sanity check adds extra CPU cycles and it adds overhead. The concern about cache
to be cold on second iteration looks valid. You can get some perf figures to see
the cost.

I would just keep the original approach. But no strong opinion here.

--
Uladzislau Rezki


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory
  2026-07-13 17:30       ` Uladzislau Rezki
@ 2026-07-14  4:34         ` Dev Jain
  0 siblings, 0 replies; 27+ messages in thread
From: Dev Jain @ 2026-07-14  4:34 UTC (permalink / raw)
  To: Uladzislau Rezki
  Cc: Wen Jiang, Andrew Morton, catalin.marinas, linux-mm, will,
	Xueyuan.chen21, ajd, anshuman.khandual, david, linux-arm-kernel,
	linux-kernel, rppt, ryan.roberts, Wen Jiang



On 13/07/26 11:00 pm, Uladzislau Rezki wrote:
> On Mon, Jul 13, 2026 at 08:43:14PM +0530, Dev Jain wrote:
>>
>>
>> On 10/07/26 2:24 pm, Wen Jiang wrote:
>>> On Fri, 10 Jul 2026 at 07:08, Andrew Morton <akpm@linux-foundation.org> wrote:
>>>>
>>>> On Thu,  9 Jul 2026 15:38:17 +0800 Wen Jiang <jiangwenxiaomi@gmail.com> wrote:
>>>>
>>>>> This patchset accelerates ioremap, vmalloc, and vmap when the memory
>>>>> is physically fully or partially contiguous.
>>>>
>>>> Thanks, I added this to mm.git's mm-new branch for wider testing.
>>>>
>>>> AI review asked some questions, and some of them are new since the v5
>>>> series:
>>>>         https://sashiko.dev/#/patchset/20260709073823.6643-1-jiangwen6@xiaomi.com
>>>
>>> Hi Andrew,
>>>
>>> I've gone through the Sashiko findings:
>>>
>>> - Patch 1 (find_num_contig): Over-interpretation. No new hugetlbfs hstate
>>>   is added. The extra sizes are only used by init_mm kernel mappings via.
>>>
>>> - Patch 5/6 (NULL page): Invalid input. vmap() expects a fully populated
>>>     array of valid struct page pointers.
>>
>> Correct, but vmap_pages_pte_range has !page and !pfn_valid checks.
>>
>> I really hate those checks - if those checks have any remote possibility of
>> firing, then we already have a bug at
>>
>> vm_map_ram -> vmap_pages_range -> vmap_pages_range_noflush -> kmsan_vmap_pages_range_noflush
>>
>> because the last function dereferences the struct page pointers.
>>
>> It is painful to do the page array sanity check deep into vmap - it implies
>> we simply cannot play with the page array before that.
>>
>> But since vmap is an exported function, doing a sanity check for the page array
>> in the vmap code makes sense.
>>
>> So how about the following:
>>
>> diff --git a/mm/vmalloc.c b/mm/vmalloc.c
>> index afaa14ebf17bb..0c44bb7a45b5d 100644
>> --- a/mm/vmalloc.c
>> +++ b/mm/vmalloc.c
>> @@ -566,14 +566,6 @@ static int vmap_pages_pte_range(pmd_t *pmd, unsigned long addr,
>>  			err = -EBUSY;
>>  			break;
>>  		}
>> -		if (WARN_ON(!page)) {
>> -			err = -ENOMEM;
>> -			break;
>> -		}
>> -		if (WARN_ON(!pfn_valid(page_to_pfn(page)))) {
>> -			err = -EINVAL;
>> -			break;
>> -		}
>>
>>  		pfn = page_to_pfn(page);
>>  		size = vmap_set_ptes(pte, addr, end, pfn, prot, shift);
>> @@ -603,11 +595,6 @@ static int vmap_pages_pmd_range(pud_t *pud, unsigned long addr,
>>  			struct page *page = pages[*nr];
>>  			phys_addr_t phys_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,
>> @@ -3663,6 +3650,19 @@ static struct vm_struct *vmap_get_aligned_vm_area(unsigned long size,
>>  	return __get_vm_area_node_aligned_caller(size, PAGE_SIZE, flags, caller);
>>  }
>>
>> +static inline bool vmap_page_sanity_checks(struct page **pages, unsigned int count)
>> +{
>> +	for (int i = 0; i < count; ++i) {
>> +		if (WARN_ON(!pages[i]))
>> +			return true;
>> +
>> +		if (WARN_ON(!pfn_valid(page_to_pfn(pages[i]))))
>> +			return true;
>> +	}
>> +
>> +	return false;
>> +}
>> +
>>  /**
>>   * vmap - map an array of pages into virtually contiguous space
>>   * @pages: array of page pointers
>> @@ -3706,6 +3706,9 @@ void *vmap(struct page **pages, unsigned int count,
>>  	if (!area)
>>  		return NULL;
>>
>> +	if (unlikely(vmap_page_sanity_checks(pages, count)))
>> +		return NULL;
>> +
>>  	addr = (unsigned long)area->addr;
>>  	if (vmap_pages_range_batched(addr, addr + size, pgprot_nx(prot),
>>  				pages) < 0) {
>>
>>
>>
>> Reasoning for calling vmap_page_sanity_checks() before vmap_pages_range_batched,
>> and not at the start of vmap: I am worried that since vmap() is already very
>> fast, we may cause a regression:
>>
>> vmap() -> scan page array with linear map pointers -> vmap_get_aligned_vm_area (does
>> memory allocation, throwing out the linear map VAs from cache and TLB) -> walk
>> the pgtables and again access cold page array.
>>
>> Perhaps I am being very pedantic here. What do you think?
>>
> Sanity check adds extra CPU cycles and it adds overhead. The concern about cache
> to be cold on second iteration looks valid. You can get some perf figures to see
> the cost.
> 
> I would just keep the original approach. But no strong opinion here.

Given you don't have a strong opinion here, I would also prefer keeping the original
approach. This feels more like a code structure problem than a correctness problem,
*and* given that with kmsan builds we have this problem for years now in the kernel.

So perhaps we can look at how to solve this later.


> 
> --
> Uladzislau Rezki



^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 5/6] mm/vmalloc: map contiguous pages in batches for vmap() if possible
  2026-07-09  7:38 ` [PATCH v6 5/6] mm/vmalloc: map contiguous pages in batches for vmap() if possible Wen Jiang
  2026-07-13 15:19   ` Dev Jain
@ 2026-07-14  4:59   ` Dev Jain
  1 sibling, 0 replies; 27+ messages in thread
From: Dev Jain @ 2026-07-14  4:59 UTC (permalink / raw)
  To: Wen Jiang, akpm, catalin.marinas, linux-mm, urezki, will
  Cc: Xueyuan.chen21, ajd, anshuman.khandual, david, linux-arm-kernel,
	linux-kernel, rppt, ryan.roberts, Barry Song (Xiaomi), Wen Jiang,
	Leo Yan



On 09/07/26 1:08 pm, Wen Jiang wrote:
> 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 <jiangwen6@xiaomi.com>
> Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
> Tested-by: Leo Yan <leo.yan@arm.com>
> ---
>  mm/vmalloc.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 85 insertions(+), 2 deletions(-)
> 
> diff --git a/mm/vmalloc.c b/mm/vmalloc.c
> index d2a4d649af549..db0492151ad08 100644
> --- a/mm/vmalloc.c
> +++ b/mm/vmalloc.c
> @@ -3543,6 +3543,89 @@ void vunmap(const void *addr)
>  }
>  EXPORT_SYMBOL(vunmap);
>  
> +static inline unsigned int vm_shift(pgprot_t prot, unsigned long size)
> +{
> +	if (arch_vmap_pmd_supported(prot) && size >= PMD_SIZE)
> +		return PMD_SHIFT;
> +
> +	return arch_vmap_pte_supported_shift(size);
> +}
> +
> +static inline int get_vmap_batch_order(struct page **pages,
> +		pgprot_t prot, unsigned int max_steps, unsigned int idx)
> +{
> +	unsigned int nr_contig;
> +	int order;
> +
> +	if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMAP))
> +		return 0;
> +
> +	nr_contig = num_pages_contiguous(&pages[idx], max_steps);
> +	if (nr_contig < 2)
> +		return 0;
> +
> +	order = ilog2(nr_contig);
> +
> +	/* Limit order by pfn alignment */
> +	order = min_t(int, order, __ffs(page_to_pfn(pages[idx])));

As said previously, you need to account for the case where pfn is zero.


> +
> +	if (vm_shift(prot, PAGE_SIZE << order) == PAGE_SHIFT)
> +		return 0;

Correct. There is no point in clamping the order by vm_shift(), because then
we won't be able to batch multiple cont blocks. So in case (PAGE_SIZE << order) is
a quantity less than CONT_PTE_SHIFT, simply bail out from batching.


> +
> +	return order;
> +}
> +
> +static int vmap_pages_range_batched(unsigned long addr, unsigned long end,
> +		pgprot_t prot, struct page **pages)
> +{
> +	unsigned int count = (end - addr) >> PAGE_SHIFT;
> +	unsigned int prev_shift = 0, idx = 0;
> +	unsigned long map_addr = addr, batch_end = addr;
> +	int err;
> +
> +	err = kmsan_vmap_pages_range_noflush(addr, end, prot, pages,
> +						PAGE_SHIFT, GFP_KERNEL);

Nit: Indentation. PAGE_SHIFT should start right below addr.

Apart from these two points, nothing jumped at me. So after these
are taken care of:

Reviewed-by: Dev Jain <dev.jain@arm.com>



> +	if (err)
> +		goto out;
> +
> +	for (unsigned int i = 0; i < count; ) {
> +		unsigned int shift = PAGE_SHIFT +
> +			get_vmap_batch_order(pages, prot, count - i, i);
> +
> +		if (!i)
> +			prev_shift = shift;
> +
> +		if (shift != prev_shift) {
> +			err = vmap_pages_range_noflush_walk(map_addr, batch_end,
> +					prot, pages + idx, prev_shift);
> +			if (err)
> +				goto out;
> +			prev_shift = shift;
> +			map_addr = batch_end;
> +			idx = i;
> +		}
> +
> +		/*
> +		 * Once small pages are encountered, the remaining pages
> +		 * are likely small as well.
> +		 */
> +		if (shift == PAGE_SHIFT)
> +			break;
> +
> +		batch_end += 1UL << shift;
> +		i += 1U << (shift - PAGE_SHIFT);
> +	}
> +
> +	/* Remaining */
> +	if (map_addr < end)
> +		err = vmap_pages_range_noflush_walk(map_addr, end,
> +				prot, pages + idx, prev_shift);
> +
> +out:
> +	flush_cache_vmap(addr, end);
> +	return err;
> +}
> +
>  /**
>   * vmap - map an array of pages into virtually contiguous space
>   * @pages: array of page pointers
> @@ -3586,8 +3669,8 @@ void *vmap(struct page **pages, unsigned int count,
>  		return NULL;
>  
>  	addr = (unsigned long)area->addr;
> -	if (vmap_pages_range(addr, addr + size, pgprot_nx(prot),
> -				pages, PAGE_SHIFT) < 0) {
> +	if (vmap_pages_range_batched(addr, addr + size, pgprot_nx(prot),
> +				pages) < 0) {
>  		vunmap(area->addr);
>  		return NULL;
>  	}



^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 6/6] mm/vmalloc: align vm_area so vmap() can batch mappings
  2026-07-09  7:38 ` [PATCH v6 6/6] mm/vmalloc: align vm_area so vmap() can batch mappings Wen Jiang
@ 2026-07-14  5:05   ` Dev Jain
  0 siblings, 0 replies; 27+ messages in thread
From: Dev Jain @ 2026-07-14  5:05 UTC (permalink / raw)
  To: Wen Jiang, akpm, catalin.marinas, linux-mm, urezki, will
  Cc: Xueyuan.chen21, ajd, anshuman.khandual, david, linux-arm-kernel,
	linux-kernel, rppt, ryan.roberts, Barry Song (Xiaomi), Wen Jiang,
	Leo Yan



On 09/07/26 1:08 pm, Wen Jiang wrote:
> 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 <jiangwen6@xiaomi.com>
> Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
> 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(-)
> 
> diff --git a/mm/vmalloc.c b/mm/vmalloc.c
> index db0492151ad08..2ac805cdf18ad 100644
> --- a/mm/vmalloc.c
> +++ b/mm/vmalloc.c
> @@ -3314,6 +3314,14 @@ struct vm_struct *get_vm_area_caller(unsigned long size, unsigned long flags,
>  				  NUMA_NO_NODE, GFP_KERNEL, caller);
>  }
>  
> +static struct vm_struct *__get_vm_area_node_aligned_caller(unsigned long size,
> +		unsigned long align, unsigned long flags, const void *caller)
> +{
> +	return __get_vm_area_node(size, align, PAGE_SHIFT, flags,
> +				  VMALLOC_START, VMALLOC_END,
> +				  NUMA_NO_NODE, GFP_KERNEL, caller);
> +}
> +
>  /**
>   * find_vm_area - find a continuous kernel virtual area
>   * @addr:	  base address
> @@ -3626,6 +3634,30 @@ static int vmap_pages_range_batched(unsigned long addr, unsigned long end,
>  	return err;
>  }
>  
> +static struct vm_struct *vmap_get_aligned_vm_area(unsigned long size,
> +		unsigned long flags, pgprot_t prot, const void *caller)
> +{
> +	struct vm_struct *vm_area;
> +	unsigned int shift;
> +
> +	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)
> +			return vm_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)
> +			return vm_area;
> +	}
> +
> +	return __get_vm_area_node_aligned_caller(size, PAGE_SIZE, flags, caller);
> +}
> +
>  /**
>   * vmap - map an array of pages into virtually contiguous space
>   * @pages: array of page pointers
> @@ -3664,7 +3696,8 @@ void *vmap(struct page **pages, unsigned int count,
>  		return NULL;
>  
>  	size = (unsigned long)count << PAGE_SHIFT;
> -	area = get_vm_area_caller(size, flags, __builtin_return_address(0));
> +	area = vmap_get_aligned_vm_area(size, flags, prot,
> +				__builtin_return_address(0));
>  	if (!area)
>  		return NULL;
>  



^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 5/6] mm/vmalloc: map contiguous pages in batches for vmap() if possible
  2026-07-13 15:19   ` Dev Jain
@ 2026-07-14  5:16     ` Wen Jiang
  2026-07-14  5:33       ` Dev Jain
  0 siblings, 1 reply; 27+ messages in thread
From: Wen Jiang @ 2026-07-14  5:16 UTC (permalink / raw)
  To: Dev Jain
  Cc: akpm, catalin.marinas, linux-mm, urezki, will, Xueyuan.chen21,
	ajd, anshuman.khandual, david, linux-arm-kernel, linux-kernel,
	rppt, ryan.roberts, Barry Song (Xiaomi), Wen Jiang, Leo Yan

On Mon, 13 Jul 2026 at 23:19, Dev Jain <dev.jain@arm.com> wrote:
>
>
>
> On 09/07/26 1:08 pm, Wen Jiang wrote:
> > 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 <jiangwen6@xiaomi.com>
> > Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
> > Tested-by: Leo Yan <leo.yan@arm.com>
> > ---
> >  mm/vmalloc.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++--
> >  1 file changed, 85 insertions(+), 2 deletions(-)
> >
> > diff --git a/mm/vmalloc.c b/mm/vmalloc.c
> > index d2a4d649af549..db0492151ad08 100644
> > --- a/mm/vmalloc.c
> > +++ b/mm/vmalloc.c
> > @@ -3543,6 +3543,89 @@ void vunmap(const void *addr)
> >  }
> >  EXPORT_SYMBOL(vunmap);
> >
> > +static inline unsigned int vm_shift(pgprot_t prot, unsigned long size)
> > +{
> > +     if (arch_vmap_pmd_supported(prot) && size >= PMD_SIZE)
> > +             return PMD_SHIFT;
> > +
> > +     return arch_vmap_pte_supported_shift(size);
> > +}
>
> Need to throw in a preparatory patch for this, which will (in addition to
> introduction of the vm_shift() function) do:
>

Hi Dev,

Your suggestion is to extract "vm_shift" into a separate patch, say
Patch 5, to make it a common utility function just like Patch 3. And
move the existing code that can use vm_shift into this new patch as
well, correct?

Thanks,
Wen

> diff --git a/mm/vmalloc.c b/mm/vmalloc.c
> index afaa14ebf17bb..dac87e1cd484b 100644
> --- a/mm/vmalloc.c
> +++ b/mm/vmalloc.c
> @@ -4175,10 +4175,7 @@ void *__vmalloc_node_range_noprof(unsigned long size, unsigned long align,
>                  * supporting them.
>                  */
>
> -               if (arch_vmap_pmd_supported(prot) && size >= PMD_SIZE)
> -                       shift = PMD_SHIFT;
> -               else
> -                       shift = arch_vmap_pte_supported_shift(size);
> +               shift = vm_shift(prot, size);
>
>                 align = max(original_align, 1UL << shift);
>         }
>
>
>
> > +
> > +static inline int get_vmap_batch_order(struct page **pages,
> > +             pgprot_t prot, unsigned int max_steps, unsigned int idx)
> > +{
> > +     unsigned int nr_contig;
> > +     int order;
> > +
> > +     if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMAP))
> > +             return 0;
> > +
> > +     nr_contig = num_pages_contiguous(&pages[idx], max_steps);
> > +     if (nr_contig < 2)
> > +             return 0;
> > +
> > +     order = ilog2(nr_contig);
> > +
> > +     /* Limit order by pfn alignment */
> > +     order = min_t(int, order, __ffs(page_to_pfn(pages[idx])));
>
> You missed Sashiko's point here :) the pfn may be zero and this
> will blow up. You can just first derive the pfn and do the clamping
> only when pfn > 0.
>
>

Will fix this.

Thanks,
Wen
>
> > +
> > +     if (vm_shift(prot, PAGE_SIZE << order) == PAGE_SHIFT)
> > +             return 0;
> > +
> > +     return order;
> > +}
> > +


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 5/6] mm/vmalloc: map contiguous pages in batches for vmap() if possible
  2026-07-14  5:16     ` Wen Jiang
@ 2026-07-14  5:33       ` Dev Jain
  0 siblings, 0 replies; 27+ messages in thread
From: Dev Jain @ 2026-07-14  5:33 UTC (permalink / raw)
  To: Wen Jiang
  Cc: akpm, catalin.marinas, linux-mm, urezki, will, Xueyuan.chen21,
	ajd, anshuman.khandual, david, linux-arm-kernel, linux-kernel,
	rppt, ryan.roberts, Barry Song (Xiaomi), Wen Jiang, Leo Yan



On 14/07/26 10:46 am, Wen Jiang wrote:
> On Mon, 13 Jul 2026 at 23:19, Dev Jain <dev.jain@arm.com> wrote:
>>
>>
>>
>> On 09/07/26 1:08 pm, Wen Jiang wrote:
>>> 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 <jiangwen6@xiaomi.com>
>>> Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
>>> Tested-by: Leo Yan <leo.yan@arm.com>
>>> ---
>>>  mm/vmalloc.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++--
>>>  1 file changed, 85 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/mm/vmalloc.c b/mm/vmalloc.c
>>> index d2a4d649af549..db0492151ad08 100644
>>> --- a/mm/vmalloc.c
>>> +++ b/mm/vmalloc.c
>>> @@ -3543,6 +3543,89 @@ void vunmap(const void *addr)
>>>  }
>>>  EXPORT_SYMBOL(vunmap);
>>>
>>> +static inline unsigned int vm_shift(pgprot_t prot, unsigned long size)
>>> +{
>>> +     if (arch_vmap_pmd_supported(prot) && size >= PMD_SIZE)
>>> +             return PMD_SHIFT;
>>> +
>>> +     return arch_vmap_pte_supported_shift(size);
>>> +}
>>
>> Need to throw in a preparatory patch for this, which will (in addition to
>> introduction of the vm_shift() function) do:
>>
> 
> Hi Dev,
> 
> Your suggestion is to extract "vm_shift" into a separate patch, say
> Patch 5, to make it a common utility function just like Patch 3. And
> move the existing code that can use vm_shift into this new patch as
> well, correct?


Yes it will look like the current patch 3. So before the current patch 5,
you can have a patch introducing vm_shift and making the code chunk
in __vmalloc_node_range_noprof() call it.



> 
> Thanks,
> Wen
> 
>> diff --git a/mm/vmalloc.c b/mm/vmalloc.c
>> index afaa14ebf17bb..dac87e1cd484b 100644
>> --- a/mm/vmalloc.c
>> +++ b/mm/vmalloc.c
>> @@ -4175,10 +4175,7 @@ void *__vmalloc_node_range_noprof(unsigned long size, unsigned long align,
>>                  * supporting them.
>>                  */
>>
>> -               if (arch_vmap_pmd_supported(prot) && size >= PMD_SIZE)
>> -                       shift = PMD_SHIFT;
>> -               else
>> -                       shift = arch_vmap_pte_supported_shift(size);
>> +               shift = vm_shift(prot, size);
>>
>>                 align = max(original_align, 1UL << shift);
>>         }
>>
>>
>>
>>> +
>>> +static inline int get_vmap_batch_order(struct page **pages,
>>> +             pgprot_t prot, unsigned int max_steps, unsigned int idx)
>>> +{
>>> +     unsigned int nr_contig;
>>> +     int order;
>>> +
>>> +     if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMAP))
>>> +             return 0;
>>> +
>>> +     nr_contig = num_pages_contiguous(&pages[idx], max_steps);
>>> +     if (nr_contig < 2)
>>> +             return 0;
>>> +
>>> +     order = ilog2(nr_contig);
>>> +
>>> +     /* Limit order by pfn alignment */
>>> +     order = min_t(int, order, __ffs(page_to_pfn(pages[idx])));
>>
>> You missed Sashiko's point here :) the pfn may be zero and this
>> will blow up. You can just first derive the pfn and do the clamping
>> only when pfn > 0.
>>
>>
> 
> Will fix this.
> 
> Thanks,
> Wen
>>
>>> +
>>> +     if (vm_shift(prot, PAGE_SIZE << order) == PAGE_SHIFT)
>>> +             return 0;
>>> +
>>> +     return order;
>>> +}
>>> +



^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 4/6] mm/vmalloc: Extend page table walk to support larger page_shift sizes and eliminate page table rewalk
  2026-07-13 13:49   ` Dev Jain
@ 2026-07-14  6:10     ` Wen Jiang
  2026-07-14  6:45       ` Dev Jain
  0 siblings, 1 reply; 27+ messages in thread
From: Wen Jiang @ 2026-07-14  6:10 UTC (permalink / raw)
  To: Dev Jain
  Cc: akpm, catalin.marinas, linux-mm, urezki, will, Xueyuan.chen21,
	ajd, anshuman.khandual, david, linux-arm-kernel, linux-kernel,
	rppt, ryan.roberts, Barry Song (Xiaomi), Wen Jiang, Leo Yan

On Mon, 13 Jul 2026 at 21:49, Dev Jain <dev.jain@arm.com> wrote:
>
>
>
> On 09/07/26 1:08 pm, Wen Jiang wrote:
> > 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 <jiangwen6@xiaomi.com>
> > Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
> > Tested-by: Leo Yan <leo.yan@arm.com>
> > Reviewed-by: Dev Jain <dev.jain@arm.com>
> > ---
> >  mm/vmalloc.c | 81 ++++++++++++++++++++++++++++++----------------------
> >  1 file changed, 47 insertions(+), 34 deletions(-)
> >
> > diff --git a/mm/vmalloc.c b/mm/vmalloc.c
> > index d7fef85b241c4..d2a4d649af549 100644
> > --- a/mm/vmalloc.c
> > +++ b/mm/vmalloc.c
> > @@ -125,7 +125,8 @@ static int vmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
> >       pte_t *pte;
> >       u64 pfn;
> >       struct page *page;
> > -     unsigned long size = PAGE_SIZE;
> > +     unsigned long size;
> > +     unsigned int steps;
> >
> >       if (WARN_ON_ONCE(!PAGE_ALIGNED(end - addr)))
> >               return -EINVAL;
> > @@ -147,8 +148,8 @@ static int vmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
> >               }
> >
> >               size = vmap_set_ptes(pte, addr, end, pfn, prot, max_page_shift);
> > -             pfn += PFN_DOWN(size);
> > -     } while (pte += PFN_DOWN(size), addr += size, addr != end);
> > +             steps = PFN_DOWN(size);
> > +     } while (pte += steps, pfn += steps, addr += size, addr != end);
>
> I think I had also recommended to put these vmap_pte_range() changes
> into the previous patch.
>

Hi Dev

I put this change in patch 4 following Uladzislau's earlier suggestion:

https://lore.kernel.org/linux-mm/ah3Cn3YnefIVJDo2@milan/

Uladzislau, do you have a preference on where this hunk should go?

Thanks,
Wen


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 4/6] mm/vmalloc: Extend page table walk to support larger page_shift sizes and eliminate page table rewalk
  2026-07-14  6:10     ` Wen Jiang
@ 2026-07-14  6:45       ` Dev Jain
  0 siblings, 0 replies; 27+ messages in thread
From: Dev Jain @ 2026-07-14  6:45 UTC (permalink / raw)
  To: Wen Jiang
  Cc: akpm, catalin.marinas, linux-mm, urezki, will, Xueyuan.chen21,
	ajd, anshuman.khandual, david, linux-arm-kernel, linux-kernel,
	rppt, ryan.roberts, Barry Song (Xiaomi), Wen Jiang, Leo Yan



On 14/07/26 11:40 am, Wen Jiang wrote:
> On Mon, 13 Jul 2026 at 21:49, Dev Jain <dev.jain@arm.com> wrote:
>>
>>
>>
>> On 09/07/26 1:08 pm, Wen Jiang wrote:
>>> 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 <jiangwen6@xiaomi.com>
>>> Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
>>> Tested-by: Leo Yan <leo.yan@arm.com>
>>> Reviewed-by: Dev Jain <dev.jain@arm.com>
>>> ---
>>>  mm/vmalloc.c | 81 ++++++++++++++++++++++++++++++----------------------
>>>  1 file changed, 47 insertions(+), 34 deletions(-)
>>>
>>> diff --git a/mm/vmalloc.c b/mm/vmalloc.c
>>> index d7fef85b241c4..d2a4d649af549 100644
>>> --- a/mm/vmalloc.c
>>> +++ b/mm/vmalloc.c
>>> @@ -125,7 +125,8 @@ static int vmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
>>>       pte_t *pte;
>>>       u64 pfn;
>>>       struct page *page;
>>> -     unsigned long size = PAGE_SIZE;
>>> +     unsigned long size;
>>> +     unsigned int steps;
>>>
>>>       if (WARN_ON_ONCE(!PAGE_ALIGNED(end - addr)))
>>>               return -EINVAL;
>>> @@ -147,8 +148,8 @@ static int vmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
>>>               }
>>>
>>>               size = vmap_set_ptes(pte, addr, end, pfn, prot, max_page_shift);
>>> -             pfn += PFN_DOWN(size);
>>> -     } while (pte += PFN_DOWN(size), addr += size, addr != end);
>>> +             steps = PFN_DOWN(size);
>>> +     } while (pte += steps, pfn += steps, addr += size, addr != end);
>>
>> I think I had also recommended to put these vmap_pte_range() changes
>> into the previous patch.
>>
> 
> Hi Dev
> 
> I put this change in patch 4 following Uladzislau's earlier suggestion:
> 
> https://lore.kernel.org/linux-mm/ah3Cn3YnefIVJDo2@milan/
> 
> Uladzislau, do you have a preference on where this hunk should go?
> 

Sorry if I wasn't clear enough. I am not contradicting Uladzislau's comment -
separate patch 3 and 4 are good. I was just commenting on the specific change
you did here w.r.t vmap_pte_range.

The only bit you change above is to introduce a local variable 'steps'. You
can move this bit into the previous patch, so that patch 4 is completely
focussed on extending the vmap_pages_* functions to accept a 'shift'.


> Thanks,
> Wen



^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 1/6] arm64/hugetlb: Extend batching of multiple CONT_PTE in a single PTE setup
  2026-07-09  7:38 ` [PATCH v6 1/6] arm64/hugetlb: Extend batching of multiple CONT_PTE in a single PTE setup Wen Jiang
@ 2026-07-14  6:54   ` Anshuman Khandual
  2026-07-14  8:35     ` Wen Jiang
  0 siblings, 1 reply; 27+ messages in thread
From: Anshuman Khandual @ 2026-07-14  6:54 UTC (permalink / raw)
  To: Wen Jiang, akpm, catalin.marinas, linux-mm, urezki, will
  Cc: Xueyuan.chen21, ajd, david, linux-arm-kernel, linux-kernel, rppt,
	ryan.roberts, dev.jain, Barry Song (Xiaomi), Wen Jiang, Leo Yan

On 09/07/26 1:08 PM, Wen Jiang wrote:
> From: "Barry Song (Xiaomi)" <baohua@kernel.org>
> 
> For sizes aligned to CONT_PTE_SIZE and smaller than PMD_SIZE,
> we can handle CONT_PTE_SIZE groups together.
> 
> Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
> Signed-off-by: Wen Jiang <jiangwen6@xiaomi.com>
> Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
> Tested-by: Leo Yan <leo.yan@arm.com>
> Reviewed-by: Dev Jain <dev.jain@arm.com>
> ---
>  arch/arm64/mm/hugetlbpage.c | 10 ++++++++++
>  1 file changed, 10 insertions(+)
> 
> diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c
> index a42c05cf56408..0da746f729388 100644
> --- a/arch/arm64/mm/hugetlbpage.c
> +++ b/arch/arm64/mm/hugetlbpage.c
> @@ -110,6 +110,12 @@ static inline int num_contig_ptes(unsigned long size, size_t *pgsize)
>  		contig_ptes = CONT_PTES;
>  		break;
>  	default:
> +		if (size > 0 && size < PMD_SIZE &&

Is there a possibility for size <= 0 ? If not then
the above conditional check might be redundant.
> +				IS_ALIGNED(size, CONT_PTE_SIZE)) {
> +			*pgsize = PAGE_SIZE;
> +			contig_ptes = size >> PAGE_SHIFT;
> +			break;
> +		}
>  		WARN_ON(!__hugetlb_valid_size(size));

Would not these CONT_PTE_SIZE aligned sizes upto PMD_SIZE
trigger warning here because __hugetlb_valid_size() still
does not enumerate them ?
>  	}
>  
> @@ -359,6 +365,10 @@ pte_t arch_make_huge_pte(pte_t entry, unsigned int shift, vm_flags_t flags)
>  	case CONT_PTE_SIZE:
>  		return pte_mkcont(entry);
>  	default:
> +		if (pagesize > 0 && pagesize < PMD_SIZE &&
> +				IS_ALIGNED(pagesize, CONT_PTE_SIZE))
> +			return pte_mkcont(entry);
> +
>  		break;
>  	}
>  	pr_warn("%s: unrecognized huge page size 0x%lx\n",



^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 2/6] arm64/vmalloc: Allow arch_vmap_pte_range_map_size to batch multiple CONT_PTE
  2026-07-09  7:38 ` [PATCH v6 2/6] arm64/vmalloc: Allow arch_vmap_pte_range_map_size to batch multiple CONT_PTE Wen Jiang
@ 2026-07-14  7:13   ` Anshuman Khandual
  2026-07-14  9:24     ` Wen Jiang
  0 siblings, 1 reply; 27+ messages in thread
From: Anshuman Khandual @ 2026-07-14  7:13 UTC (permalink / raw)
  To: Wen Jiang, akpm, catalin.marinas, linux-mm, urezki, will
  Cc: Xueyuan.chen21, ajd, david, linux-arm-kernel, linux-kernel, rppt,
	ryan.roberts, dev.jain, Barry Song (Xiaomi), Wen Jiang, Leo Yan



On 09/07/26 1:08 PM, Wen Jiang wrote:
> 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.

Too little commit description for the proposed change here.
> 
> Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
> Signed-off-by: Wen Jiang <jiangwen6@xiaomi.com>
> Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
> Tested-by: Leo Yan <leo.yan@arm.com>
> Reviewed-by: Dev Jain <dev.jain@arm.com>
> ---
>  arch/arm64/include/asm/vmalloc.h | 6 +++++-
>  1 file changed, 5 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm64/include/asm/vmalloc.h b/arch/arm64/include/asm/vmalloc.h
> index 4ec1acd3c1b34..7d9c7dc795c42 100644
> --- a/arch/arm64/include/asm/vmalloc.h
> +++ b/arch/arm64/include/asm/vmalloc.h
> @@ -23,6 +23,8 @@ static inline unsigned long arch_vmap_pte_range_map_size(unsigned long addr,
>  						unsigned long end, u64 pfn,
>  						unsigned int max_page_shift)
>  {
> +	unsigned long size;
> +
>  	/*
>  	 * If the block is at least CONT_PTE_SIZE in size, and is naturally
>  	 * aligned in both virtual and physical space, then we can pte-map the
> @@ -40,7 +42,9 @@ static inline unsigned long arch_vmap_pte_range_map_size(unsigned long addr,
>  	if (!IS_ALIGNED(PFN_PHYS(pfn), CONT_PTE_SIZE))
>  		return PAGE_SIZE;
>  
> -	return CONT_PTE_SIZE;
> +	size = min3(end - addr, 1UL << max_page_shift, PMD_SIZE >> 1);
> +	size = rounddown_pow_of_two(size);
> +	return size;

Please do explain the fact in a comment that huge pte mappings
upto PMD_SIZE are being allowed here, if the given block is
CONT_PTE_SIZE aligned.

IIUC arch_vmap_pte_range_map_size() gets used only when config
CONFIG_HUGETLB_PAGE is enabled. Hence should not these new huge
sizes being supported here also be added as valid HugeTLB sizes
thus updating __hugetlb_valid_size() and adding corresponding
new HugeTLB page sizes with hugetlb_add_hstate() ?

OR could arch_vmap_pte_range_map_size() and set_huge_pte_at()
can be updated for vmalloc without doing corresponding changes
into HugeTLB itself ?
>  }
>  
>  #define arch_vmap_pte_range_unmap_size arch_vmap_pte_range_unmap_size



^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 1/6] arm64/hugetlb: Extend batching of multiple CONT_PTE in a single PTE setup
  2026-07-14  6:54   ` Anshuman Khandual
@ 2026-07-14  8:35     ` Wen Jiang
  0 siblings, 0 replies; 27+ messages in thread
From: Wen Jiang @ 2026-07-14  8:35 UTC (permalink / raw)
  To: Anshuman Khandual
  Cc: akpm, catalin.marinas, linux-mm, urezki, will, Xueyuan.chen21,
	ajd, david, linux-arm-kernel, linux-kernel, rppt, ryan.roberts,
	dev.jain, Barry Song (Xiaomi), Wen Jiang, Leo Yan

On Tue, 14 Jul 2026 at 14:54, Anshuman Khandual
<anshuman.khandual@arm.com> wrote:
>
> On 09/07/26 1:08 PM, Wen Jiang wrote:
> > From: "Barry Song (Xiaomi)" <baohua@kernel.org>
> >
> > For sizes aligned to CONT_PTE_SIZE and smaller than PMD_SIZE,
> > we can handle CONT_PTE_SIZE groups together.
> >
> > Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
> > Signed-off-by: Wen Jiang <jiangwen6@xiaomi.com>
> > Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
> > Tested-by: Leo Yan <leo.yan@arm.com>
> > Reviewed-by: Dev Jain <dev.jain@arm.com>
> > ---
> >  arch/arm64/mm/hugetlbpage.c | 10 ++++++++++
> >  1 file changed, 10 insertions(+)
> >
> > diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c
> > index a42c05cf56408..0da746f729388 100644
> > --- a/arch/arm64/mm/hugetlbpage.c
> > +++ b/arch/arm64/mm/hugetlbpage.c
> > @@ -110,6 +110,12 @@ static inline int num_contig_ptes(unsigned long size, size_t *pgsize)
> >               contig_ptes = CONT_PTES;
> >               break;
> >       default:
> > +             if (size > 0 && size < PMD_SIZE &&
>
> Is there a possibility for size <= 0 ? If not then
> the above conditional check might be redundant.

Hi Anshuman,

The "size > 0" check is to guard against size == 0.

> > +                             IS_ALIGNED(size, CONT_PTE_SIZE)) {
> > +                     *pgsize = PAGE_SIZE;
> > +                     contig_ptes = size >> PAGE_SHIFT;
> > +                     break;
> > +             }
> >               WARN_ON(!__hugetlb_valid_size(size));
>
> Would not these CONT_PTE_SIZE aligned sizes upto PMD_SIZE
> trigger warning here because __hugetlb_valid_size() still
> does not enumerate them ?

The intermediate CONT_PTE_SIZE-aligned sizes will not hit the WARN_ON(),
as they are handled by the new branch and break before the
__hugetlb_valid_size() check. They are accepted here for vmalloc's partial
contiguous-PTE mappings.

Thanks,
Wen
> >       }
> >
> > @@ -359,6 +365,10 @@ pte_t arch_make_huge_pte(pte_t entry, unsigned int shift, vm_flags_t flags)
> >       case CONT_PTE_SIZE:
> >               return pte_mkcont(entry);
> >       default:
> > +             if (pagesize > 0 && pagesize < PMD_SIZE &&
> > +                             IS_ALIGNED(pagesize, CONT_PTE_SIZE))
> > +                     return pte_mkcont(entry);
> > +
> >               break;
> >       }
> >       pr_warn("%s: unrecognized huge page size 0x%lx\n",
>


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory
  2026-07-10  8:54   ` Wen Jiang
  2026-07-10  8:59     ` Wen Jiang
  2026-07-13 15:13     ` Dev Jain
@ 2026-07-14  8:36     ` Anshuman Khandual
  2026-07-14 11:17       ` Dev Jain
  2 siblings, 1 reply; 27+ messages in thread
From: Anshuman Khandual @ 2026-07-14  8:36 UTC (permalink / raw)
  To: Wen Jiang, Andrew Morton
  Cc: catalin.marinas, linux-mm, urezki, will, Xueyuan.chen21, ajd,
	david, linux-arm-kernel, linux-kernel, rppt, ryan.roberts,
	dev.jain, Wen Jiang



On 10/07/26 2:24 PM, Wen Jiang wrote:
> On Fri, 10 Jul 2026 at 07:08, Andrew Morton <akpm@linux-foundation.org> wrote:
>>
>> On Thu,  9 Jul 2026 15:38:17 +0800 Wen Jiang <jiangwenxiaomi@gmail.com> wrote:
>>
>>> This patchset accelerates ioremap, vmalloc, and vmap when the memory
>>> is physically fully or partially contiguous.
>>
>> Thanks, I added this to mm.git's mm-new branch for wider testing.
>>
>> AI review asked some questions, and some of them are new since the v5
>> series:
>>         https://sashiko.dev/#/patchset/20260709073823.6643-1-jiangwen6@xiaomi.com
> 
> Hi Andrew,
> 
> I've gone through the Sashiko findings:
> 
> - Patch 1 (find_num_contig): Over-interpretation. No new hugetlbfs hstate
>   is added. The extra sizes are only used by init_mm kernel mappings via.

But not sure if that is a right approach. If these multi CONT_PTE
sized mappings need to be supported in vmalloc() but without adding
corresponding HugeTLB sizes, probably these required helpers could
just be factored outside HugeTLB.
> 
> - Patch 5/6 (NULL page): Invalid input. vmap() expects a fully populated
>     array of valid struct page pointers.
> 
> - Patch 6 (32-bit count << PAGE_SHIFT overflow): Pre-existing. This was
>     already discussed in the V3 thread, and a separate fix was proposed
>     there.
> 
> Thanks,
> Wen



^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 2/6] arm64/vmalloc: Allow arch_vmap_pte_range_map_size to batch multiple CONT_PTE
  2026-07-14  7:13   ` Anshuman Khandual
@ 2026-07-14  9:24     ` Wen Jiang
  0 siblings, 0 replies; 27+ messages in thread
From: Wen Jiang @ 2026-07-14  9:24 UTC (permalink / raw)
  To: Anshuman Khandual
  Cc: akpm, catalin.marinas, linux-mm, urezki, will, Xueyuan.chen21,
	ajd, david, linux-arm-kernel, linux-kernel, rppt, ryan.roberts,
	dev.jain, Barry Song (Xiaomi), Wen Jiang, Leo Yan

On Tue, 14 Jul 2026 at 15:13, Anshuman Khandual
<anshuman.khandual@arm.com> wrote:
>
>
>
> On 09/07/26 1:08 PM, Wen Jiang wrote:
> > 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.
>
> Too little commit description for the proposed change here.
> >
> > Signed-off-by: Barry Song (Xiaomi) <baohua@kernel.org>
> > Signed-off-by: Wen Jiang <jiangwen6@xiaomi.com>
> > Tested-by: Xueyuan Chen <xueyuan.chen21@gmail.com>
> > Tested-by: Leo Yan <leo.yan@arm.com>
> > Reviewed-by: Dev Jain <dev.jain@arm.com>
> > ---
> >  arch/arm64/include/asm/vmalloc.h | 6 +++++-
> >  1 file changed, 5 insertions(+), 1 deletion(-)
> >
> > diff --git a/arch/arm64/include/asm/vmalloc.h b/arch/arm64/include/asm/vmalloc.h
> > index 4ec1acd3c1b34..7d9c7dc795c42 100644
> > --- a/arch/arm64/include/asm/vmalloc.h
> > +++ b/arch/arm64/include/asm/vmalloc.h
> > @@ -23,6 +23,8 @@ static inline unsigned long arch_vmap_pte_range_map_size(unsigned long addr,
> >                                               unsigned long end, u64 pfn,
> >                                               unsigned int max_page_shift)
> >  {
> > +     unsigned long size;
> > +
> >       /*
> >        * If the block is at least CONT_PTE_SIZE in size, and is naturally
> >        * aligned in both virtual and physical space, then we can pte-map the
> > @@ -40,7 +42,9 @@ static inline unsigned long arch_vmap_pte_range_map_size(unsigned long addr,
> >       if (!IS_ALIGNED(PFN_PHYS(pfn), CONT_PTE_SIZE))
> >               return PAGE_SIZE;
> >
> > -     return CONT_PTE_SIZE;
> > +     size = min3(end - addr, 1UL << max_page_shift, PMD_SIZE >> 1);
> > +     size = rounddown_pow_of_two(size);
> > +     return size;
>
> Please do explain the fact in a comment that huge pte mappings
> upto PMD_SIZE are being allowed here, if the given block is
> CONT_PTE_SIZE aligned.
>
> IIUC arch_vmap_pte_range_map_size() gets used only when config
> CONFIG_HUGETLB_PAGE is enabled. Hence should not these new huge
> sizes being supported here also be added as valid HugeTLB sizes
> thus updating __hugetlb_valid_size() and adding corresponding
> new HugeTLB page sizes with hugetlb_add_hstate() ?
>
> OR could arch_vmap_pte_range_map_size() and set_huge_pte_at()
> can be updated for vmalloc without doing corresponding changes
> into HugeTLB itself ?

These sizes are not new HugeTLB page sizes. They are only vmalloc mapping
spans selected for a particular virtually and physically aligned range, so that
the PTEs can be installed with the contiguous bit in larger batches.

Therefore I do not think they should be added to __hugetlb_valid_size() or
registered with hugetlb_add_hstate(). Those describe the HugeTLB hstate sizes,
while this change only affects how vmalloc chooses the PTE-level mapping span.

Thanks,
Wen
> >  }
> >
> >  #define arch_vmap_pte_range_unmap_size arch_vmap_pte_range_unmap_size
>


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory
  2026-07-14  8:36     ` Anshuman Khandual
@ 2026-07-14 11:17       ` Dev Jain
  0 siblings, 0 replies; 27+ messages in thread
From: Dev Jain @ 2026-07-14 11:17 UTC (permalink / raw)
  To: Anshuman Khandual, Wen Jiang, Andrew Morton
  Cc: catalin.marinas, linux-mm, urezki, will, Xueyuan.chen21, ajd,
	david, linux-arm-kernel, linux-kernel, rppt, ryan.roberts,
	Wen Jiang



On 14/07/26 2:06 pm, Anshuman Khandual wrote:
> 
> 
> On 10/07/26 2:24 PM, Wen Jiang wrote:
>> On Fri, 10 Jul 2026 at 07:08, Andrew Morton <akpm@linux-foundation.org> wrote:
>>>
>>> On Thu,  9 Jul 2026 15:38:17 +0800 Wen Jiang <jiangwenxiaomi@gmail.com> wrote:
>>>
>>>> This patchset accelerates ioremap, vmalloc, and vmap when the memory
>>>> is physically fully or partially contiguous.
>>>
>>> Thanks, I added this to mm.git's mm-new branch for wider testing.
>>>
>>> AI review asked some questions, and some of them are new since the v5
>>> series:
>>>         https://sashiko.dev/#/patchset/20260709073823.6643-1-jiangwen6@xiaomi.com
>>
>> Hi Andrew,
>>
>> I've gone through the Sashiko findings:
>>
>> - Patch 1 (find_num_contig): Over-interpretation. No new hugetlbfs hstate
>>   is added. The extra sizes are only used by init_mm kernel mappings via.
> 
> But not sure if that is a right approach. If these multi CONT_PTE
> sized mappings need to be supported in vmalloc() but without adding
> corresponding HugeTLB sizes, probably these required helpers could
> just be factored outside HugeTLB.

The problem is that the existing vmalloc-huge code reuses the hugetlb helpers
because it is easier that way.

If you really look at it, num_contig_ptes(), set_huge_pte_at() and arch_make_huge_pte()
do not have anything to do with hugetlbfs, but with huge mappings. It is unfortunate
that these helpers are sitting in hugetlbpage.c . Really these functions should be
pulled out of CONFIG_HUGETLBFS and put into some common header - but I can't think
of a clean solution to this.

So I think for this series, the least we can do is add a comment to clarify that these
helpers can be used by non-hugetlbfs mm code to set multiple huge mappings at the PTE level.


>>
>> - Patch 5/6 (NULL page): Invalid input. vmap() expects a fully populated
>>     array of valid struct page pointers.
>>
>> - Patch 6 (32-bit count << PAGE_SHIFT overflow): Pre-existing. This was
>>     already discussed in the V3 thread, and a separate fix was proposed
>>     there.
>>
>> Thanks,
>> Wen
> 



^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2026-07-14 11:17 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-09  7:38 [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory Wen Jiang
2026-07-09  7:38 ` [PATCH v6 1/6] arm64/hugetlb: Extend batching of multiple CONT_PTE in a single PTE setup Wen Jiang
2026-07-14  6:54   ` Anshuman Khandual
2026-07-14  8:35     ` Wen Jiang
2026-07-09  7:38 ` [PATCH v6 2/6] arm64/vmalloc: Allow arch_vmap_pte_range_map_size to batch multiple CONT_PTE Wen Jiang
2026-07-14  7:13   ` Anshuman Khandual
2026-07-14  9:24     ` Wen Jiang
2026-07-09  7:38 ` [PATCH v6 3/6] mm/vmalloc: Extract vmap_set_ptes() to consolidate PTE mapping logic Wen Jiang
2026-07-09  7:38 ` [PATCH v6 4/6] mm/vmalloc: Extend page table walk to support larger page_shift sizes and eliminate page table rewalk Wen Jiang
2026-07-13 13:49   ` Dev Jain
2026-07-14  6:10     ` Wen Jiang
2026-07-14  6:45       ` Dev Jain
2026-07-09  7:38 ` [PATCH v6 5/6] mm/vmalloc: map contiguous pages in batches for vmap() if possible Wen Jiang
2026-07-13 15:19   ` Dev Jain
2026-07-14  5:16     ` Wen Jiang
2026-07-14  5:33       ` Dev Jain
2026-07-14  4:59   ` Dev Jain
2026-07-09  7:38 ` [PATCH v6 6/6] mm/vmalloc: align vm_area so vmap() can batch mappings Wen Jiang
2026-07-14  5:05   ` Dev Jain
2026-07-09 23:08 ` [PATCH v6 0/6] mm/vmalloc: Speed up ioremap, vmalloc and vmap with contiguous memory Andrew Morton
2026-07-10  8:54   ` Wen Jiang
2026-07-10  8:59     ` Wen Jiang
2026-07-13 15:13     ` Dev Jain
2026-07-13 17:30       ` Uladzislau Rezki
2026-07-14  4:34         ` Dev Jain
2026-07-14  8:36     ` Anshuman Khandual
2026-07-14 11:17       ` Dev Jain

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox