mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2 0/7] mm: speed up ZONE_DEVICE memmap initialization
@ 2026-05-21  4:01 Li Zhe
  2026-05-21  4:01 ` [PATCH v2 1/7] mm: factor zone-device page init helpers out of __init_zone_device_page Li Zhe
                   ` (7 more replies)
  0 siblings, 8 replies; 19+ messages in thread
From: Li Zhe @ 2026-05-21  4:01 UTC (permalink / raw)
  To: apopple, akpm, arnd, bp, dave.hansen, david, mingo, rppt, tglx, kees
  Cc: linux-arch, linux-kernel, linux-mm, lizhe.67, x86, linux-hardening

memmap_init_zone_device() can spend a substantial amount of time
initializing large ZONE_DEVICE ranges because it repeats nearly
identical struct page setup for every PFN.

This series reduces that overhead in seven steps.

The first patch factors the reusable pieces out of
__init_zone_device_page() so later patches can share the same logic
without changing the existing slow path.

The second patch adds set_page_section_from_pfn(), so generic callers
can update section bits from a PFN without open-coding
SECTION_IN_PAGE_FLAGS checks.

The third patch adds a template-based fast path for ZONE_DEVICE head
pages. Instead of rebuilding the same struct page state for every PFN,
it prepares a reusable page template once and copies it to each
destination page.

The fourth patch extends the same template-based approach to compound
tails, so pfns_per_compound > 1 can also benefit from the fast path.

The fifth patch introduces memcpy_streaming() and
memcpy_streaming_drain() as a generic interface for write-once
streaming copies, with a memcpy() fallback for architectures that do
not provide a specialized backend.

The sixth patch extends x86 memcpy_flushcache() small fixed-size
fastpaths so struct-page-sized streaming copies can stay on the inline
path.

The last patch switches the zone-device template-copy path over to
memcpy_streaming(). It refreshes PFN-dependent fields in the reusable
template before each copy, keeps pageblock-aligned PFNs on regular
memcpy(), and drains streaming stores before later normal stores update
overlapping or dependent metadata.

The optimized path is disabled when the page_ref_set tracepoint is
enabled, sanitized builds remain on the slow path so their
instrumented stores are preserved, and the fast path falls back to the
existing slow path if sizeof(struct page) is not an integral number of
u64 words.

Testing
=======

Tests were run in a VM on an Intel Ice Lake server.

Two PMEM configurations were used:
  - a 100 GB fsdax namespace configured with map=dev, which exercises
    the nd_pmem rebind path (pfns_per_compound == 1)
  - a 100 GB devdax namespace configured with align=2097152, which
    exercises the dax_pmem rebind path (pfns_per_compound > 1)

For each configuration, the corresponding driver was unbound and
rebound 30 times. Memmap initialization latency was collected from the
pr_debug() output of memmap_init_zone_device().

The first bind is reported separately, and the average of subsequent
rebinds is used as the steady-state result.

Performance
===========

nd_pmem rebind, 100 GB fsdax namespace, map=dev
  Base(v7.1-rc3):
    First binding: 1486 ms
    Average of subsequent rebinds: 273.52 ms
  With patches 1-3 applied:
    First binding: 1422 ms
    Average of subsequent rebinds: 245.73 ms
  Full series:
    First binding: 1389 ms
    Average of subsequent rebinds: 111.08 ms

dax_pmem rebind, 100 GB devdax namespace, align=2097152
  Base(v7.1-rc3):
    First binding: 1515 ms
    Average of subsequent rebinds: 313.45 ms
  With patches 1-4 applied:
    First binding: 1422 ms
    Average of subsequent rebinds: 256.56 ms
  Full series:
    First binding: 1294 ms
    Average of subsequent rebinds: 110.24 ms

Li Zhe (7):
  mm: factor zone-device page init helpers out of
    __init_zone_device_page
  mm: add a set_page_section_from_pfn() helper
  mm: add a template-based fast path for zone-device page init
  mm: extend the template fast path to zone-device compound tails
  string: introduce memcpy_streaming() helpers
  x86/string: extend memcpy_flushcache() fixed-size fastpaths
  mm: use memcpy_streaming() in zone-device template copies

 arch/x86/include/asm/string_64.h | 100 +++++++++++++---
 include/linux/mm.h               |  19 ++-
 include/linux/string.h           |  18 +++
 mm/mm_init.c                     | 198 +++++++++++++++++++++++++++----
 4 files changed, 294 insertions(+), 41 deletions(-)

---
v1: https://lore.kernel.org/all/20260515082045.63029-1-lizhe.67@bytedance.com/

Changelogs:

v1->v2:
- Move the pageblock-helper split into patch 1, and add a dedicated
  set_page_section_from_pfn() helper so generic callers no longer
  open-code SECTION_IN_PAGE_FLAGS handling. Suggested by Mike Rapoport.
- Drop the v1 32-bit gating and document instead that CONFIG_ZONE_DEVICE
  is 64-bit only because it depends on MEMORY_HOTPLUG. Suggested by
  Mike Rapoport.
- Replace the v1 BUILD_BUG_ON() struct page layout checks with a runtime
  fast-path eligibility check that falls back to the slow path when the
  layout is unsuitable. Suggested by Mike Rapoport.
- Rename the template fast-path helpers to zone_device_* names for
  clarity. Suggested by Mike Rapoport.
- Replace the v1 open-coded arch_optimize_store_u64()/drain() approach
  with a generic memcpy_streaming()/memcpy_streaming_drain() interface,
  and move the x86 optimization under memcpy_flushcache(). Suggested by
  Alistair Popple.
- Split the old v1 streaming-copy patch into three patches: introduce
  the generic helper, extend the x86 backend, and then switch mm over
  to the new interface. This is part of the memcpy-based rework
  suggested by Alistair Popple.
- Refresh the performance section and report whole-series results as
  series-level numbers.
- Fix a missing memcpy_streaming_drain() in the compound-page
  initialization path, following review feedback from Andrew Morton.

-- 
2.20.1

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

* [PATCH v2 1/7] mm: factor zone-device page init helpers out of __init_zone_device_page
  2026-05-21  4:01 [PATCH v2 0/7] mm: speed up ZONE_DEVICE memmap initialization Li Zhe
@ 2026-05-21  4:01 ` Li Zhe
  2026-05-23 12:10   ` Mike Rapoport
  2026-05-21  4:01 ` [PATCH v2 2/7] mm: add a set_page_section_from_pfn() helper Li Zhe
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 19+ messages in thread
From: Li Zhe @ 2026-05-21  4:01 UTC (permalink / raw)
  To: apopple, akpm, arnd, bp, dave.hansen, david, mingo, rppt, tglx, kees
  Cc: linux-arch, linux-kernel, linux-mm, lizhe.67, x86, linux-hardening

memmap_init_zone_device() currently mixes refcount policy, core
ZONE_DEVICE page setup, and pageblock metadata handling in a single
helper.

Factor the refcount policy into zone_device_page_init_refcount(), move
the common page initialization into __zone_device_page_init(), split
pageblock handling into zone_device_page_init_pageblock(), and wrap the
existing slow path in zone_device_page_init_slow().

This keeps the slow-path behaviour unchanged and gives later patches
reusable helper boundaries.

No functional change intended.

Signed-off-by: Li Zhe <lizhe.67@bytedance.com>
---
 mm/mm_init.c | 64 +++++++++++++++++++++++++++++++++-------------------
 1 file changed, 41 insertions(+), 23 deletions(-)

diff --git a/mm/mm_init.c b/mm/mm_init.c
index f9f8e1af921c..4ba506df93bc 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -987,11 +987,36 @@ static void __init memmap_init(void)
 }
 
 #ifdef CONFIG_ZONE_DEVICE
-static void __ref __init_zone_device_page(struct page *page, unsigned long pfn,
+static inline int zone_device_page_init_refcount(
+		const struct dev_pagemap *pgmap)
+{
+	/*
+	 * ZONE_DEVICE pages other than MEMORY_TYPE_GENERIC are released
+	 * directly to the driver page allocator which will set the page count
+	 * to 1 when allocating the page.
+	 *
+	 * MEMORY_TYPE_GENERIC and MEMORY_TYPE_FS_DAX pages automatically have
+	 * their refcount reset to one whenever they are freed (ie. after
+	 * their refcount drops to 0).
+	 */
+	switch (pgmap->type) {
+	case MEMORY_DEVICE_FS_DAX:
+	case MEMORY_DEVICE_PRIVATE:
+	case MEMORY_DEVICE_COHERENT:
+	case MEMORY_DEVICE_PCI_P2PDMA:
+		return 0;
+	case MEMORY_DEVICE_GENERIC:
+		return 1;
+	default:
+		WARN_ONCE(1, "Unknown memory type!");
+		return 1;
+	}
+}
+
+static void __ref __zone_device_page_init(struct page *page, unsigned long pfn,
 					  unsigned long zone_idx, int nid,
 					  struct dev_pagemap *pgmap)
 {
-
 	__init_single_page(page, pfn, zone_idx, nid);
 
 	/*
@@ -1010,7 +1035,11 @@ static void __ref __init_zone_device_page(struct page *page, unsigned long pfn,
 	 */
 	page_folio(page)->pgmap = pgmap;
 	page->zone_device_data = NULL;
+}
 
+static void __ref zone_device_page_init_pageblock(struct page *page,
+						  unsigned long pfn)
+{
 	/*
 	 * Mark the block movable so that blocks are reserved for
 	 * movable at startup. This will force kernel allocations
@@ -1025,27 +1054,16 @@ static void __ref __init_zone_device_page(struct page *page, unsigned long pfn,
 		init_pageblock_migratetype(page, MIGRATE_MOVABLE, false);
 		cond_resched();
 	}
+}
 
-	/*
-	 * ZONE_DEVICE pages other than MEMORY_TYPE_GENERIC are released
-	 * directly to the driver page allocator which will set the page count
-	 * to 1 when allocating the page.
-	 *
-	 * MEMORY_TYPE_GENERIC and MEMORY_TYPE_FS_DAX pages automatically have
-	 * their refcount reset to one whenever they are freed (ie. after
-	 * their refcount drops to 0).
-	 */
-	switch (pgmap->type) {
-	case MEMORY_DEVICE_FS_DAX:
-	case MEMORY_DEVICE_PRIVATE:
-	case MEMORY_DEVICE_COHERENT:
-	case MEMORY_DEVICE_PCI_P2PDMA:
+static void __ref zone_device_page_init_slow(struct page *page,
+		unsigned long pfn, unsigned long zone_idx, int nid,
+		struct dev_pagemap *pgmap)
+{
+	__zone_device_page_init(page, pfn, zone_idx, nid, pgmap);
+	if (!zone_device_page_init_refcount(pgmap))
 		set_page_count(page, 0);
-		break;
-
-	case MEMORY_DEVICE_GENERIC:
-		break;
-	}
+	zone_device_page_init_pageblock(page, pfn);
 }
 
 /*
@@ -1084,7 +1102,7 @@ static void __ref memmap_init_compound(struct page *head,
 	for (pfn = head_pfn + 1; pfn < end_pfn; pfn++) {
 		struct page *page = pfn_to_page(pfn);
 
-		__init_zone_device_page(page, pfn, zone_idx, nid, pgmap);
+		zone_device_page_init_slow(page, pfn, zone_idx, nid, pgmap);
 		prep_compound_tail(page, head, order);
 		set_page_count(page, 0);
 	}
@@ -1120,7 +1138,7 @@ void __ref memmap_init_zone_device(struct zone *zone,
 	for (pfn = start_pfn; pfn < end_pfn; pfn += pfns_per_compound) {
 		struct page *page = pfn_to_page(pfn);
 
-		__init_zone_device_page(page, pfn, zone_idx, nid, pgmap);
+		zone_device_page_init_slow(page, pfn, zone_idx, nid, pgmap);
 
 		if (pfns_per_compound == 1)
 			continue;
-- 
2.20.1

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

* [PATCH v2 2/7] mm: add a set_page_section_from_pfn() helper
  2026-05-21  4:01 [PATCH v2 0/7] mm: speed up ZONE_DEVICE memmap initialization Li Zhe
  2026-05-21  4:01 ` [PATCH v2 1/7] mm: factor zone-device page init helpers out of __init_zone_device_page Li Zhe
@ 2026-05-21  4:01 ` Li Zhe
  2026-05-23 12:10   ` Mike Rapoport
  2026-05-21  4:01 ` [PATCH v2 3/7] mm: add a template-based fast path for zone-device page init Li Zhe
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 19+ messages in thread
From: Li Zhe @ 2026-05-21  4:01 UTC (permalink / raw)
  To: apopple, akpm, arnd, bp, dave.hansen, david, mingo, rppt, tglx, kees
  Cc: linux-arch, linux-kernel, linux-mm, lizhe.67, x86, linux-hardening

Callers that want to update section bits from a PFN currently need to
open-code:

	set_page_section(page, pfn_to_section_nr(pfn));

and guard that sequence with #ifdef SECTION_IN_PAGE_FLAGS.

Add set_page_section_from_pfn() to wrap that update in one place. When
section bits are stored in page flags, the helper derives the section
number from the PFN and updates the page flags. Otherwise it degrades to
a no-op.

Convert set_page_links() to use the new helper so later ZONE_DEVICE
fast-path patches can also update section bits without open-coding
SECTION_IN_PAGE_FLAGS at each callsite.

This keeps the PFN-to-section translation local to the configurations
that actually store section bits in struct page flags, and avoids
exposing that detail to generic callers.

No functional change intended.

Signed-off-by: Li Zhe <lizhe.67@bytedance.com>
---
 include/linux/mm.h | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index af23453e9dbd..bf84e698385c 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2507,11 +2507,26 @@ static inline void set_page_section(struct page *page, unsigned long section)
 	page->flags.f |= (section & SECTIONS_MASK) << SECTIONS_PGSHIFT;
 }
 
+static inline void set_page_section_from_pfn(struct page *page,
+					     unsigned long pfn)
+{
+	set_page_section(page, pfn_to_section_nr(pfn));
+}
+
 static inline unsigned long memdesc_section(memdesc_flags_t mdf)
 {
 	return (mdf.f >> SECTIONS_PGSHIFT) & SECTIONS_MASK;
 }
 #else /* !SECTION_IN_PAGE_FLAGS */
+static inline void set_page_section(struct page *page, unsigned long section)
+{
+}
+
+static inline void set_page_section_from_pfn(struct page *page,
+					     unsigned long pfn)
+{
+}
+
 static inline unsigned long memdesc_section(memdesc_flags_t mdf)
 {
 	return 0;
@@ -2734,9 +2749,7 @@ static inline void set_page_links(struct page *page, enum zone_type zone,
 {
 	set_page_zone(page, zone);
 	set_page_node(page, node);
-#ifdef SECTION_IN_PAGE_FLAGS
-	set_page_section(page, pfn_to_section_nr(pfn));
-#endif
+	set_page_section_from_pfn(page, pfn);
 }
 
 /**
-- 
2.20.1

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

* [PATCH v2 3/7] mm: add a template-based fast path for zone-device page init
  2026-05-21  4:01 [PATCH v2 0/7] mm: speed up ZONE_DEVICE memmap initialization Li Zhe
  2026-05-21  4:01 ` [PATCH v2 1/7] mm: factor zone-device page init helpers out of __init_zone_device_page Li Zhe
  2026-05-21  4:01 ` [PATCH v2 2/7] mm: add a set_page_section_from_pfn() helper Li Zhe
@ 2026-05-21  4:01 ` Li Zhe
  2026-05-23 12:10   ` Mike Rapoport
  2026-05-21  4:01 ` [PATCH v2 4/7] mm: extend the template fast path to zone-device compound tails Li Zhe
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 19+ messages in thread
From: Li Zhe @ 2026-05-21  4:01 UTC (permalink / raw)
  To: apopple, akpm, arnd, bp, dave.hansen, david, mingo, rppt, tglx, kees
  Cc: linux-arch, linux-kernel, linux-mm, lizhe.67, x86, linux-hardening

memmap_init_zone_device() repeats nearly identical head-page
initialization for each PFN. Prepare one reusable ZONE_DEVICE head-page
template through the existing slow path, copy it into each destination
page, and then fix up the PFN-dependent fields after the copy.

The optimized path assigns _refcount through the copied template, so
keep it disabled when the page_ref_set tracepoint is enabled. Also fall
back to the slow path if struct page is not an integral number of u64
words.

This patch accelerates the pfns_per_compound == 1 case. Compound tails
are handled in the next patch.

Tested in a VM with a 100 GB fsdax namespace device configured with
map=dev on Intel Ice Lake server. This test exercises the nd_pmem rebind
path (pfns_per_compound == 1).

Test procedure:
Rebind the nd_pmem driver 30 times and collect the memmap initialization
time from the pr_debug() output of memmap_init_zone_device().

Base(v7.1-rc3):
  First binding: 1486 ms
  Average of subsequent rebinds: 273.52 ms

With patches 1-3 applied:
  First binding: 1422 ms
  Average of subsequent rebinds: 245.73 ms

This reduces the average rebind time from 273.52 ms to 245.73 ms, or
about 10%.

Signed-off-by: Li Zhe <lizhe.67@bytedance.com>
---
 mm/mm_init.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 69 insertions(+), 1 deletion(-)

diff --git a/mm/mm_init.c b/mm/mm_init.c
index 4ba506df93bc..2992711351a0 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -1066,6 +1066,63 @@ static void __ref zone_device_page_init_slow(struct page *page,
 	zone_device_page_init_pageblock(page, pfn);
 }
 
+/*
+ * ZONE_DEVICE depends on MEMORY_HOTPLUG, and MEMORY_HOTPLUG is 64-bit
+ * only. That means CONFIG_ZONE_DEVICE cannot be enabled on 32-bit
+ * builds, so this fast-path code does not need a separate 32-bit
+ * fallback implementation.
+ */
+static inline bool zone_device_page_init_optimization_enabled(void)
+{
+	/*
+	 * The template fast path copies a preinitialized struct page as an
+	 * array of u64 words. Skip it when the page_ref_set tracepoint is
+	 * enabled, and fall back to the slow path if struct page is not an
+	 * integral number of u64 words.
+	 */
+	return !page_ref_tracepoint_active(page_ref_set) &&
+		IS_ALIGNED(sizeof(struct page), sizeof(u64));
+}
+
+static inline void zone_device_template_page_init(struct page *template,
+						  unsigned long pfn,
+						  unsigned long zone_idx,
+						  int nid,
+						  struct dev_pagemap *pgmap)
+{
+	__zone_device_page_init(template, pfn, zone_idx, nid, pgmap);
+	if (!zone_device_page_init_refcount(pgmap))
+		set_page_count(template, 0);
+}
+
+/*
+ * The copied template already provides the PFN-invariant portion of a
+ * ZONE_DEVICE struct page. Fix up the fields that still depend on @pfn
+ * after the copy, namely the section bits and page->virtual when present.
+ */
+static inline void zone_device_page_init_finish(struct page *page,
+							unsigned long pfn)
+{
+	set_page_section_from_pfn(page, pfn);
+#ifdef WANT_PAGE_VIRTUAL
+	if (!is_highmem_idx(ZONE_DEVICE))
+		set_page_address(page, __va(pfn << PAGE_SHIFT));
+#endif
+}
+
+static void zone_device_page_init_from_template(struct page *page,
+		unsigned long pfn, const struct page *template)
+{
+	const u64 *src = (const u64 *)template;
+	u64 *dst = (u64 *)page;
+	unsigned int i;
+
+	for (i = 0; i < sizeof(struct page) / sizeof(u64); i++)
+		dst[i] = src[i];
+	zone_device_page_init_finish(page, pfn);
+	zone_device_page_init_pageblock(page, pfn);
+}
+
 /*
  * With compound page geometry and when struct pages are stored in ram most
  * tail pages are reused. Consequently, the amount of unique struct pages to
@@ -1114,6 +1171,7 @@ void __ref memmap_init_zone_device(struct zone *zone,
 				   unsigned long nr_pages,
 				   struct dev_pagemap *pgmap)
 {
+	bool use_template = zone_device_page_init_optimization_enabled();
 	unsigned long pfn, end_pfn = start_pfn + nr_pages;
 	struct pglist_data *pgdat = zone->zone_pgdat;
 	struct vmem_altmap *altmap = pgmap_altmap(pgmap);
@@ -1121,6 +1179,7 @@ void __ref memmap_init_zone_device(struct zone *zone,
 	unsigned long zone_idx = zone_idx(zone);
 	unsigned long start = jiffies;
 	int nid = pgdat->node_id;
+	struct page template;
 
 	if (WARN_ON_ONCE(!pgmap || zone_idx != ZONE_DEVICE))
 		return;
@@ -1135,10 +1194,19 @@ void __ref memmap_init_zone_device(struct zone *zone,
 		nr_pages = end_pfn - start_pfn;
 	}
 
+	if (use_template)
+		zone_device_template_page_init(&template, start_pfn, zone_idx,
+					       nid, pgmap);
+
 	for (pfn = start_pfn; pfn < end_pfn; pfn += pfns_per_compound) {
 		struct page *page = pfn_to_page(pfn);
 
-		zone_device_page_init_slow(page, pfn, zone_idx, nid, pgmap);
+		if (use_template)
+			zone_device_page_init_from_template(page, pfn,
+							    &template);
+		else
+			zone_device_page_init_slow(page, pfn, zone_idx,
+						   nid, pgmap);
 
 		if (pfns_per_compound == 1)
 			continue;
-- 
2.20.1

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

* [PATCH v2 4/7] mm: extend the template fast path to zone-device compound tails
  2026-05-21  4:01 [PATCH v2 0/7] mm: speed up ZONE_DEVICE memmap initialization Li Zhe
                   ` (2 preceding siblings ...)
  2026-05-21  4:01 ` [PATCH v2 3/7] mm: add a template-based fast path for zone-device page init Li Zhe
@ 2026-05-21  4:01 ` Li Zhe
  2026-05-23 12:10   ` Mike Rapoport
  2026-05-21  4:01 ` [PATCH v2 5/7] string: introduce memcpy_streaming() helpers Li Zhe
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 19+ messages in thread
From: Li Zhe @ 2026-05-21  4:01 UTC (permalink / raw)
  To: apopple, akpm, arnd, bp, dave.hansen, david, mingo, rppt, tglx, kees
  Cc: linux-arch, linux-kernel, linux-mm, lizhe.67, x86, linux-hardening

The template fast path from the previous patch only accelerates head
pages. Compound tails in memmap_init_compound() still go through the
slow path one by one.

Build separate head and tail templates and reuse one prepared tail
template across the tail pages in a compound range. Head pages keep the
zone_device_page_init_refcount() policy, while compound tails always
start with a refcount of 0 after prep_compound_tail().

This extends the template-copy fast path to pfns_per_compound > 1
without changing the existing slow path.

Tested in a VM with a 100 GB devdax namespace (align=2097152) on Intel
Ice Lake server. This test exercises the dax_pmem rebind path and
measures memmap initialization latency.

Test procedure:
Unbind and rebind the dax_pmem driver 30 times, collect memmap
initialization time from the pr_debug() output of memmap_init_zone_device().

Base(v7.1-rc3):
  First binding: 1515 ms
  Average of subsequent rebinds: 313.45 ms

With patches 1-4 applied:
  First binding: 1422 ms
  Average of subsequent rebinds: 256.56 ms

This reduces the average rebind time from 313.45 ms to 256.56 ms, or
about 18.1%.

Signed-off-by: Li Zhe <lizhe.67@bytedance.com>
---
 mm/mm_init.c | 51 +++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 39 insertions(+), 12 deletions(-)

diff --git a/mm/mm_init.c b/mm/mm_init.c
index 2992711351a0..17a84d4cda01 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -1084,17 +1084,25 @@ static inline bool zone_device_page_init_optimization_enabled(void)
 		IS_ALIGNED(sizeof(struct page), sizeof(u64));
 }
 
-static inline void zone_device_template_page_init(struct page *template,
-						  unsigned long pfn,
-						  unsigned long zone_idx,
-						  int nid,
-						  struct dev_pagemap *pgmap)
+static inline void zone_device_template_head_page_init(struct page *template,
+		unsigned long pfn, unsigned long zone_idx, int nid,
+		struct dev_pagemap *pgmap)
 {
 	__zone_device_page_init(template, pfn, zone_idx, nid, pgmap);
 	if (!zone_device_page_init_refcount(pgmap))
 		set_page_count(template, 0);
 }
 
+static inline void zone_device_template_tail_page_init(struct page *template,
+		unsigned long pfn, unsigned long zone_idx, int nid,
+		struct dev_pagemap *pgmap, const struct page *head,
+		unsigned int order)
+{
+	__zone_device_page_init(template, pfn, zone_idx, nid, pgmap);
+	prep_compound_tail(template, head, order);
+	set_page_count(template, 0);
+}
+
 /*
  * The copied template already provides the PFN-invariant portion of a
  * ZONE_DEVICE struct page. Fix up the fields that still depend on @pfn
@@ -1144,10 +1152,12 @@ static void __ref memmap_init_compound(struct page *head,
 				       unsigned long head_pfn,
 				       unsigned long zone_idx, int nid,
 				       struct dev_pagemap *pgmap,
-				       unsigned long nr_pages)
+				       unsigned long nr_pages,
+				       bool use_template)
 {
 	unsigned long pfn, end_pfn = head_pfn + nr_pages;
 	unsigned int order = pgmap->vmemmap_shift;
+	struct page template;
 
 	/*
 	 * We have to initialize the pages, including setting up page links.
@@ -1156,12 +1166,28 @@ static void __ref memmap_init_compound(struct page *head,
 	 * the pages in the same go.
 	 */
 	__SetPageHead(head);
+
+	/*
+	 * A tail template can be reused for all tail pages in the same compound page
+	 * because shared state for compound tails is pre-set by prep_compound_tail().
+	 * The per-page page->virtual and section in flags are fixed up after copying.
+	 */
+	if (use_template)
+		zone_device_template_tail_page_init(&template, head_pfn + 1,
+						    zone_idx, nid, pgmap,
+						    head, order);
+
 	for (pfn = head_pfn + 1; pfn < end_pfn; pfn++) {
 		struct page *page = pfn_to_page(pfn);
 
-		zone_device_page_init_slow(page, pfn, zone_idx, nid, pgmap);
-		prep_compound_tail(page, head, order);
-		set_page_count(page, 0);
+		if (use_template) {
+			zone_device_page_init_from_template(page, pfn,
+							    &template);
+		} else {
+			zone_device_page_init_slow(page, pfn, zone_idx, nid, pgmap);
+			prep_compound_tail(page, head, order);
+			set_page_count(page, 0);
+		}
 	}
 	prep_compound_head(head, order);
 }
@@ -1195,8 +1221,8 @@ void __ref memmap_init_zone_device(struct zone *zone,
 	}
 
 	if (use_template)
-		zone_device_template_page_init(&template, start_pfn, zone_idx,
-					       nid, pgmap);
+		zone_device_template_head_page_init(&template, start_pfn,
+						    zone_idx, nid, pgmap);
 
 	for (pfn = start_pfn; pfn < end_pfn; pfn += pfns_per_compound) {
 		struct page *page = pfn_to_page(pfn);
@@ -1212,7 +1238,8 @@ void __ref memmap_init_zone_device(struct zone *zone,
 			continue;
 
 		memmap_init_compound(page, pfn, zone_idx, nid, pgmap,
-				     compound_nr_pages(altmap, pgmap));
+				     compound_nr_pages(altmap, pgmap),
+				     use_template);
 	}
 
 	pr_debug("%s initialised %lu pages in %ums\n", __func__,
-- 
2.20.1

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

* [PATCH v2 5/7] string: introduce memcpy_streaming() helpers
  2026-05-21  4:01 [PATCH v2 0/7] mm: speed up ZONE_DEVICE memmap initialization Li Zhe
                   ` (3 preceding siblings ...)
  2026-05-21  4:01 ` [PATCH v2 4/7] mm: extend the template fast path to zone-device compound tails Li Zhe
@ 2026-05-21  4:01 ` Li Zhe
  2026-05-21  4:01 ` [PATCH v2 6/7] x86/string: extend memcpy_flushcache() fixed-size fastpaths Li Zhe
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 19+ messages in thread
From: Li Zhe @ 2026-05-21  4:01 UTC (permalink / raw)
  To: apopple, akpm, arnd, bp, dave.hansen, david, mingo, rppt, tglx, kees
  Cc: linux-arch, linux-kernel, linux-mm, lizhe.67, x86, linux-hardening

Introduce a generic memcpy_streaming() interface for write-once copy
sites that can fall back to memcpy() when no architecture-specific
optimization is available.

Add memcpy_streaming_drain() alongside it so callers can separate the
copy primitive from any required ordering point. On x86, wire the helper
to memcpy_flushcache() and sfence so common code can request a streaming
copy without embedding x86-specific movnti details.

Callers are responsible for invoking memcpy_streaming_drain() before
later normal stores that must be ordered after the streaming copy.

Signed-off-by: Li Zhe <lizhe.67@bytedance.com>
---
 arch/x86/include/asm/string_64.h | 13 +++++++++++++
 include/linux/string.h           | 18 ++++++++++++++++++
 2 files changed, 31 insertions(+)

diff --git a/arch/x86/include/asm/string_64.h b/arch/x86/include/asm/string_64.h
index 4635616863f5..15504b844f1e 100644
--- a/arch/x86/include/asm/string_64.h
+++ b/arch/x86/include/asm/string_64.h
@@ -100,6 +100,19 @@ static __always_inline void memcpy_flushcache(void *dst, const void *src, size_t
 	}
 	__memcpy_flushcache(dst, src, cnt);
 }
+
+#define __HAVE_ARCH_MEMCPY_STREAMING 1
+static __always_inline void memcpy_streaming(void *dst, const void *src,
+					     size_t cnt)
+{
+	memcpy_flushcache(dst, src, cnt);
+}
+
+static __always_inline void memcpy_streaming_drain(void)
+{
+	asm volatile("sfence" : : : "memory");
+}
+
 #endif
 
 #endif /* __KERNEL__ */
diff --git a/include/linux/string.h b/include/linux/string.h
index b850bd91b3d8..ba029be9c187 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -281,6 +281,24 @@ static inline void memcpy_flushcache(void *dst, const void *src, size_t cnt)
 }
 #endif
 
+#ifndef __HAVE_ARCH_MEMCPY_STREAMING
+/*
+ * memcpy_streaming() is for write-once copy sites that may use
+ * non-temporal stores on some architectures. Callers must follow it
+ * with memcpy_streaming_drain() before later normal stores that need to
+ * be ordered after the streaming copy. Implementations that use regular
+ * cached stores can make the drain a no-op.
+ */
+static inline void memcpy_streaming(void *dst, const void *src, size_t cnt)
+{
+	memcpy(dst, src, cnt);
+}
+
+static inline void memcpy_streaming_drain(void)
+{
+}
+#endif
+
 void *memchr_inv(const void *s, int c, size_t n);
 char *strreplace(char *str, char old, char new);
 
-- 
2.20.1

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

* [PATCH v2 6/7] x86/string: extend memcpy_flushcache() fixed-size fastpaths
  2026-05-21  4:01 [PATCH v2 0/7] mm: speed up ZONE_DEVICE memmap initialization Li Zhe
                   ` (4 preceding siblings ...)
  2026-05-21  4:01 ` [PATCH v2 5/7] string: introduce memcpy_streaming() helpers Li Zhe
@ 2026-05-21  4:01 ` Li Zhe
  2026-05-21  4:01 ` [PATCH v2 7/7] mm: use memcpy_streaming() in zone-device template copies Li Zhe
  2026-05-21 22:20 ` [PATCH v2 0/7] mm: speed up ZONE_DEVICE memmap initialization Andrew Morton
  7 siblings, 0 replies; 19+ messages in thread
From: Li Zhe @ 2026-05-21  4:01 UTC (permalink / raw)
  To: apopple, akpm, arnd, bp, dave.hansen, david, mingo, rppt, tglx, kees
  Cc: linux-arch, linux-kernel, linux-mm, lizhe.67, x86, linux-hardening

Small constant-sized flushcache copies currently fall back to
__memcpy_flushcache() unless they are exactly 4, 8, or 16 bytes.

Factor the existing inline movnti sequences into small helpers and
extend the fixed-size fastpath coverage to 24..96 bytes. This keeps
common struct-page-sized copies on the inline path for the upcoming
memcpy_streaming() user, while still falling back to
__memcpy_flushcache() for uncommon sizes.

Signed-off-by: Li Zhe <lizhe.67@bytedance.com>
---
 arch/x86/include/asm/string_64.h | 87 +++++++++++++++++++++++++++-----
 1 file changed, 73 insertions(+), 14 deletions(-)

diff --git a/arch/x86/include/asm/string_64.h b/arch/x86/include/asm/string_64.h
index 15504b844f1e..94dc92f287f3 100644
--- a/arch/x86/include/asm/string_64.h
+++ b/arch/x86/include/asm/string_64.h
@@ -82,22 +82,81 @@ int strcmp(const char *cs, const char *ct);
 #ifdef CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE
 #define __HAVE_ARCH_MEMCPY_FLUSHCACHE 1
 void __memcpy_flushcache(void *dst, const void *src, size_t cnt);
-static __always_inline void memcpy_flushcache(void *dst, const void *src, size_t cnt)
+
+static __always_inline void memcpy_flushcache_4(void *dst, const void *src)
+{
+	asm ("movntil %1, %0" : "=m"(*(u32 *)dst) : "r"(*(u32 *)src));
+}
+
+static __always_inline void memcpy_flushcache_8(void *dst, const void *src)
+{
+	asm ("movntiq %1, %0" : "=m"(*(u64 *)dst) : "r"(*(u64 *)src));
+}
+
+static __always_inline void memcpy_flushcache_16(void *dst, const void *src)
+{
+	memcpy_flushcache_8(dst, src);
+	memcpy_flushcache_8(dst + 8, src + 8);
+}
+
+/*
+ * Keep common fixed-size copies on the inline movnti path instead of
+ * dropping into the generic helper.
+ */
+static __always_inline int memcpy_flushcache_small(void *dst, const void *src,
+						   size_t cnt)
 {
-	if (__builtin_constant_p(cnt)) {
-		switch (cnt) {
-			case 4:
-				asm ("movntil %1, %0" : "=m"(*(u32 *)dst) : "r"(*(u32 *)src));
-				return;
-			case 8:
-				asm ("movntiq %1, %0" : "=m"(*(u64 *)dst) : "r"(*(u64 *)src));
-				return;
-			case 16:
-				asm ("movntiq %1, %0" : "=m"(*(u64 *)dst) : "r"(*(u64 *)src));
-				asm ("movntiq %1, %0" : "=m"(*(u64 *)(dst + 8)) : "r"(*(u64 *)(src + 8)));
-				return;
-		}
+	switch (cnt) {
+	case 96:
+		memcpy_flushcache_16(dst + 80, src + 80);
+		fallthrough;
+	case 80:
+		memcpy_flushcache_16(dst + 64, src + 64);
+		fallthrough;
+	case 64:
+		memcpy_flushcache_16(dst + 48, src + 48);
+		fallthrough;
+	case 48:
+		memcpy_flushcache_16(dst + 32, src + 32);
+		fallthrough;
+	case 32:
+		memcpy_flushcache_16(dst + 16, src + 16);
+		fallthrough;
+	case 16:
+		memcpy_flushcache_16(dst, src);
+		return 1;
+
+	case 88:
+		memcpy_flushcache_16(dst + 72, src + 72);
+		fallthrough;
+	case 72:
+		memcpy_flushcache_16(dst + 56, src + 56);
+		fallthrough;
+	case 56:
+		memcpy_flushcache_16(dst + 40, src + 40);
+		fallthrough;
+	case 40:
+		memcpy_flushcache_16(dst + 24, src + 24);
+		fallthrough;
+	case 24:
+		memcpy_flushcache_16(dst + 8, src + 8);
+		fallthrough;
+	case 8:
+		memcpy_flushcache_8(dst, src);
+		return 1;
+
+	case 4:
+		memcpy_flushcache_4(dst, src);
+		return 1;
 	}
+
+	return 0;
+}
+
+static __always_inline void memcpy_flushcache(void *dst, const void *src, size_t cnt)
+{
+	if (__builtin_constant_p(cnt) && memcpy_flushcache_small(dst, src, cnt))
+		return;
 	__memcpy_flushcache(dst, src, cnt);
 }
 
-- 
2.20.1

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

* [PATCH v2 7/7] mm: use memcpy_streaming() in zone-device template copies
  2026-05-21  4:01 [PATCH v2 0/7] mm: speed up ZONE_DEVICE memmap initialization Li Zhe
                   ` (5 preceding siblings ...)
  2026-05-21  4:01 ` [PATCH v2 6/7] x86/string: extend memcpy_flushcache() fixed-size fastpaths Li Zhe
@ 2026-05-21  4:01 ` Li Zhe
  2026-05-23 12:10   ` Mike Rapoport
  2026-05-21 22:20 ` [PATCH v2 0/7] mm: speed up ZONE_DEVICE memmap initialization Andrew Morton
  7 siblings, 1 reply; 19+ messages in thread
From: Li Zhe @ 2026-05-21  4:01 UTC (permalink / raw)
  To: apopple, akpm, arnd, bp, dave.hansen, david, mingo, rppt, tglx, kees
  Cc: linux-arch, linux-kernel, linux-mm, lizhe.67, x86, linux-hardening

The template fast path still leaves the actual copy sequence up to the
compiler. Use the streaming-copy helpers introduced in the previous
patches for the ZONE_DEVICE template-copy path so common mm code can
request a write-once copy primitive without embedding arch-specific
store layout in the generic layer.

ZONE_DEVICE memmap initialization is a write-once path: each struct page
is populated once and is not expected to be reused from cache
immediately afterwards. A regular cached copy can therefore incur
write-allocate traffic and pollute the cache without much benefit.
Using memcpy_streaming() lets this path use an architecture-optimized
streaming copy where available, while still degrading to memcpy() on
architectures that do not provide a specialized implementation.

Update the PFN-dependent section bits and page->virtual state in the
reusable template before each copy instead of patching the destination
page afterwards. This keeps the hot path as a single streaming copy for
the common case and avoids post-copy normal stores to cachelines that
were just written through the streaming path. Keep pageblock-aligned PFNs
on memcpy() so pageblock initialization can immediately read back page
metadata without introducing a read-after-streaming dependency.

When the streaming backend uses non-temporal stores, order them before
entering memmap_init_compound(), before prep_compound_head() updates the
overlapping compound metadata, and before returning from
memmap_init_zone_device().

Keep sanitized builds on the slow path so KASAN/KMSAN retain their
instrumented stores.

Tested in a VM with a 100 GB fsdax namespace device configured with
map=dev and a 100 GB devdax namespace (align=2097152) on Intel Ice Lake
server.

Test procedure:
Rebind the nd_pmem and dax_pmem driver 30 times and collect the memmap
initialization time from the pr_debug() output of
memmap_init_zone_device().

Base(v7.1-rc3):
  First binding for nd_pmem driver: 1486 ms
  Average of subsequent rebinds: 273.52 ms

  First binding for dax_pmem driver: 1515 ms
  Average of subsequent rebinds: 313.45 ms

With this series:
  First binding for nd_pmem driver: 1389 ms
  Average of subsequent rebinds: 111.08 ms

  First binding for dax_pmem driver: 1294 ms
  Average of subsequent rebinds: 110.24 ms

This reduces the average rebind time by about 59.4% for nd_pmem and
64.8% for dax_pmem.

Signed-off-by: Li Zhe <lizhe.67@bytedance.com>
---
 mm/mm_init.c | 83 +++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 60 insertions(+), 23 deletions(-)

diff --git a/mm/mm_init.c b/mm/mm_init.c
index 17a84d4cda01..08feb24795b8 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -1075,13 +1075,15 @@ static void __ref zone_device_page_init_slow(struct page *page,
 static inline bool zone_device_page_init_optimization_enabled(void)
 {
 	/*
-	 * The template fast path copies a preinitialized struct page as an
-	 * array of u64 words. Skip it when the page_ref_set tracepoint is
-	 * enabled, and fall back to the slow path if struct page is not an
-	 * integral number of u64 words.
+	 * The template fast path copies a preinitialized struct page from
+	 * a reusable template. Keep sanitized builds on the slow path so
+	 * their instrumented stores remain intact, skip the fast path when
+	 * the page_ref_set tracepoint is enabled, and fall back if
+	 * struct page is not an integral number of u64 words.
 	 */
-	return !page_ref_tracepoint_active(page_ref_set) &&
-		IS_ALIGNED(sizeof(struct page), sizeof(u64));
+	return !IS_ENABLED(CONFIG_KASAN) && !IS_ENABLED(CONFIG_KMSAN) &&
+	       !page_ref_tracepoint_active(page_ref_set) &&
+	       IS_ALIGNED(sizeof(struct page), sizeof(u64));
 }
 
 static inline void zone_device_template_head_page_init(struct page *template,
@@ -1104,30 +1106,42 @@ static inline void zone_device_template_tail_page_init(struct page *template,
 }
 
 /*
- * The copied template already provides the PFN-invariant portion of a
- * ZONE_DEVICE struct page. Fix up the fields that still depend on @pfn
- * after the copy, namely the section bits and page->virtual when present.
+ * 'template' is a reusable page prototype rather than a strictly immutable
+ * object. Most ZONE_DEVICE fields stay constant across the pages covered by
+ * the current template, but section bits and page->virtual may still depend
+ * on the PFN. Refresh those PFN-dependent fields in the template before
+ * copying it into @page.
  */
-static inline void zone_device_page_init_finish(struct page *page,
-							unsigned long pfn)
+static inline void zone_device_page_update_template(struct page *template,
+						    unsigned long pfn)
 {
-	set_page_section_from_pfn(page, pfn);
+	set_page_section_from_pfn(template, pfn);
 #ifdef WANT_PAGE_VIRTUAL
 	if (!is_highmem_idx(ZONE_DEVICE))
-		set_page_address(page, __va(pfn << PAGE_SHIFT));
+		set_page_address(template, __va(pfn << PAGE_SHIFT));
 #endif
 }
 
 static void zone_device_page_init_from_template(struct page *page,
-		unsigned long pfn, const struct page *template)
+		unsigned long pfn, struct page *template)
 {
-	const u64 *src = (const u64 *)template;
-	u64 *dst = (u64 *)page;
-	unsigned int i;
+	/*
+	 * 'template' carries the invariant portion of a ZONE_DEVICE struct
+	 * page. Update the PFN-dependent fields in place before copying it
+	 * to the destination page.
+	 *
+	 * pageblock-aligned pages immediately feed
+	 * init_pageblock_migratetype(), which reads back page metadata via
+	 * helpers like page_zone(page). Avoid a read-after-streaming
+	 * dependency for these rare pages by using regular cached stores
+	 * instead of non-temporal ones.
+	 */
+	zone_device_page_update_template(template, pfn);
+	if (unlikely(pageblock_aligned(pfn)))
+		memcpy(page, template, sizeof(*page));
+	else
+		memcpy_streaming(page, template, sizeof(*page));
 
-	for (i = 0; i < sizeof(struct page) / sizeof(u64); i++)
-		dst[i] = src[i];
-	zone_device_page_init_finish(page, pfn);
 	zone_device_page_init_pageblock(page, pfn);
 }
 
@@ -1168,9 +1182,10 @@ static void __ref memmap_init_compound(struct page *head,
 	__SetPageHead(head);
 
 	/*
-	 * A tail template can be reused for all tail pages in the same compound page
-	 * because shared state for compound tails is pre-set by prep_compound_tail().
-	 * The per-page page->virtual and section in flags are fixed up after copying.
+	 * All tails of the same compound page share the state established by
+	 * prep_compound_tail(). Reuse one tail template for the whole range
+	 * and refresh only the PFN-dependent fields in that template before
+	 * each copy.
 	 */
 	if (use_template)
 		zone_device_template_tail_page_init(&template, head_pfn + 1,
@@ -1189,6 +1204,15 @@ static void __ref memmap_init_compound(struct page *head,
 			set_page_count(page, 0);
 		}
 	}
+
+	/*
+	 * prep_compound_head() updates compound metadata in struct folio fields
+	 * that alias the first tail-page descriptors. When the tail pages above
+	 * were populated with non-temporal stores, order those writes before the
+	 * overlapping metadata updates below.
+	 */
+	if (use_template)
+		memcpy_streaming_drain();
 	prep_compound_head(head, order);
 }
 
@@ -1237,10 +1261,23 @@ void __ref memmap_init_zone_device(struct zone *zone,
 		if (pfns_per_compound == 1)
 			continue;
 
+		/*
+		 * Compound-head setup immediately updates head->flags, so make
+		 * the streaming template copy visible before entering
+		 * memmap_init_compound().
+		 */
+		if (use_template)
+			memcpy_streaming_drain();
+
 		memmap_init_compound(page, pfn, zone_idx, nid, pgmap,
 				     compound_nr_pages(altmap, pgmap),
 				     use_template);
 	}
+	/*
+	 * Drain any remaining non-temporal stores before returning.
+	 */
+	if (use_template)
+		memcpy_streaming_drain();
 
 	pr_debug("%s initialised %lu pages in %ums\n", __func__,
 		nr_pages, jiffies_to_msecs(jiffies - start));
-- 
2.20.1

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

* Re: [PATCH v2 0/7] mm: speed up ZONE_DEVICE memmap initialization
  2026-05-21  4:01 [PATCH v2 0/7] mm: speed up ZONE_DEVICE memmap initialization Li Zhe
                   ` (6 preceding siblings ...)
  2026-05-21  4:01 ` [PATCH v2 7/7] mm: use memcpy_streaming() in zone-device template copies Li Zhe
@ 2026-05-21 22:20 ` Andrew Morton
  2026-05-22  7:49   ` Li Zhe
  7 siblings, 1 reply; 19+ messages in thread
From: Andrew Morton @ 2026-05-21 22:20 UTC (permalink / raw)
  To: Li Zhe
  Cc: apopple, arnd, bp, dave.hansen, david, mingo, rppt, tglx, kees,
	linux-arch, linux-kernel, linux-mm, x86, linux-hardening

On Thu, 21 May 2026 12:01:17 +0800 "Li Zhe" <lizhe.67@bytedance.com> wrote:

> memmap_init_zone_device() can spend a substantial amount of time
> initializing large ZONE_DEVICE ranges because it repeats nearly
> identical struct page setup for every PFN.
> 
> This series reduces that overhead in seven steps.

Cool, thanks, we all love speedups.

> The first patch factors the reusable pieces out of
> __init_zone_device_page() so later patches can share the same logic
> without changing the existing slow path.
> 
> The second patch adds set_page_section_from_pfn(), so generic callers
> can update section bits from a PFN without open-coding
> SECTION_IN_PAGE_FLAGS checks.
> 
> The third patch adds a template-based fast path for ZONE_DEVICE head
> pages. Instead of rebuilding the same struct page state for every PFN,
> it prepares a reusable page template once and copies it to each
> destination page.
> 
> The fourth patch extends the same template-based approach to compound
> tails, so pfns_per_compound > 1 can also benefit from the fast path.
> 
> The fifth patch introduces memcpy_streaming() and
> memcpy_streaming_drain() as a generic interface for write-once
> streaming copies, with a memcpy() fallback for architectures that do
> not provide a specialized backend.
> 
> The sixth patch extends x86 memcpy_flushcache() small fixed-size
> fastpaths so struct-page-sized streaming copies can stay on the inline
> path.
> 
> The last patch switches the zone-device template-copy path over to
> memcpy_streaming(). It refreshes PFN-dependent fields in the reusable
> template before each copy, keeps pageblock-aligned PFNs on regular
> memcpy(), and drains streaming stores before later normal stores update
> overlapping or dependent metadata.
> 
> The optimized path is disabled when the page_ref_set tracepoint is
> enabled, sanitized builds remain on the slow path so their
> instrumented stores are preserved, and the fast path falls back to the
> existing slow path if sizeof(struct page) is not an integral number of
> u64 words.
> 
> Testing
> =======
> 
> Tests were run in a VM on an Intel Ice Lake server.
> 
> Two PMEM configurations were used:
>   - a 100 GB fsdax namespace configured with map=dev, which exercises
>     the nd_pmem rebind path (pfns_per_compound == 1)
>   - a 100 GB devdax namespace configured with align=2097152, which
>     exercises the dax_pmem rebind path (pfns_per_compound > 1)
> 
> For each configuration, the corresponding driver was unbound and
> rebound 30 times. Memmap initialization latency was collected from the
> pr_debug() output of memmap_init_zone_device().
> 
> The first bind is reported separately, and the average of subsequent
> rebinds is used as the steady-state result.

How closely does this workload resemble any real-world user workload?

> Performance
> ===========
> 
> nd_pmem rebind, 100 GB fsdax namespace, map=dev
>   Base(v7.1-rc3):
>     First binding: 1486 ms
>     Average of subsequent rebinds: 273.52 ms
>   With patches 1-3 applied:
>     First binding: 1422 ms
>     Average of subsequent rebinds: 245.73 ms
>   Full series:
>     First binding: 1389 ms
>     Average of subsequent rebinds: 111.08 ms
> 
> dax_pmem rebind, 100 GB devdax namespace, align=2097152
>   Base(v7.1-rc3):
>     First binding: 1515 ms
>     Average of subsequent rebinds: 313.45 ms
>   With patches 1-4 applied:
>     First binding: 1422 ms
>     Average of subsequent rebinds: 256.56 ms
>   Full series:
>     First binding: 1294 ms
>     Average of subsequent rebinds: 110.24 ms

The improvements appear to range between "modest" and "large", but what
I'd like to understand is how frequently real-world users are using
these operations in real-world workloads.

IOW, (and this is always the bottom line), how valuable is this
patchset to our users?

>   mm: factor zone-device page init helpers out of
>     __init_zone_device_page
>   mm: add a set_page_section_from_pfn() helper
>   mm: add a template-based fast path for zone-device page init
>   mm: extend the template fast path to zone-device compound tails
>   string: introduce memcpy_streaming() helpers
>   x86/string: extend memcpy_flushcache() fixed-size fastpaths
>   mm: use memcpy_streaming() in zone-device template copies
> 
>  arch/x86/include/asm/string_64.h | 100 +++++++++++++---
>  include/linux/mm.h               |  19 ++-
>  include/linux/string.h           |  18 +++
>  mm/mm_init.c                     | 198 +++++++++++++++++++++++++++----
>  4 files changed, 294 insertions(+), 41 deletions(-)

I won't take any action at this stage - let's await reviewer input.  If
none is forthcoming then please remind me and I'll figure out what to
do.

The ever-present reviewer called "Sashiko" has thoughts to offer:

	https://sashiko.dev/#/patchset/20260521040124.10608-1-lizhe.67@bytedance.com

Please take a look, decide if there's useful material in there.


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

* Re: [PATCH v2 0/7] mm: speed up ZONE_DEVICE memmap initialization
  2026-05-21 22:20 ` [PATCH v2 0/7] mm: speed up ZONE_DEVICE memmap initialization Andrew Morton
@ 2026-05-22  7:49   ` Li Zhe
  0 siblings, 0 replies; 19+ messages in thread
From: Li Zhe @ 2026-05-22  7:49 UTC (permalink / raw)
  To: akpm
  Cc: apopple, arnd, bp, dave.hansen, david, kees, linux-arch,
	linux-hardening, linux-kernel, linux-mm, lizhe.67, mingo, rppt,
	tglx, x86

On Thu, 21 May 2026 15:20:29 -0700, akpm@linux-foundation.org wrote:

> On Thu, 21 May 2026 12:01:17 +0800 "Li Zhe" <lizhe.67@bytedance.com> wrote:
> 
> > memmap_init_zone_device() can spend a substantial amount of time
> > initializing large ZONE_DEVICE ranges because it repeats nearly
> > identical struct page setup for every PFN.
> >
> > This series reduces that overhead in seven steps.
> 
> Cool, thanks, we all love speedups.
> 
> > The first patch factors the reusable pieces out of
> > __init_zone_device_page() so later patches can share the same logic
> > without changing the existing slow path.
> >
> > The second patch adds set_page_section_from_pfn(), so generic callers
> > can update section bits from a PFN without open-coding
> > SECTION_IN_PAGE_FLAGS checks.
> >
> > The third patch adds a template-based fast path for ZONE_DEVICE head
> > pages. Instead of rebuilding the same struct page state for every PFN,
> > it prepares a reusable page template once and copies it to each
> > destination page.
> >
> > The fourth patch extends the same template-based approach to compound
> > tails, so pfns_per_compound > 1 can also benefit from the fast path.
> >
> > The fifth patch introduces memcpy_streaming() and
> > memcpy_streaming_drain() as a generic interface for write-once
> > streaming copies, with a memcpy() fallback for architectures that do
> > not provide a specialized backend.
> >
> > The sixth patch extends x86 memcpy_flushcache() small fixed-size
> > fastpaths so struct-page-sized streaming copies can stay on the inline
> > path.
> >
> > The last patch switches the zone-device template-copy path over to
> > memcpy_streaming(). It refreshes PFN-dependent fields in the reusable
> > template before each copy, keeps pageblock-aligned PFNs on regular
> > memcpy(), and drains streaming stores before later normal stores update
> > overlapping or dependent metadata.
> >
> > The optimized path is disabled when the page_ref_set tracepoint is
> > enabled, sanitized builds remain on the slow path so their
> > instrumented stores are preserved, and the fast path falls back to the
> > existing slow path if sizeof(struct page) is not an integral number of
> > u64 words.
> >
> > Testing
> > =======
> >
> > Tests were run in a VM on an Intel Ice Lake server.
> >
> > Two PMEM configurations were used:
> >   - a 100 GB fsdax namespace configured with map=dev, which exercises
> >     the nd_pmem rebind path (pfns_per_compound == 1)
> >   - a 100 GB devdax namespace configured with align=2097152, which
> >     exercises the dax_pmem rebind path (pfns_per_compound > 1)
> >
> > For each configuration, the corresponding driver was unbound and
> > rebound 30 times. Memmap initialization latency was collected from the
> > pr_debug() output of memmap_init_zone_device().
> >
> > The first bind is reported separately, and the average of subsequent
> > rebinds is used as the steady-state result.
> 
> How closely does this workload resemble any real-world user workload?

Not directly. The unbind/rebind loop is mainly a controlled and
repeatable way to measure the memmap_init_zone_device() path with minimal
unrelated noise.

> > Performance
> > ===========
> >
> > nd_pmem rebind, 100 GB fsdax namespace, map=dev
> >   Base(v7.1-rc3):
> >     First binding: 1486 ms
> >     Average of subsequent rebinds: 273.52 ms
> >   With patches 1-3 applied:
> >     First binding: 1422 ms
> >     Average of subsequent rebinds: 245.73 ms
> >   Full series:
> >     First binding: 1389 ms
> >     Average of subsequent rebinds: 111.08 ms
> >
> > dax_pmem rebind, 100 GB devdax namespace, align=2097152
> >   Base(v7.1-rc3):
> >     First binding: 1515 ms
> >     Average of subsequent rebinds: 313.45 ms
> >   With patches 1-4 applied:
> >     First binding: 1422 ms
> >     Average of subsequent rebinds: 256.56 ms
> >   Full series:
> >     First binding: 1294 ms
> >     Average of subsequent rebinds: 110.24 ms
> 
> The improvements appear to range between "modest" and "large", but what
> I'd like to understand is how frequently real-world users are using
> these operations in real-world workloads.
> 
> IOW, (and this is always the bottom line), how valuable is this
> patchset to our users?

This is not a steady-state data-path optimization. Its value is in pmem
bring-up paths, and in our deployment we do have scenarios where
multiple pmem devices are hotplugged , so reducing this latency is useful
in practice for us.

> >   mm: factor zone-device page init helpers out of
> >     __init_zone_device_page
> >   mm: add a set_page_section_from_pfn() helper
> >   mm: add a template-based fast path for zone-device page init
> >   mm: extend the template fast path to zone-device compound tails
> >   string: introduce memcpy_streaming() helpers
> >   x86/string: extend memcpy_flushcache() fixed-size fastpaths
> >   mm: use memcpy_streaming() in zone-device template copies
> >
> >  arch/x86/include/asm/string_64.h | 100 +++++++++++++---
> >  include/linux/mm.h               |  19 ++-
> >  include/linux/string.h           |  18 +++
> >  mm/mm_init.c                     | 198 +++++++++++++++++++++++++++----
> >  4 files changed, 294 insertions(+), 41 deletions(-)
> 
> I won't take any action at this stage - let's await reviewer input.  If
> none is forthcoming then please remind me and I'll figure out what to
> do.
> 
> The ever-present reviewer called "Sashiko" has thoughts to offer:
> 
> 	https://sashiko.dev/#/patchset/20260521040124.10608-1-lizhe.67@bytedance.com
> 
> Please take a look, decide if there's useful material in there.

There is useful material there, mainly around patches 5 and 6.

The memcpy_streaming() x86 backend should be narrower, and the expanded
memcpy_flushcache() small-copy fastpath should keep naturally aligned
cases only and preserve forward movnti store order.

I'll address those points in the next revision and rerun the benchmarks.

Thanks,
Zhe

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

* Re: [PATCH v2 1/7] mm: factor zone-device page init helpers out of __init_zone_device_page
  2026-05-21  4:01 ` [PATCH v2 1/7] mm: factor zone-device page init helpers out of __init_zone_device_page Li Zhe
@ 2026-05-23 12:10   ` Mike Rapoport
  2026-05-25  3:51     ` Li Zhe
  0 siblings, 1 reply; 19+ messages in thread
From: Mike Rapoport @ 2026-05-23 12:10 UTC (permalink / raw)
  To: Li Zhe
  Cc: apopple, akpm, arnd, bp, dave.hansen, david, mingo, rppt, tglx,
	kees, linux-arch, linux-kernel, linux-mm, x86, linux-hardening

On Thu, 21 May 2026 12:01:18 +0800, Li Zhe <lizhe.67@bytedance.com> wrote:
> diff --git a/mm/mm_init.c b/mm/mm_init.c
> index f9f8e1af921c..4ba506df93bc 100644
> --- a/mm/mm_init.c
> +++ b/mm/mm_init.c
> @@ -987,11 +987,36 @@ static void __init memmap_init(void)
>  }
>  
>  #ifdef CONFIG_ZONE_DEVICE
> -static void __ref __init_zone_device_page(struct page *page, unsigned long pfn,
> +static inline int zone_device_page_init_refcount(
> +		const struct dev_pagemap *pgmap)

The name zone_device_page_init_refcount() implies that this function
actually intializes the refcount, but it's actually used as a predicate
based on pagemap properties, so let's make it bool and name accordingly. 
How about

static inline bool pagemap_resets_refcount(const struct dev_pagemap *pagemap)

Other than that:

Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>

-- 
Sincerely yours,
Mike.


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

* Re: [PATCH v2 2/7] mm: add a set_page_section_from_pfn() helper
  2026-05-21  4:01 ` [PATCH v2 2/7] mm: add a set_page_section_from_pfn() helper Li Zhe
@ 2026-05-23 12:10   ` Mike Rapoport
  0 siblings, 0 replies; 19+ messages in thread
From: Mike Rapoport @ 2026-05-23 12:10 UTC (permalink / raw)
  To: Li Zhe
  Cc: apopple, akpm, arnd, bp, dave.hansen, david, mingo, rppt, tglx,
	kees, linux-arch, linux-kernel, linux-mm, x86, linux-hardening

On Thu, 21 May 2026 12:01:19 +0800, Li Zhe <lizhe.67@bytedance.com> wrote:
> Callers that want to update section bits from a PFN currently need to
> open-code:
> 
> 	set_page_section(page, pfn_to_section_nr(pfn));
> 
> and guard that sequence with #ifdef SECTION_IN_PAGE_FLAGS.
> 
> [...]

Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>

-- 
Sincerely yours,
Mike.


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

* Re: [PATCH v2 3/7] mm: add a template-based fast path for zone-device page init
  2026-05-21  4:01 ` [PATCH v2 3/7] mm: add a template-based fast path for zone-device page init Li Zhe
@ 2026-05-23 12:10   ` Mike Rapoport
  2026-05-25  4:02     ` Li Zhe
  0 siblings, 1 reply; 19+ messages in thread
From: Mike Rapoport @ 2026-05-23 12:10 UTC (permalink / raw)
  To: Li Zhe
  Cc: apopple, akpm, arnd, bp, dave.hansen, david, mingo, rppt, tglx,
	kees, linux-arch, linux-kernel, linux-mm, x86, linux-hardening

On Thu, 21 May 2026 12:01:20 +0800, Li Zhe <lizhe.67@bytedance.com> wrote:
> diff --git a/mm/mm_init.c b/mm/mm_init.c
> index 4ba506df93bc..2992711351a0 100644
> --- a/mm/mm_init.c
> +++ b/mm/mm_init.c
> @@ -1066,6 +1066,63 @@ static void __ref zone_device_page_init_slow(struct page *page,
> [ ... skip 33 lines ... ]
> + * The copied template already provides the PFN-invariant portion of a
> + * ZONE_DEVICE struct page. Fix up the fields that still depend on @pfn
> + * after the copy, namely the section bits and page->virtual when present.
> + */
> +static inline void zone_device_page_init_finish(struct page *page,
> +							unsigned long pfn)

Please align the indentation for the second parameters line like int
zone_device_template_page_init() or use two tabs for indentation
everywhere.

With that fixed:

-- 
Sincerely yours,
Mike.


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

* Re: [PATCH v2 4/7] mm: extend the template fast path to zone-device compound tails
  2026-05-21  4:01 ` [PATCH v2 4/7] mm: extend the template fast path to zone-device compound tails Li Zhe
@ 2026-05-23 12:10   ` Mike Rapoport
  2026-05-25  4:11     ` Li Zhe
  0 siblings, 1 reply; 19+ messages in thread
From: Mike Rapoport @ 2026-05-23 12:10 UTC (permalink / raw)
  To: Li Zhe
  Cc: apopple, akpm, arnd, bp, dave.hansen, david, mingo, rppt, tglx,
	kees, linux-arch, linux-kernel, linux-mm, x86, linux-hardening

On Thu, 21 May 2026 12:01:21 +0800, Li Zhe <lizhe.67@bytedance.com> wrote:
> diff --git a/mm/mm_init.c b/mm/mm_init.c
> index 2992711351a0..17a84d4cda01 100644
> --- a/mm/mm_init.c
> +++ b/mm/mm_init.c
> @@ -1084,17 +1084,25 @@ static inline bool zone_device_page_init_optimization_enabled(void)
>  		IS_ALIGNED(sizeof(struct page), sizeof(u64));
>  }
>  
> -static inline void zone_device_template_page_init(struct page *template,
> -						  unsigned long pfn,
> -						  unsigned long zone_idx,
> -						  int nid,
> -						  struct dev_pagemap *pgmap)
> +static inline void zone_device_template_head_page_init(struct page *template,
> +		unsigned long pfn, unsigned long zone_idx, int nid,
> +		struct dev_pagemap *pgmap)

Yeah, two tabs from the start would be better :)

> @@ -1156,12 +1166,28 @@ static void __ref memmap_init_compound(struct page *head,
> [ ... skip 17 lines ... ]
> -		zone_device_page_init_slow(page, pfn, zone_idx, nid, pgmap);
> -		prep_compound_tail(page, head, order);
> -		set_page_count(page, 0);
> +		if (use_template) {
> +			zone_device_page_init_from_template(page, pfn,
> +							    &template);

continue here and there won't be need to indent the slow path.

Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>

-- 
Sincerely yours,
Mike.


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

* Re: [PATCH v2 7/7] mm: use memcpy_streaming() in zone-device template copies
  2026-05-21  4:01 ` [PATCH v2 7/7] mm: use memcpy_streaming() in zone-device template copies Li Zhe
@ 2026-05-23 12:10   ` Mike Rapoport
  2026-05-25  4:59     ` Li Zhe
  0 siblings, 1 reply; 19+ messages in thread
From: Mike Rapoport @ 2026-05-23 12:10 UTC (permalink / raw)
  To: Li Zhe
  Cc: apopple, akpm, arnd, bp, dave.hansen, david, mingo, rppt, tglx,
	kees, linux-arch, linux-kernel, linux-mm, x86, linux-hardening

On Thu, 21 May 2026 12:01:24 +0800, Li Zhe <lizhe.67@bytedance.com> wrote:
> diff --git a/mm/mm_init.c b/mm/mm_init.c
> index 17a84d4cda01..08feb24795b8 100644
> --- a/mm/mm_init.c
> +++ b/mm/mm_init.c
> @@ -1075,13 +1075,15 @@ static void __ref zone_device_page_init_slow(struct page *page,
> [ ... skip 12 lines ... ]
>  	 */
> -	return !page_ref_tracepoint_active(page_ref_set) &&
> -		IS_ALIGNED(sizeof(struct page), sizeof(u64));
> +	return !IS_ENABLED(CONFIG_KASAN) && !IS_ENABLED(CONFIG_KMSAN) &&
> +	       !page_ref_tracepoint_active(page_ref_set) &&
> +	       IS_ALIGNED(sizeof(struct page), sizeof(u64));

Oh, can we rewrite it as a sequence of

	if (cond)
		return false;
	...

	return true;

I believe the compilers would generate the same code.

> @@ -1104,30 +1106,42 @@ static inline void zone_device_template_tail_page_init(struct page *template,
> [ ... skip 10 lines ... ]
> + * copying it into @page.
>   */
> -static inline void zone_device_page_init_finish(struct page *page,
> -							unsigned long pfn)
> +static inline void zone_device_page_update_template(struct page *template,
> +						    unsigned long pfn)

Two tabs for indentation here from the first patch this function
appears please.

Why do we need the intermediate step of _init_finish() if we later
change it to _update_template()?

> [ ... skip 30 lines ... ]
> +		memcpy(page, template, sizeof(*page));
> +	else
> +		memcpy_streaming(page, template, sizeof(*page));
>  
> -	for (i = 0; i < sizeof(struct page) / sizeof(u64); i++)
> -		dst[i] = src[i];

If you replace this with memcpy, what's the point for doing the loop in
intermediate patches?

-- 
Sincerely yours,
Mike.


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

* Re: [PATCH v2 1/7] mm: factor zone-device page init helpers out of __init_zone_device_page
  2026-05-23 12:10   ` Mike Rapoport
@ 2026-05-25  3:51     ` Li Zhe
  0 siblings, 0 replies; 19+ messages in thread
From: Li Zhe @ 2026-05-25  3:51 UTC (permalink / raw)
  To: rppt
  Cc: akpm, apopple, arnd, bp, dave.hansen, david, kees, linux-arch,
	linux-hardening, linux-kernel, linux-mm, lizhe.67, mingo, tglx,
	x86

On Sat, 23 May 2026 15:10:52 +0300, rppt@kernel.org wrote:

> On Thu, 21 May 2026 12:01:18 +0800, Li Zhe <lizhe.67@bytedance.com> wrote:
> > diff --git a/mm/mm_init.c b/mm/mm_init.c
> > index f9f8e1af921c..4ba506df93bc 100644
> > --- a/mm/mm_init.c
> > +++ b/mm/mm_init.c
> > @@ -987,11 +987,36 @@ static void __init memmap_init(void)
> >  }
> >
> >  #ifdef CONFIG_ZONE_DEVICE
> > -static void __ref __init_zone_device_page(struct page *page, unsigned long pfn,
> > +static inline int zone_device_page_init_refcount(
> > +		const struct dev_pagemap *pgmap)
> 
> The name zone_device_page_init_refcount() implies that this function
> actually intializes the refcount, but it's actually used as a predicate
> based on pagemap properties, so let's make it bool and name accordingly.
> How about
> 
> static inline bool pagemap_resets_refcount(const struct dev_pagemap *pagemap)
> 
> Other than that:
> 
> Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>

Thanks, I will fix this in the next revision.

Thanks,
Zhe

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

* Re: [PATCH v2 3/7] mm: add a template-based fast path for zone-device page init
  2026-05-23 12:10   ` Mike Rapoport
@ 2026-05-25  4:02     ` Li Zhe
  0 siblings, 0 replies; 19+ messages in thread
From: Li Zhe @ 2026-05-25  4:02 UTC (permalink / raw)
  To: rppt
  Cc: akpm, apopple, arnd, bp, dave.hansen, david, kees, linux-arch,
	linux-hardening, linux-kernel, linux-mm, lizhe.67, mingo, tglx,
	x86

On Sat, 23 May 2026 15:10:52 +0300, rppt@kernel.org wrote:

> On Thu, 21 May 2026 12:01:20 +0800, Li Zhe <lizhe.67@bytedance.com> wrote:
> > diff --git a/mm/mm_init.c b/mm/mm_init.c
> > index 4ba506df93bc..2992711351a0 100644
> > --- a/mm/mm_init.c
> > +++ b/mm/mm_init.c
> > @@ -1066,6 +1066,63 @@ static void __ref zone_device_page_init_slow(struct page *page,
> > [ ... skip 33 lines ... ]
> > + * The copied template already provides the PFN-invariant portion of a
> > + * ZONE_DEVICE struct page. Fix up the fields that still depend on @pfn
> > + * after the copy, namely the section bits and page->virtual when present.
> > + */
> > +static inline void zone_device_page_init_finish(struct page *page,
> > +							unsigned long pfn)
> 
> Please align the indentation for the second parameters line like int
> zone_device_template_page_init() or use two tabs for indentation
> everywhere.

Thanks for the review. I will adjust the indentation to use two tabs
for the second parameter line in the next revision.

Thanks,
Zhe

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

* Re: [PATCH v2 4/7] mm: extend the template fast path to zone-device compound tails
  2026-05-23 12:10   ` Mike Rapoport
@ 2026-05-25  4:11     ` Li Zhe
  0 siblings, 0 replies; 19+ messages in thread
From: Li Zhe @ 2026-05-25  4:11 UTC (permalink / raw)
  To: rppt
  Cc: akpm, apopple, arnd, bp, dave.hansen, david, kees, linux-arch,
	linux-hardening, linux-kernel, linux-mm, lizhe.67, mingo, tglx,
	x86

On Sat, 23 May 2026 15:10:52 +0300, rppt@kernel.org wrote:

> On Thu, 21 May 2026 12:01:21 +0800, Li Zhe <lizhe.67@bytedance.com> wrote:
> > diff --git a/mm/mm_init.c b/mm/mm_init.c
> > index 2992711351a0..17a84d4cda01 100644
> > --- a/mm/mm_init.c
> > +++ b/mm/mm_init.c
> > @@ -1084,17 +1084,25 @@ static inline bool zone_device_page_init_optimization_enabled(void)
> >  		IS_ALIGNED(sizeof(struct page), sizeof(u64));
> >  }
> >
> > -static inline void zone_device_template_page_init(struct page *template,
> > -						  unsigned long pfn,
> > -						  unsigned long zone_idx,
> > -						  int nid,
> > -						  struct dev_pagemap *pgmap)
> > +static inline void zone_device_template_head_page_init(struct page *template,
> > +		unsigned long pfn, unsigned long zone_idx, int nid,
> > +		struct dev_pagemap *pgmap)
> 
> Yeah, two tabs from the start would be better :)
> 
> > @@ -1156,12 +1166,28 @@ static void __ref memmap_init_compound(struct page *head,
> > [ ... skip 17 lines ... ]
> > -		zone_device_page_init_slow(page, pfn, zone_idx, nid, pgmap);
> > -		prep_compound_tail(page, head, order);
> > -		set_page_count(page, 0);
> > +		if (use_template) {
> > +			zone_device_page_init_from_template(page, pfn,
> > +							    &template);
> 
> continue here and there won't be need to indent the slow path.
> 
> Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>

Thanks for the review. I will fix this in the next revision.

Thanks,
Zhe

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

* Re: [PATCH v2 7/7] mm: use memcpy_streaming() in zone-device template copies
  2026-05-23 12:10   ` Mike Rapoport
@ 2026-05-25  4:59     ` Li Zhe
  0 siblings, 0 replies; 19+ messages in thread
From: Li Zhe @ 2026-05-25  4:59 UTC (permalink / raw)
  To: rppt
  Cc: akpm, apopple, arnd, bp, dave.hansen, david, kees, linux-arch,
	linux-hardening, linux-kernel, linux-mm, lizhe.67, mingo, tglx,
	x86

On Sat, 23 May 2026 15:10:52 +0300, rppt@kernel.org wrote:

> On Thu, 21 May 2026 12:01:24 +0800, Li Zhe <lizhe.67@bytedance.com> wrote:
> > diff --git a/mm/mm_init.c b/mm/mm_init.c
> > index 17a84d4cda01..08feb24795b8 100644
> > --- a/mm/mm_init.c
> > +++ b/mm/mm_init.c
> > @@ -1075,13 +1075,15 @@ static void __ref zone_device_page_init_slow(struct page *page,
> > [ ... skip 12 lines ... ]
> >  	 */
> > -	return !page_ref_tracepoint_active(page_ref_set) &&
> > -		IS_ALIGNED(sizeof(struct page), sizeof(u64));
> > +	return !IS_ENABLED(CONFIG_KASAN) && !IS_ENABLED(CONFIG_KMSAN) &&
> > +	       !page_ref_tracepoint_active(page_ref_set) &&
> > +	       IS_ALIGNED(sizeof(struct page), sizeof(u64));
> 
> Oh, can we rewrite it as a sequence of
> 
> 	if (cond)
> 		return false;
> 	...
> 
> 	return true;
> 
> I believe the compilers would generate the same code.

Thanks for the review. I will fix this in the next revision.

> > @@ -1104,30 +1106,42 @@ static inline void zone_device_template_tail_page_init(struct page *template,
> > [ ... skip 10 lines ... ]
> > + * copying it into @page.
> >   */
> > -static inline void zone_device_page_init_finish(struct page *page,
> > -							unsigned long pfn)
> > +static inline void zone_device_page_update_template(struct page *template,
> > +						    unsigned long pfn)
> 
> Two tabs for indentation here from the first patch this function
> appears please.

Thanks for the review. I will fix this in the next revision.

> Why do we need the intermediate step of _init_finish() if we later
> change it to _update_template()?

Instead of introducing an intermediate _init_finish() and renaming it
later, I will simply use zone_device_page_update_template() directly
from the beginning in the patch series.

> > [ ... skip 30 lines ... ]
> > +		memcpy(page, template, sizeof(*page));
> > +	else
> > +		memcpy_streaming(page, template, sizeof(*page));
> >
> > -	for (i = 0; i < sizeof(struct page) / sizeof(u64); i++)
> > -		dst[i] = src[i];
> 
> If you replace this with memcpy, what's the point for doing the loop in
> intermediate patches?

Thanks for the review. I will fix this in the next revision.

Thanks,
Zhe

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

end of thread, other threads:[~2026-05-25  4:59 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-21  4:01 [PATCH v2 0/7] mm: speed up ZONE_DEVICE memmap initialization Li Zhe
2026-05-21  4:01 ` [PATCH v2 1/7] mm: factor zone-device page init helpers out of __init_zone_device_page Li Zhe
2026-05-23 12:10   ` Mike Rapoport
2026-05-25  3:51     ` Li Zhe
2026-05-21  4:01 ` [PATCH v2 2/7] mm: add a set_page_section_from_pfn() helper Li Zhe
2026-05-23 12:10   ` Mike Rapoport
2026-05-21  4:01 ` [PATCH v2 3/7] mm: add a template-based fast path for zone-device page init Li Zhe
2026-05-23 12:10   ` Mike Rapoport
2026-05-25  4:02     ` Li Zhe
2026-05-21  4:01 ` [PATCH v2 4/7] mm: extend the template fast path to zone-device compound tails Li Zhe
2026-05-23 12:10   ` Mike Rapoport
2026-05-25  4:11     ` Li Zhe
2026-05-21  4:01 ` [PATCH v2 5/7] string: introduce memcpy_streaming() helpers Li Zhe
2026-05-21  4:01 ` [PATCH v2 6/7] x86/string: extend memcpy_flushcache() fixed-size fastpaths Li Zhe
2026-05-21  4:01 ` [PATCH v2 7/7] mm: use memcpy_streaming() in zone-device template copies Li Zhe
2026-05-23 12:10   ` Mike Rapoport
2026-05-25  4:59     ` Li Zhe
2026-05-21 22:20 ` [PATCH v2 0/7] mm: speed up ZONE_DEVICE memmap initialization Andrew Morton
2026-05-22  7:49   ` Li Zhe

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

all inboxes | Powered by JetHome®