All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/22] mm: introduce N_MEMORY_COMMON
@ 2026-11-14  7:09 Gregory Price (Meta)
  2026-11-14  7:09 ` [PATCH 01/22] mm: add node-state-filtered zone and node iterators Gregory Price (Meta)
                   ` (21 more replies)
  0 siblings, 22 replies; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

This is phase 1 of the Private NUMA node series.

I will send v6 "Private Memory NUMA nodes" (phases 2 and 3) separately
next week for further discussion - but I pulled this out ahead to make
the core filtering mechanism easier to review.

The series is now broken up like so:

Phase 1: Introduce N_MEMORY_COMMON to define "general purpose" (common)
         memory as a concept separate from a node having memory as a
         topological feature (N_MEMORY).

         This phase is a no-op for existing systems, but creates the
         baseline for ensuring private node memory is default opted-out
         of being touched by MM (much like ZONE_DEVICE).


Phase 2: Introduce N_MEMORY_PRIVATE to define memory not part of the
         general purpose pool. With this we can introduce a minimum set
         of KUnit tests for the page allocator to demonstrate the
         isolation mechanism.

         At this point it will be possible for a driver to re-use the
         buddy allocator to make uniform order-N allocations.


Phase 3: Introduce feature bits (COMPACTION, RECLAIM, MEMPOLICY) which
         is the minimum viable set to allow userland to manage a private
         memory node via mbind() and migrate_pages().  

         This enables management of HBM and Coherent GPU memory via
         userland tools (numactl, cpuset) while protecting them from
         incidental allocation (no fallbacks, only explicit placement).

         The memory could even be hotplugged in ZONE_NORMAL and pinned.

Actual cover letter for this portion
====================================

N_MEMORY has two meanings today:
  1) this node has memory
  2) ordinary allocations and MM services can use this node's memory

The two are the same today. They stop being the same once a NUMA node
with memory isn't part of the general allocation pool.

This series adds N_MEMORY_COMMON for the second meaning and converts
the relevant N_MEMORY users accordingly. N_MEMORY continues reporting
memory topology - and some users (cpusets) still utilize it.

At the end of this series, N_MEMORY == N_MEMORY_COMMON, so the series
changes no behavior for existing systems. A later series will add memory
outside N_MEMORY_COMMON without having to audit every N_MEMORY user again.

Rather than add new hooks for page-table and pfn walkers, we consolidate
existing ZONE_DEVICE checks (page/folio_is_zone_device()) into a general
`page/folio_is_common_memory()` check.

The series is laid out as:

  1-3   helper functions to check node states and a folio's
        membership to a common memory node.  N_MEMORY_COMMON itself, and
        the `has_common_memory` sysfs ABI.
  4     zonelists: the fallback lists hold common nodes, and a node's
        __GFP_THISNODE list is built only for a common node
  5-21  N_MEMORY->N_MEMORY_COMMON and zone_device -> common_memory
        conversions. One component per patch.
  22    removal of first_memory_node/next_memory_node, which no longer
        have users after this series.

Each conversion restricts or adjusts:
  - an MM service (reclaim, compaction, KSM, collapse, etc)
  - an allocation target to common memory
  - accounting meant for ordinary allocations from common memory only

has_common_memory lists the same nodes as has_memory until memory
outside N_MEMORY_COMMON exists. It is added now so that userspace, such
as libnuma, can start choosing allocation targets from it, and so that
the meaning is fixed before anything makes the two lists differ.

first/next_memory_node() is replaced by first/next_node_state().

Gregory Price (Meta) (22):
  mm: add node-state-filtered zone and node iterators
  mm: publish and clear node memory states via helpers
  mm: introduce the common-memory node state
  mm/page_alloc: build zonelists from common memory
  mm/mempolicy: keep interleave and tmpfs mount policies on common memory
  mm: base hash distribution on common memory nodes
  mm/memory_hotplug: migrate offlined pages to common memory
  mm/memory-tiers: restrict demotion topology to common memory
  mm/ksm: restrict merging to common memory
  mm/khugepaged: restrict collapse to common memory
  mm/mempolicy: don't NUMA-hint folios outside common memory
  mm/damon: restrict DAMON-native folio operations to common memory
  mm/hugetlb: restrict pool growth to common memory
  mm/page_alloc: count common memory nodes for kswapd recovery
  mm/oom_kill: compare OOM constraints against common memory
  tty/sysrq: use common memory for the manual OOM zonelist
  mm/vmscan: restrict reclaim to common memory
  mm/compaction: restrict compaction to common memory
  mm/migrate: restrict move_pages() destinations to common memory
  PM: hibernate: size the image from common memory
  mm/khugepaged: base the recommended min_free_kbytes on common memory
  nodemask: remove the now-unused first_memory_node/next_memory_node

 Documentation/ABI/stable/sysfs-devices-node | 16 +++++++
 Documentation/mm/physical_memory.rst        |  8 +++-
 drivers/base/node.c                         |  2 +
 drivers/tty/sysrq.c                         |  3 +-
 include/linux/mmzone.h                      | 46 +++++++++++++++++++++
 include/linux/nodemask.h                    | 38 +++++++++++++----
 kernel/power/snapshot.c                     |  7 +++-
 mm/compaction.c                             | 14 +++----
 mm/damon/ops-common.c                       |  7 ++--
 mm/damon/vaddr.c                            |  2 +-
 mm/hugetlb.c                                | 44 +++++++++-----------
 mm/internal.h                               | 18 ++++++++
 mm/khugepaged.c                             | 12 ++++--
 mm/ksm.c                                    |  8 ++--
 mm/memory-tiers.c                           | 23 ++++++-----
 mm/memory_hotplug.c                         | 25 ++++++-----
 mm/mempolicy.c                              | 30 +++++++-------
 mm/migrate.c                                |  2 +-
 mm/mm_init.c                                | 16 ++++---
 mm/oom_kill.c                               |  7 ++--
 mm/page_alloc.c                             | 43 +++++++++++++------
 mm/vmscan.c                                 | 26 ++++++------
 22 files changed, 266 insertions(+), 131 deletions(-)

-- 
2.53.1-Meta


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

* [PATCH 01/22] mm: add node-state-filtered zone and node iterators
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:19   ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 02/22] mm: publish and clear node memory states via helpers Gregory Price (Meta)
                   ` (20 subsequent siblings)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

Add node-state-filter helper functions/macros so MM walkers can select
their node eligibility explicitly.

Add nodemask intersection and nodemask/state iterators using the bitmap
intersection helpers, without allocating a temporary nodemask.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 include/linux/mmzone.h   | 46 ++++++++++++++++++++++++++++++++++++++++
 include/linux/nodemask.h | 28 ++++++++++++++++++++----
 2 files changed, 70 insertions(+), 4 deletions(-)

diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index c161d563ce8fd..5ff190d870830 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -1845,6 +1845,13 @@ extern struct zone *next_zone(struct zone *zone);
 			; /* do nothing */		\
 		else
 
+/* Populated zones on nodes in @state. */
+#define for_each_populated_zone_state(zone, state)	\
+	for_each_populated_zone(zone)			\
+		if (!node_state(zone_to_nid(zone), state))	\
+			; /* do nothing */		\
+		else
+
 static inline struct zone *zonelist_zone(struct zoneref *zoneref)
 {
 	return zoneref->zone;
@@ -1937,6 +1944,45 @@ static inline struct zoneref *first_zones_zonelist(struct zonelist *zonelist,
 			zone = zonelist_zone(z))
 
 
+static inline struct zoneref *
+next_zones_zonelist_state(struct zoneref *z, enum zone_type highest_zoneidx,
+			  const nodemask_t *nodes, enum node_states state)
+{
+	z = next_zones_zonelist(z, highest_zoneidx, nodes);
+	while (zonelist_zone(z) && !node_state(zonelist_node_idx(z), state))
+		z = next_zones_zonelist(++z, highest_zoneidx, nodes);
+	return z;
+}
+
+static inline struct zoneref *
+first_zones_zonelist_state(struct zonelist *zonelist,
+			   enum zone_type highest_zoneidx,
+			   const nodemask_t *nodes, enum node_states state)
+{
+	return next_zones_zonelist_state(zonelist->_zonerefs, highest_zoneidx,
+					 nodes, state);
+}
+
+/**
+ * for_each_zone_node_state - iterate zones in intersect(nodemask, node_state(state))
+ * @zone: The current zone in the iterator
+ * @z: The current pointer within zonelist->_zonerefs being iterated
+ * @zlist: The zonelist being iterated
+ * @highidx: The zone index of the highest zone to return
+ * @nodemask: Nodemask allowed by the allocator
+ * @state: The node state the zone's node must be in
+ *
+ * The state filter is part of the iteration rather than a trailing if, so that
+ * this expands to a single for statement: a macro ending in an unbraced if
+ * silently steals a following else from the caller.
+ */
+#define for_each_zone_node_state(zone, z, zlist, highidx, nodemask, state)	\
+	for (z = first_zones_zonelist_state(zlist, highidx, nodemask, state),	\
+		zone = zonelist_zone(z);					\
+		zone;								\
+		z = next_zones_zonelist_state(++z, highidx, nodemask, state),	\
+		zone = zonelist_zone(z))
+
 /**
  * for_each_zone_zonelist - helper macro to iterate over valid zones in a zonelist at or below a given zone index
  * @zone: The current zone in the iterator
diff --git a/include/linux/nodemask.h b/include/linux/nodemask.h
index 607373bd83ee6..28a456c3f5964 100644
--- a/include/linux/nodemask.h
+++ b/include/linux/nodemask.h
@@ -386,6 +386,10 @@ static __always_inline void __nodes_fold(nodemask_t *dstp, const nodemask_t *ori
 	for ((node) = 0; (node) < 1 && !nodes_empty(mask); (node)++)
 #endif /* MAX_NUMNODES */
 
+/* Iterate the nodes present in both masks without a temporary nodemask. */
+#define for_each_node_mask_and(node, mask1, mask2) \
+	for_each_and_bit(node, nodes_addr(mask1), nodes_addr(mask2), MAX_NUMNODES)
+
 /*
  * Bitmasks that are kept for all the nodes.
  */
@@ -411,6 +415,10 @@ enum node_states {
 
 extern nodemask_t node_states[NR_NODE_STATES];
 
+/* Iterate the nodes in a mask that also belong to the specified state. */
+#define for_each_node_mask_state(node, mask, state) \
+	for_each_node_mask_and(node, mask, node_states[state])
+
 #if MAX_NUMNODES > 1
 static __always_inline int node_state(int node, enum node_states state)
 {
@@ -435,15 +443,21 @@ static __always_inline int num_node_state(enum node_states state)
 #define for_each_node_state(__node, __state) \
 	for_each_node_mask((__node), node_states[__state])
 
-#define first_online_node	first_node(node_states[N_ONLINE])
-#define first_memory_node	first_node(node_states[N_MEMORY])
+#define first_node_state(state)	first_node(node_states[state])
+static __always_inline unsigned int next_node_state(int nid, enum node_states state)
+{
+	return next_node(nid, node_states[state]);
+}
+
+#define first_online_node	first_node_state(N_ONLINE)
+#define first_memory_node	first_node_state(N_MEMORY)
 static __always_inline unsigned int next_online_node(int nid)
 {
-	return next_node(nid, node_states[N_ONLINE]);
+	return next_node_state(nid, N_ONLINE);
 }
 static __always_inline unsigned int next_memory_node(int nid)
 {
-	return next_node(nid, node_states[N_MEMORY]);
+	return next_node_state(nid, N_MEMORY);
 }
 
 static __always_inline void node_set_online(int nid)
@@ -481,6 +495,12 @@ static __always_inline int num_node_state(enum node_states state)
 #define for_each_node_state(node, __state) \
 	for ( (node) = 0; (node) == 0; (node) = 1)
 
+#define first_node_state(state)	0
+static __always_inline unsigned int next_node_state(int nid, enum node_states state)
+{
+	return MAX_NUMNODES;
+}
+
 #define first_online_node	0
 #define first_memory_node	0
 #define next_online_node(nid)	(MAX_NUMNODES)
-- 
2.53.1-Meta


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

* [PATCH 02/22] mm: publish and clear node memory states via helpers
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
  2026-11-14  7:09 ` [PATCH 01/22] mm: add node-state-filtered zone and node iterators Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:20   ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 03/22] mm: introduce the common-memory node state Gregory Price (Meta)
                   ` (19 subsequent siblings)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

N_MEMORY, N_NORMAL_MEMORY and N_HIGH_MEMORY are set separately in
free_area_init(), check_for_memory() and online_pages(), and cleared
in offline_pages().

Centralize those updates in node_set/clear_memory_state().

Behavior is unchanged.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 mm/internal.h       |  3 +++
 mm/memory_hotplug.c | 11 ++++-------
 mm/mm_init.c        | 14 ++++++--------
 mm/page_alloc.c     | 15 +++++++++++++++
 4 files changed, 28 insertions(+), 15 deletions(-)

diff --git a/mm/internal.h b/mm/internal.h
index cd3675c044e0a..608a83c34c224 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -62,6 +62,9 @@ void lru_add_drain_cpu_zone(struct zone *zone);
 void folio_deactivate(struct folio *folio);
 void folio_mark_lazyfree(struct folio *folio);
 
+void node_set_memory_state(int nid, bool high, bool normal);
+void node_clear_memory_state(int nid);
+
 /* mm/vmscan.c */
 unsigned long zone_reclaimable_pages(struct zone *zone);
 unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index f04a248d8adaf..490f400498f14 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1184,14 +1184,11 @@ int online_pages(unsigned long pfn, unsigned long nr_pages,
 	online_pages_range(pfn, nr_pages);
 	adjust_present_page_count(pfn_to_page(pfn), group, nr_pages);
 
-	if (node_arg.nid >= 0)
-		node_set_state(nid, N_MEMORY);
 	/*
-	 * Check whether we are adding normal memory to the node for the first
-	 * time.
+	 * Publish this node's memory states, including any newly-normal zone.
+	 * Hotplug never adds highmem.
 	 */
-	if (!node_state(nid, N_NORMAL_MEMORY) && zone_idx(zone) <= ZONE_NORMAL)
-		node_set_state(nid, N_NORMAL_MEMORY);
+	node_set_memory_state(nid, false, zone_idx(zone) <= ZONE_NORMAL);
 
 	if (need_zonelists_rebuild)
 		build_all_zonelists(NULL);
@@ -2146,7 +2143,7 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages,
 	 * list. Otherwise this node would still appear in the fallback lists.
 	 */
 	if (node_arg.nid >= 0)
-		node_clear_state(node, N_MEMORY);
+		node_clear_memory_state(node);
 	if (!populated_zone(zone)) {
 		zone_pcp_reset(zone);
 		build_all_zonelists(NULL);
diff --git a/mm/mm_init.c b/mm/mm_init.c
index a9d848c92678b..b4c4dba93652d 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -1763,21 +1763,21 @@ static void __init free_area_init_node(int nid)
 	lru_gen_init_pgdat(pgdat);
 }
 
-/* Any regular or high memory on that node? */
+/* Publish the memory states of a node that has present pages. */
 static void __init check_for_memory(pg_data_t *pgdat)
 {
 	enum zone_type zone_type;
+	bool high = false, normal = false;
 
 	for (zone_type = 0; zone_type <= ZONE_MOVABLE - 1; zone_type++) {
 		struct zone *zone = &pgdat->node_zones[zone_type];
 		if (populated_zone(zone)) {
-			if (IS_ENABLED(CONFIG_HIGHMEM))
-				node_set_state(pgdat->node_id, N_HIGH_MEMORY);
-			if (zone_type <= ZONE_NORMAL)
-				node_set_state(pgdat->node_id, N_NORMAL_MEMORY);
+			high = IS_ENABLED(CONFIG_HIGHMEM);
+			normal = zone_type <= ZONE_NORMAL;
 			break;
 		}
 	}
+	node_set_memory_state(pgdat->node_id, high, normal);
 }
 
 #if MAX_NUMNODES > 1
@@ -1924,10 +1924,8 @@ static void __init free_area_init(void)
 		 *memory-less node. The pgdat will get fully initialized by
 		 *hotadd_init_pgdat() when memory is hotplugged into this node.
 		 */
-		if (pgdat->node_present_pages) {
-			node_set_state(nid, N_MEMORY);
+		if (pgdat->node_present_pages)
 			check_for_memory(pgdat);
-		}
 	}
 
 	calc_nr_kernel_pages();
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 8532af052d74b..834d199ff8429 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -208,6 +208,21 @@ nodemask_t node_states[NR_NODE_STATES] __read_mostly = {
 };
 EXPORT_SYMBOL(node_states);
 
+void __meminit node_set_memory_state(int nid, bool high, bool normal)
+{
+	node_set_state(nid, N_MEMORY);
+	if (high)
+		node_set_state(nid, N_HIGH_MEMORY);
+	if (normal)
+		node_set_state(nid, N_NORMAL_MEMORY);
+}
+
+void __meminit node_clear_memory_state(int nid)
+{
+	node_clear_state(nid, N_NORMAL_MEMORY);
+	node_clear_state(nid, N_MEMORY);
+}
+
 gfp_t gfp_allowed_mask __read_mostly = GFP_BOOT_MASK;
 
 #ifdef CONFIG_HUGETLB_PAGE_SIZE_VARIABLE
-- 
2.53.1-Meta


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

* [PATCH 03/22] mm: introduce the common-memory node state
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
  2026-11-14  7:09 ` [PATCH 01/22] mm: add node-state-filtered zone and node iterators Gregory Price (Meta)
  2026-11-14  7:09 ` [PATCH 02/22] mm: publish and clear node memory states via helpers Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:17   ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 04/22] mm/page_alloc: build zonelists from common memory Gregory Price (Meta)
                   ` (18 subsequent siblings)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

N_MEMORY is overloaded with two meanings:
  1) a node has memory.
  2) a node's memory is reachable via ordinary allocation or
     an arbitrary MM service may use that memory.

Allow N_MEMORY to mean #1, and introduce N_MEMORY_COMMON to mean #2.

N_MEMORY_COMMON nodes contain "common memory" capable of being managed
arbitrarily by the kernel MM based solely on zone membership. Following
commits convert relevant N_MEMORY users to N_MEMORY_COMMON.

Add folio_is_common_memory() and page_is_common_memory() for MM services
that walk folios. Following commits convert folio_is_zone_device() and
page_is_zone_device() callers accordingly.

Expose the state via `has_common_memory` so userspace can identify
the memory available to ordinary allocations and common MM services.

Add has_memory to the docs while we're here.

Every node with memory is common, so N_MEMORY_COMMON equals N_MEMORY
and there is no behavioral change.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 Documentation/ABI/stable/sysfs-devices-node | 16 ++++++++++++++++
 Documentation/mm/physical_memory.rst        |  8 +++++++-
 drivers/base/node.c                         |  2 ++
 include/linux/nodemask.h                    |  9 ++++++++-
 mm/internal.h                               | 15 +++++++++++++++
 mm/page_alloc.c                             |  3 +++
 6 files changed, 51 insertions(+), 2 deletions(-)

diff --git a/Documentation/ABI/stable/sysfs-devices-node b/Documentation/ABI/stable/sysfs-devices-node
index 2d0e023f22a71..a93de328f8fe8 100644
--- a/Documentation/ABI/stable/sysfs-devices-node
+++ b/Documentation/ABI/stable/sysfs-devices-node
@@ -10,6 +10,15 @@ Contact:	Linux Memory Management list <linux-mm@kvack.org>
 Description:
 		Nodes that are online.
 
+What:		/sys/devices/system/node/has_memory
+Date:		December 2012
+Contact:	Linux Memory Management list <linux-mm@kvack.org>
+Description:
+		Nodes that have memory.
+
+		This memory may or may not be reachable via standard
+		allocations, see has_common_memory for details.
+
 What:		/sys/devices/system/node/has_normal_memory
 Date:		October 2002
 Contact:	Linux Memory Management list <linux-mm@kvack.org>
@@ -29,6 +38,13 @@ Description:
 		Nodes that have regular or high memory.
 		Depends on CONFIG_HIGHMEM.
 
+What:		/sys/devices/system/node/has_common_memory
+Date:		November 2026
+Contact:	Linux Memory Management list <linux-mm@kvack.org>
+Description:
+		Nodes whose memory ordinary allocations may use without
+		requesting the node explicitly.
+
 What:		/sys/devices/system/node/nodeX
 Date:		October 2002
 Contact:	Linux Memory Management list <linux-mm@kvack.org>
diff --git a/Documentation/mm/physical_memory.rst b/Documentation/mm/physical_memory.rst
index 2e67e8b23a994..fd5c5d136180f 100644
--- a/Documentation/mm/physical_memory.rst
+++ b/Documentation/mm/physical_memory.rst
@@ -168,7 +168,12 @@ nodes with particular properties as defined by ``enum node_states``:
   The node has regular or high memory. When ``CONFIG_HIGHMEM`` is disabled
   aliased to ``N_NORMAL_MEMORY``.
 ``N_MEMORY``
-  The node has memory(regular, high, movable)
+  The node has online memory (regular, high, movable).  This is a topology and
+  accounting state. Presence in this mask does not mean ordinary allocations
+  can use the node's memory; see ``N_MEMORY_COMMON``.
+``N_MEMORY_COMMON``
+  The node is on the page allocator's fallback zonelists, so an allocation
+  that named no node can be satisfied from it.
 ``N_CPU``
   The node has one or more CPUs
 ``N_GENERIC_INITIATOR``
@@ -184,6 +189,7 @@ For example, for node 2 with normal memory and CPUs, bit 2 will be set in ::
   node_states[N_NORMAL_MEMORY]
   node_states[N_HIGH_MEMORY]
   node_states[N_MEMORY]
+  node_states[N_MEMORY_COMMON]
   node_states[N_CPU]
 
 For various operations possible with nodemasks please refer to
diff --git a/drivers/base/node.c b/drivers/base/node.c
index dcdc8626627a8..cc6696d1390f3 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -965,6 +965,7 @@ static struct node_attr node_state_attr[] = {
 	[N_HIGH_MEMORY] = _NODE_ATTR(has_high_memory, N_HIGH_MEMORY),
 #endif
 	[N_MEMORY] = _NODE_ATTR(has_memory, N_MEMORY),
+	[N_MEMORY_COMMON] = _NODE_ATTR(has_common_memory, N_MEMORY_COMMON),
 	[N_CPU] = _NODE_ATTR(has_cpu, N_CPU),
 	[N_GENERIC_INITIATOR] = _NODE_ATTR(has_generic_initiator,
 					   N_GENERIC_INITIATOR),
@@ -978,6 +979,7 @@ static struct attribute *node_state_attrs[] = {
 	&node_state_attr[N_HIGH_MEMORY].attr.attr,
 #endif
 	&node_state_attr[N_MEMORY].attr.attr,
+	&node_state_attr[N_MEMORY_COMMON].attr.attr,
 	&node_state_attr[N_CPU].attr.attr,
 	&node_state_attr[N_GENERIC_INITIATOR].attr.attr,
 	NULL
diff --git a/include/linux/nodemask.h b/include/linux/nodemask.h
index 28a456c3f5964..7ace610bdd654 100644
--- a/include/linux/nodemask.h
+++ b/include/linux/nodemask.h
@@ -402,7 +402,14 @@ enum node_states {
 #else
 	N_HIGH_MEMORY = N_NORMAL_MEMORY,
 #endif
-	N_MEMORY,		/* The node has memory(regular, high, movable) */
+	/*
+	 * N_MEMORY indicates online memory exists on the node - it is used
+	 * to manage per-node meta-data and topology decisions.
+	 * N_MEMORY_COMMON indicates whether the node can satisfy arbitrary
+	 * allocations and whether a service should manage that node's folios.
+	 */
+	N_MEMORY,		/* the node has memory */
+	N_MEMORY_COMMON,	/* common pool (fallback zonelist) */
 	N_CPU,		/* The node has one or more cpus */
 	N_GENERIC_INITIATOR,	/* The node has one or more Generic Initiators */
 	NR_NODE_STATES
diff --git a/mm/internal.h b/mm/internal.h
index 608a83c34c224..af6fc27a9fc98 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -65,6 +65,21 @@ void folio_mark_lazyfree(struct folio *folio);
 void node_set_memory_state(int nid, bool high, bool normal);
 void node_clear_memory_state(int nid);
 
+/*
+ * Common memory is available to generic MM services. ZONE_DEVICE folios do
+ * not qualify even when their NUMA node is common.
+ */
+static inline bool folio_is_common_memory(struct folio *folio)
+{
+	return !folio_is_zone_device(folio) &&
+	       node_state(folio_nid(folio), N_MEMORY_COMMON);
+}
+
+static inline bool page_is_common_memory(struct page *page)
+{
+	return folio_is_common_memory(page_folio(page));
+}
+
 /* mm/vmscan.c */
 unsigned long zone_reclaimable_pages(struct zone *zone);
 unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 834d199ff8429..6ffa972811462 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -203,6 +203,7 @@ nodemask_t node_states[NR_NODE_STATES] __read_mostly = {
 	[N_HIGH_MEMORY] = { { [0] = 1UL } },
 #endif
 	[N_MEMORY] = { { [0] = 1UL } },
+	[N_MEMORY_COMMON] = { { [0] = 1UL } },
 	[N_CPU] = { { [0] = 1UL } },
 #endif	/* NUMA */
 };
@@ -215,10 +216,12 @@ void __meminit node_set_memory_state(int nid, bool high, bool normal)
 		node_set_state(nid, N_HIGH_MEMORY);
 	if (normal)
 		node_set_state(nid, N_NORMAL_MEMORY);
+	node_set_state(nid, N_MEMORY_COMMON);
 }
 
 void __meminit node_clear_memory_state(int nid)
 {
+	node_clear_state(nid, N_MEMORY_COMMON);
 	node_clear_state(nid, N_NORMAL_MEMORY);
 	node_clear_state(nid, N_MEMORY);
 }
-- 
2.53.1-Meta


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

* [PATCH 04/22] mm/page_alloc: build zonelists from common memory
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
                   ` (2 preceding siblings ...)
  2026-11-14  7:09 ` [PATCH 03/22] mm: introduce the common-memory node state Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:16   ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 05/22] mm/mempolicy: keep interleave and tmpfs mount policies on " Gregory Price (Meta)
                   ` (17 subsequent siblings)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

Zonelists decide which nodes an allocation can reach.

The fallback lists hold every node in N_MEMORY, and a node's
__GFP_THISNODE list holds its own zones whatever the node's state.

Ordinary allocations should reach only common (general purpose) memory.

Build the fallback zonelists from N_MEMORY_COMMON, and __GFP_THISNODE
zonelists only for a common node.

The build_local_zonelist() refactor lets use reuse the code to build
a local-only zonelist for nodes outside the N_MEMORY_COMMON set later.

Every node with memory is common, and a memoryless node has no zones to
list, so the zonelists are unchanged.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 mm/page_alloc.c | 23 ++++++++++++-----------
 1 file changed, 12 insertions(+), 11 deletions(-)

diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 6ffa972811462..580feeb608b7a 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -5906,17 +5906,14 @@ int find_next_best_node_in(int node, nodemask_t *used_node_mask,
 }
 
 
-/*
- * Build __GFP_THISNODE zonelists
- */
-static void build_thisnode_zonelists(pg_data_t *pgdat)
+/* Build a local-only zonelist when this node is eligible. */
+static void build_local_zonelist(pg_data_t *pgdat, int zlidx, bool eligible)
 {
-	struct zoneref *zonerefs;
-	int nr_zones;
+	struct zoneref *zonerefs =
+		pgdat->node_zonelists[zlidx]._zonerefs;
 
-	zonerefs = pgdat->node_zonelists[ZONELIST_NOFALLBACK]._zonerefs;
-	nr_zones = build_zonerefs_node(pgdat, zonerefs);
-	zonerefs += nr_zones;
+	if (eligible)
+		zonerefs += build_zonerefs_node(pgdat, zonerefs);
 	zonerefs->zone = NULL;
 	zonerefs->zone_idx = 0;
 }
@@ -5960,8 +5957,12 @@ static void build_node_zonelist(pg_data_t *pgdat, const nodemask_t *candidates,
 
 static void build_zonelists(pg_data_t *pgdat)
 {
-	build_node_zonelist(pgdat, &node_states[N_MEMORY], ZONELIST_FALLBACK);
-	build_thisnode_zonelists(pgdat);
+	int nid = pgdat->node_id;
+
+	build_node_zonelist(pgdat, &node_states[N_MEMORY_COMMON],
+			    ZONELIST_FALLBACK);
+	build_local_zonelist(pgdat, ZONELIST_NOFALLBACK,
+			     node_state(nid, N_MEMORY_COMMON));
 }
 
 #ifdef CONFIG_HAVE_MEMORYLESS_NODES
-- 
2.53.1-Meta


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

* [PATCH 05/22] mm/mempolicy: keep interleave and tmpfs mount policies on common memory
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
                   ` (3 preceding siblings ...)
  2026-11-14  7:09 ` [PATCH 04/22] mm/page_alloc: build zonelists from common memory Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:18   ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 06/22] mm: base hash distribution on common memory nodes Gregory Price (Meta)
                   ` (16 subsequent siblings)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

Use N_MEMORY_COMMON for boot mempolicy calculations and their per-node
sysfs entries (weighted interleave weights). 

Restrict the node set mpol_set_nodemask() applies to set_mempolicy()
and mbind() masks to N_MEMORY_COMMON nodes.

Later when an N_MEMORY node is omitted frmo the N_MEMORY_COMMON set,
they will be stripped from mempolicy nodemasks as they're not eligible
(similar to memoryless nodes today).

All memory is common at this point, no behavior change at this point.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 mm/mempolicy.c | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index b10c098e9c2a3..662ab420fe03c 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -213,10 +213,10 @@ static void reduce_interleave_weights(unsigned int *bw, u8 *new_iw)
 	int nid;
 
 	/*
-	 * N_MEMORY can change while wi_state_lock is held. Remember the
-	 * nodes contributing to sum_bw so all passes use the same set.
+	 * N_MEMORY_COMMON can change while wi_state_lock is held. Remember
+	 * the nodes contributing to sum_bw so all passes use the same set.
 	 */
-	for_each_node_state(nid, N_MEMORY) {
+	for_each_node_state(nid, N_MEMORY_COMMON) {
 		node_set(nid, nodes);
 		sum_bw += bw[nid];
 	}
@@ -439,9 +439,9 @@ static int mpol_set_nodemask(struct mempolicy *pol,
 	if (!pol || pol->mode == MPOL_LOCAL)
 		return 0;
 
-	/* Check N_MEMORY */
+	/* Check N_MEMORY_COMMON */
 	nodes_and(nsc->mask1,
-		  cpuset_current_mems_allowed, node_states[N_MEMORY]);
+		  cpuset_current_mems_allowed, node_states[N_MEMORY_COMMON]);
 
 	VM_BUG_ON(!nodes);
 
@@ -3400,11 +3400,11 @@ void __init numa_policy_init(void)
 
 	/*
 	 * Set interleaving policy for system init. Interleaving is only
-	 * enabled across suitably sized nodes (default is >= 16MB), or
-	 * fall back to the largest node if they're all smaller.
+	 * enabled across suitably sized common memory nodes (default is
+	 * >= 16MB), or fall back to the largest node if they're all smaller.
 	 */
 	nodes_clear(interleave_nodes);
-	for_each_node_state(nid, N_MEMORY) {
+	for_each_node_state(nid, N_MEMORY_COMMON) {
 		unsigned long total_pages = node_present_pages(nid);
 
 		/* Preserve the largest node */
@@ -3476,7 +3476,7 @@ int mpol_parse_str(char *str, struct mempolicy **mpol)
 		*nodelist++ = '\0';
 		if (nodelist_parse(nodelist, nodes))
 			goto out;
-		if (!nodes_subset(nodes, node_states[N_MEMORY]))
+		if (!nodes_subset(nodes, node_states[N_MEMORY_COMMON]))
 			goto out;
 	} else
 		nodes_clear(nodes);
@@ -3508,7 +3508,7 @@ int mpol_parse_str(char *str, struct mempolicy **mpol)
 		 * Default to online nodes with memory if no nodelist
 		 */
 		if (!nodelist)
-			nodes = node_states[N_MEMORY];
+			nodes = node_states[N_MEMORY_COMMON];
 		break;
 	case MPOL_LOCAL:
 		/*
@@ -3903,6 +3903,9 @@ static int wi_node_notifier(struct notifier_block *nb,
 
 	switch (action) {
 	case NODE_ADDED_FIRST_MEMORY:
+		/* Weighted interleave spans only common memory. */
+		if (!node_state(nid, N_MEMORY_COMMON))
+			break;
 		err = sysfs_wi_node_add(nid);
 		if (err)
 			pr_err("failed to add sysfs for node%d during hotplug: %d\n",
@@ -3934,10 +3937,7 @@ static int __init add_weighted_interleave_group(struct kobject *mempolicy_kobj)
 	if (err)
 		goto err_put_kobj;
 
-	for_each_online_node(nid) {
-		if (!node_state(nid, N_MEMORY))
-			continue;
-
+	for_each_node_state(nid, N_MEMORY_COMMON) {
 		err = sysfs_wi_node_add(nid);
 		if (err) {
 			pr_err("failed to add sysfs for node%d during init: %d\n",
-- 
2.53.1-Meta


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

* [PATCH 06/22] mm: base hash distribution on common memory nodes
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
                   ` (4 preceding siblings ...)
  2026-11-14  7:09 ` [PATCH 05/22] mm/mempolicy: keep interleave and tmpfs mount policies on " Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:15   ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 07/22] mm/memory_hotplug: migrate offlined pages to common memory Gregory Price (Meta)
                   ` (15 subsequent siblings)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

hashdist is useful when large kernel hash tables can be allocated across
multiple NUMA nodes. The tables come from ordinary allocations, which
only N_MEMORY_COMMON nodes serve.

Count N_MEMORY_COMMON nodes when applying the single-node optimization.

All memory is common as of now, so behavior is unchanged.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 mm/mm_init.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mm/mm_init.c b/mm/mm_init.c
index b4c4dba93652d..0c076bf5bf380 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -685,7 +685,7 @@ __setup("hashdist=", set_hashdist);
 
 static inline void fixup_hashdist(void)
 {
-	if (num_node_state(N_MEMORY) == 1)
+	if (num_node_state(N_MEMORY_COMMON) == 1)
 		hashdist = false;
 }
 #else
-- 
2.53.1-Meta


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

* [PATCH 07/22] mm/memory_hotplug: migrate offlined pages to common memory
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
                   ` (5 preceding siblings ...)
  2026-11-14  7:09 ` [PATCH 06/22] mm: base hash distribution on common memory nodes Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:16   ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 08/22] mm/memory-tiers: restrict demotion topology " Gregory Price (Meta)
                   ` (14 subsequent siblings)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

do_migrate_range() / alloc_migration_target() is only capable of
allocating memory on N_MEMORY_COMMON nodes. The destination nodemask
presently starts with N_MEMORY and removes the source-node.

In the current code, if the destination mask becomes empty, the source
node is restored to allow migration during partial offlining.

Consider this configuration:
  Node0 = N_MEMORY_COMMON
  Node1 = N_MEMORY, not N_MEMORY_COMMON

Node 1 keeps the destination mask non-empty, despite being unreachable.
This leaves migration with no usable destination because the source
node is not an eligible allocation target.

Restrict the hot-unplug migration target-list to N_MEMORY_COMMON so it
reflects the nodes the allocator can reach.

All memory is common at this point - behavior is unchanged.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 mm/memory_hotplug.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 490f400498f14..29ff2c46499b3 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1904,7 +1904,7 @@ static void do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
 		folio_put(folio);
 	}
 	if (!list_empty(&source)) {
-		nodemask_t nmask = node_states[N_MEMORY];
+		nodemask_t nmask = node_states[N_MEMORY_COMMON];
 		struct migration_target_control mtc = {
 			.nmask = &nmask,
 			.gfp_mask = GFP_KERNEL | __GFP_MOVABLE | __GFP_RETRY_MAYFAIL,
@@ -1919,9 +1919,9 @@ static void do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)
 		mtc.nid = folio_nid(list_first_entry(&source, struct folio, lru));
 
 		/*
-		 * try to allocate from a different node but reuse this node
-		 * if there are no other online nodes to be used (e.g. we are
-		 * offlining a part of the only existing node)
+		 * try to allocate from a different common node but reuse this
+		 * node if there are no other common nodes to be used (e.g. we
+		 * are offlining a part of the only common node)
 		 */
 		node_clear(mtc.nid, nmask);
 		if (nodes_empty(nmask))
-- 
2.53.1-Meta


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

* [PATCH 08/22] mm/memory-tiers: restrict demotion topology to common memory
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
                   ` (6 preceding siblings ...)
  2026-11-14  7:09 ` [PATCH 07/22] mm/memory_hotplug: migrate offlined pages to common memory Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:16   ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 09/22] mm/ksm: restrict merging " Gregory Price (Meta)
                   ` (13 subsequent siblings)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

Memory outside N_MEMORY_COMMON is outside the ordinary allocation pool
and cannot be a generic demotion source or destination.

Build demotion paths and the default DRAM performance reference from
N_MEMORY_COMMON rather than every node that has memory, and do not
assign other nodes to a memory tier through the generic hotplug path.

Removing a node's memory already resets its demotion targets, so a node
that unplugs and returns outside N_MEMORY_COMMON keeps none.

All memory nodes currently carry N_MEMORY_COMMON, behavior is unchanged.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 mm/memory-tiers.c | 23 ++++++++++++-----------
 1 file changed, 12 insertions(+), 11 deletions(-)

diff --git a/mm/memory-tiers.c b/mm/memory-tiers.c
index 7fc56a8c66518..97d5ecefd89f3 100644
--- a/mm/memory-tiers.c
+++ b/mm/memory-tiers.c
@@ -370,7 +370,8 @@ int next_demotion_node(int node, const nodemask_t *allowed_mask)
 	 * closest demotion target.
 	 */
 	nodes_complement(mask, *allowed_mask);
-	return find_next_best_node_in(node, &mask, &node_states[N_MEMORY]);
+	return find_next_best_node_in(node, &mask,
+				      &node_states[N_MEMORY_COMMON]);
 }
 
 static void disable_all_demotion_targets(void)
@@ -405,7 +406,7 @@ static void dump_demotion_targets(void)
 {
 	int node;
 
-	for_each_node_state(node, N_MEMORY) {
+	for_each_node_state(node, N_MEMORY_COMMON) {
 		struct memory_tier *memtier = __node_get_memory_tier(node);
 		nodemask_t preferred = node_demotion[node].preferred;
 
@@ -441,7 +442,7 @@ static void establish_demotion_targets(void)
 
 	disable_all_demotion_targets();
 
-	for_each_node_state(node, N_MEMORY) {
+	for_each_node_state(node, N_MEMORY_COMMON) {
 		best_distance = -1;
 		nd = &node_demotion[node];
 
@@ -459,7 +460,7 @@ static void establish_demotion_targets(void)
 		 * nodelist to skip list so that we find the best node from the
 		 * memtier nodelist.
 		 */
-		nodes_andnot(tier_nodes, node_states[N_MEMORY], tier_nodes);
+		nodes_andnot(tier_nodes, node_states[N_MEMORY_COMMON], tier_nodes);
 
 		/*
 		 * Find all the nodes in the memory tier node list of same best distance.
@@ -468,7 +469,7 @@ static void establish_demotion_targets(void)
 		 */
 		do {
 			target = find_next_best_node_in(node, &tier_nodes,
-							&node_states[N_MEMORY]);
+							&node_states[N_MEMORY_COMMON]);
 			if (target == NUMA_NO_NODE)
 				break;
 
@@ -507,7 +508,7 @@ static void establish_demotion_targets(void)
 	 * allocation to a set of nodes that is closer the above selected
 	 * preferred node.
 	 */
-	lower_tier = node_states[N_MEMORY];
+	lower_tier = node_states[N_MEMORY_COMMON];
 	list_for_each_entry(memtier, &memory_tiers, list) {
 		/*
 		 * Keep removing current tier from lower_tier nodes,
@@ -554,7 +555,7 @@ static struct memory_tier *set_node_memory_tier(int node)
 
 	lockdep_assert_held_once(&memory_tier_lock);
 
-	if (!node_state(node, N_MEMORY))
+	if (!node_state(node, N_MEMORY_COMMON))
 		return ERR_PTR(-EINVAL);
 
 	mt_calc_adistance(node, &adist);
@@ -706,8 +707,8 @@ static int __init memory_tier_late_init(void)
 	get_online_mems();
 	guard(mutex)(&memory_tier_lock);
 
-	/* Assign each uninitialized N_MEMORY node to a memory tier. */
-	for_each_node_state(nid, N_MEMORY) {
+	/* Assign each uninitialized common memory node to a memory tier. */
+	for_each_node_state(nid, N_MEMORY_COMMON) {
 		/*
 		 * Some device drivers may have initialized
 		 * memory tiers, potentially bringing memory nodes
@@ -929,8 +930,8 @@ static int __init memory_tier_init(void)
 	if (IS_ERR(default_dram_type))
 		panic("%s() failed to allocate default DRAM tier\n", __func__);
 
-	/* Record nodes with memory and CPU to set default DRAM performance. */
-	nodes_and(default_dram_nodes, node_states[N_MEMORY],
+	/* Record common-memory nodes with CPUs for default DRAM performance. */
+	nodes_and(default_dram_nodes, node_states[N_MEMORY_COMMON],
 		  node_states[N_CPU]);
 
 	hotplug_node_notifier(memtier_hotplug_callback, MEMTIER_HOTPLUG_PRI);
-- 
2.53.1-Meta


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

* [PATCH 09/22] mm/ksm: restrict merging to common memory
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
                   ` (7 preceding siblings ...)
  2026-11-14  7:09 ` [PATCH 08/22] mm/memory-tiers: restrict demotion topology " Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:20   ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 10/22] mm/khugepaged: restrict collapse " Gregory Price (Meta)
                   ` (12 subsequent siblings)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

KSM is an opportunistic MM service and should leave memory outside
the ordinary memory pool alone - as it already does for ZONE_DEVICE
folios at each acquisition point.

Use folio_is_common_memory() at those same sites, which still rejects
ZONE_DEVICE folios.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 mm/ksm.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/mm/ksm.c b/mm/ksm.c
index 623a2cdb39242..25b2c2f944862 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -831,7 +831,7 @@ static struct page *get_mergeable_page(struct ksm_rmap_item *rmap_item)
 
 	folio = folio_walk_start(&fw, vma, addr, 0);
 	if (folio) {
-		if (!folio_is_zone_device(folio) &&
+		if (folio_is_common_memory(folio) &&
 		    folio_test_anon(folio)) {
 			folio_get(folio);
 			page = fw.page;
@@ -2571,7 +2571,8 @@ static int ksm_next_page_pmd_entry(pmd_t *pmdp, unsigned long addr, unsigned lon
 				goto not_found_unlock;
 			folio = page_folio(page);
 
-			if (folio_is_zone_device(folio) || !folio_test_anon(folio))
+			if (!folio_is_common_memory(folio) ||
+			    !folio_test_anon(folio))
 				goto not_found_unlock;
 
 			page += ((addr & (PMD_SIZE - 1)) >> PAGE_SHIFT);
@@ -2595,7 +2596,8 @@ static int ksm_next_page_pmd_entry(pmd_t *pmdp, unsigned long addr, unsigned lon
 			continue;
 		folio = page_folio(page);
 
-		if (folio_is_zone_device(folio) || !folio_test_anon(folio))
+		if (!folio_is_common_memory(folio) ||
+		    !folio_test_anon(folio))
 			continue;
 		goto found_unlock;
 	}
-- 
2.53.1-Meta


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

* [PATCH 10/22] mm/khugepaged: restrict collapse to common memory
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
                   ` (8 preceding siblings ...)
  2026-11-14  7:09 ` [PATCH 09/22] mm/ksm: restrict merging " Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:21     ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 11/22] mm/mempolicy: don't NUMA-hint folios outside " Gregory Price (Meta)
                   ` (11 subsequent siblings)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

Collapse is an opportunistic MM service and should operate only on
common memory. The anonymous scan and isolation paths already filter
zone device for the same reason.

Replace the zone_device check with page_is_common_memory().

collapse_scan_file() gains a folio_is_common_memory() check. Page-cache
folios are never ZONE_DEVICE, so there it only filters memory outside
N_MEMORY_COMMON.

All memory is common at this point, so behavior is unchanged.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 mm/khugepaged.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index 4bbe319ef86fa..b4bd29f25c145 100644
--- a/mm/khugepaged.c
+++ b/mm/khugepaged.c
@@ -628,7 +628,7 @@ static enum scan_result __collapse_huge_page_isolate(struct vm_area_struct *vma,
 			goto out;
 		}
 		page = vm_normal_page(vma, addr, pteval);
-		if (unlikely(!page) || unlikely(is_zone_device_page(page))) {
+		if (unlikely(!page) || unlikely(!page_is_common_memory(page))) {
 			result = SCAN_PAGE_NULL;
 			goto out;
 		}
@@ -1673,7 +1673,7 @@ static enum scan_result collapse_scan_anon(struct vm_area_struct *vma,
 		}
 
 		page = vm_normal_page(vma, addr, pteval);
-		if (unlikely(!page) || unlikely(is_zone_device_page(page))) {
+		if (unlikely(!page) || unlikely(!page_is_common_memory(page))) {
 			result = SCAN_PAGE_NULL;
 			goto out_unmap;
 		}
@@ -2741,6 +2741,12 @@ static enum scan_result collapse_scan_file(struct mm_struct *mm,
 			break;
 		}
 
+		if (unlikely(!folio_is_common_memory(folio))) {
+			result = SCAN_PAGE_NULL;
+			failed_pfn = folio_pfn(folio);
+			folio_put(folio);
+			break;
+		}
 		node = folio_nid(folio);
 		if (collapse_scan_abort(node, cc)) {
 			result = SCAN_SCAN_ABORT;
-- 
2.53.1-Meta


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

* [PATCH 11/22] mm/mempolicy: don't NUMA-hint folios outside common memory
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
                   ` (9 preceding siblings ...)
  2026-11-14  7:09 ` [PATCH 10/22] mm/khugepaged: restrict collapse " Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:16   ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 12/22] mm/damon: restrict DAMON-native folio operations to " Gregory Price (Meta)
                   ` (10 subsequent siblings)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

The NUMA balancing scanner installs PROT_NONE only on folios that
balancing may migrate.

folio_can_map_prot_numa() already skips ZONE_DEVICE folios. Skip folios
outside the common memory pool (N_MEMORY_COMMON) as well. Do not inject
hint faults on those folios or migrate them.

Convert the zone_device check to a common memory check (which includes
the zone device check explicitly).

All memory is common at this point, so behavior is unchanged.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 mm/mempolicy.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 662ab420fe03c..619970f0eb954 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -863,7 +863,7 @@ bool folio_can_map_prot_numa(struct folio *folio, struct vm_area_struct *vma,
 {
 	int nid;
 
-	if (!folio || folio_is_zone_device(folio) || folio_test_ksm(folio))
+	if (!folio || !folio_is_common_memory(folio) || folio_test_ksm(folio))
 		return false;
 
 	/* Also skip shared copy-on-write folios */
-- 
2.53.1-Meta


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

* [PATCH 12/22] mm/damon: restrict DAMON-native folio operations to common memory
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
                   ` (10 preceding siblings ...)
  2026-11-14  7:09 ` [PATCH 11/22] mm/mempolicy: don't NUMA-hint folios outside " Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:15   ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 13/22] mm/hugetlb: restrict pool growth " Gregory Price (Meta)
                   ` (9 subsequent siblings)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

DAMON acquires folios to age them, check their accesses, and apply LRU
operations and migration. Treat these as general-purpose MM services
and restrict them to N_MEMORY_COMMON.

Memory outside N_MEMORY_COMMON can still hold ordinary LRU folios, so
it would pass the checks that keep ZONE_DEVICE memory out of these
operations.

Treat these folios like ZONE_DEVICE memory.

DAMON cannot acquire such a folio, so it never ages one and virtual
address monitoring reports it as accessed, and DAMOS cannot isolate
one for migration.

Reject migration destinations outside N_MEMORY_COMMON.

All memory is common at this point, so behavior is unchanged.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 mm/damon/ops-common.c | 7 ++++---
 mm/damon/vaddr.c      | 2 +-
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c
index b9571f53e538c..51b4b5d572e77 100644
--- a/mm/damon/ops-common.c
+++ b/mm/damon/ops-common.c
@@ -17,8 +17,9 @@
 
 static bool damon_folio_acceptable(struct folio *folio, bool monitor)
 {
-	return folio_test_lru(folio) ||
-		(monitor && folio_test_hugetlb(folio));
+	return folio_is_common_memory(folio) &&
+		(folio_test_lru(folio) ||
+		 (monitor && folio_test_hugetlb(folio)));
 }
 
 /*
@@ -498,7 +499,7 @@ unsigned long damon_migrate_pages(struct list_head *folio_list, int target_nid)
 		return nr_migrated;
 
 	if (target_nid < 0 || target_nid >= MAX_NUMNODES ||
-			!node_state(target_nid, N_MEMORY)) {
+			!node_state(target_nid, N_MEMORY_COMMON)) {
 		damon_putback_folio_list(folio_list);
 		return nr_migrated;
 	}
diff --git a/mm/damon/vaddr.c b/mm/damon/vaddr.c
index 1e65e7651cd98..338876559140b 100644
--- a/mm/damon/vaddr.c
+++ b/mm/damon/vaddr.c
@@ -830,7 +830,7 @@ static void damos_va_migrate_dests_add(struct folio *folio,
 	if (folio_nid(folio) == nid)
 		return;
 
-	if (!folio_isolate_lru(folio))
+	if (!folio_is_common_memory(folio) || !folio_isolate_lru(folio))
 		return;
 	node_stat_add_folio(folio, NR_ISOLATED_ANON +
 			folio_is_file_lru(folio));
-- 
2.53.1-Meta


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

* [PATCH 13/22] mm/hugetlb: restrict pool growth to common memory
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
                   ` (11 preceding siblings ...)
  2026-11-14  7:09 ` [PATCH 12/22] mm/damon: restrict DAMON-native folio operations to " Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:18   ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 14/22] mm/page_alloc: count common memory nodes for kswapd recovery Gregory Price (Meta)
                   ` (8 subsequent siblings)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

Fresh HugeTLB pool folios come from ordinary allocations, which only
N_MEMORY_COMMON nodes serve.

Restrict HugeTLB allocations and pool-growth to N_MEMORY_COMMON nodes.

Keep the original allowed mask for pool shrinking and demotion so
existing pool folios can still be found on any online-memory node.

The result of this change is that hugepage allocations will fail on
nodes without N_MEMORY_COMMON.  This restriction can be loosened later
when/if it grows a user.

All memory is common at this point, so behavior is unchanged.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 mm/hugetlb.c | 44 ++++++++++++++++++++------------------------
 1 file changed, 20 insertions(+), 24 deletions(-)

diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index cb2de24affdc4..cc1468f68901b 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -3452,13 +3452,13 @@ static void __init hugetlb_hstate_alloc_pages_onenode(struct hstate *h, int nid)
 			gfp_t gfp_mask = htlb_alloc_mask(h) | __GFP_THISNODE;
 
 			folio = only_alloc_fresh_hugetlb_folio(h, gfp_mask, nid,
-					&node_states[N_MEMORY], NULL);
+					&node_states[N_MEMORY_COMMON], NULL);
 			if (!folio && !list_empty(&folio_list) &&
 			    hugetlb_vmemmap_optimizable(h)) {
 				prep_and_add_allocated_folios(h, &folio_list);
 				INIT_LIST_HEAD(&folio_list);
 				folio = only_alloc_fresh_hugetlb_folio(h, gfp_mask, nid,
-						&node_states[N_MEMORY], NULL);
+						&node_states[N_MEMORY_COMMON], NULL);
 			}
 			if (!folio)
 				break;
@@ -3528,7 +3528,7 @@ static void __init hugetlb_pages_alloc_boot_node(unsigned long start, unsigned l
 			prep_and_add_allocated_folios(h, &folio_list);
 			INIT_LIST_HEAD(&folio_list);
 		}
-		folio = alloc_pool_huge_folio(h, &node_states[N_MEMORY],
+		folio = alloc_pool_huge_folio(h, &node_states[N_MEMORY_COMMON],
 						&node_alloc_noretry, &next_node);
 		if (!folio)
 			break;
@@ -3672,13 +3672,12 @@ static void __init hugetlb_init_hstates(void)
 
 	for_each_hstate(h) {
 		/*
-		 * Always reset to first_memory_node here, even if
-		 * next_nid_to_alloc was set before - we can't
-		 * reference hugetlb_bootmem_nodes after init, and
-		 * first_memory_node is right for all further allocations.
+		 * Always reset the allocation cursor here, even if it was set
+		 * before: hugetlb_bootmem_nodes cannot be referenced after init,
+		 * and further pool allocations use common memory.
 		 */
-		h->next_nid_to_alloc = first_memory_node;
-		h->next_nid_to_free = first_memory_node;
+		h->next_nid_to_alloc = first_node_state(N_MEMORY_COMMON);
+		h->next_nid_to_free = first_node_state(N_MEMORY_COMMON);
 
 		/* oversize hugepages were init'ed in early boot */
 		if (!hstate_is_gigantic(h))
@@ -3802,23 +3801,21 @@ static int set_max_huge_pages(struct hstate *h, unsigned long count, int nid,
 	unsigned long allocated;
 	struct folio *folio;
 	LIST_HEAD(page_list);
-	NODEMASK_ALLOC(nodemask_t, node_alloc_noretry, GFP_KERNEL);
+	NODEMASK_ALLOC(struct nodemask_scratch, scratch, GFP_KERNEL);
 
-	/*
-	 * Bit mask controlling how hard we retry per-node allocations.
-	 * If we can not allocate the bit mask, do not attempt to allocate
-	 * the requested huge pages.
-	 */
-	if (node_alloc_noretry)
-		nodes_clear(*node_alloc_noretry);
-	else
+	/* Scratch masks for allocation eligibility and per-node retries. */
+	if (!scratch)
 		return -ENOMEM;
+	nodes_clear(scratch->mask2);
 
 	/*
 	 * resize_lock mutex prevents concurrent adjustments to number of
 	 * pages in hstate via the proc/sysfs interfaces.
 	 */
 	mutex_lock(&h->resize_lock);
+
+	/* Fresh pool pages come from the ordinary allocation domain. */
+	nodes_and(scratch->mask1, *nodes_allowed, node_states[N_MEMORY_COMMON]);
 	flush_free_hpage_work(h);
 	spin_lock_irq(&hugetlb_lock);
 
@@ -3855,7 +3852,7 @@ static int set_max_huge_pages(struct hstate *h, unsigned long count, int nid,
 		if (count > persistent_huge_pages(h)) {
 			spin_unlock_irq(&hugetlb_lock);
 			mutex_unlock(&h->resize_lock);
-			NODEMASK_FREE(node_alloc_noretry);
+			NODEMASK_FREE(scratch);
 			return -EINVAL;
 		}
 		/* Fall through to decrease pool */
@@ -3873,7 +3870,7 @@ static int set_max_huge_pages(struct hstate *h, unsigned long count, int nid,
 	 * within all the constraints specified by the sysctls.
 	 */
 	while (h->surplus_huge_pages && count > persistent_huge_pages(h)) {
-		if (!adjust_pool_surplus(h, nodes_allowed, -1))
+		if (!adjust_pool_surplus(h, &scratch->mask1, -1))
 			break;
 	}
 
@@ -3889,9 +3886,8 @@ static int set_max_huge_pages(struct hstate *h, unsigned long count, int nid,
 		/* yield cpu to avoid soft lockup */
 		cond_resched();
 
-		folio = alloc_pool_huge_folio(h, nodes_allowed,
-						node_alloc_noretry,
-						&h->next_nid_to_alloc);
+		folio = alloc_pool_huge_folio(h, &scratch->mask1, &scratch->mask2,
+					      &h->next_nid_to_alloc);
 		if (!folio) {
 			prep_and_add_allocated_folios(h, &page_list);
 			spin_lock_irq(&hugetlb_lock);
@@ -3978,7 +3974,7 @@ static int set_max_huge_pages(struct hstate *h, unsigned long count, int nid,
 	spin_unlock_irq(&hugetlb_lock);
 	mutex_unlock(&h->resize_lock);
 
-	NODEMASK_FREE(node_alloc_noretry);
+	NODEMASK_FREE(scratch);
 
 	return 0;
 }
-- 
2.53.1-Meta


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

* [PATCH 14/22] mm/page_alloc: count common memory nodes for kswapd recovery
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
                   ` (12 preceding siblings ...)
  2026-11-14  7:09 ` [PATCH 13/22] mm/hugetlb: restrict pool growth " Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:18   ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 15/22] mm/oom_kill: compare OOM constraints against common memory Gregory Price (Meta)
                   ` (7 subsequent siblings)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

When a zone recovers above its high watermark, free_frozen_page_commit()
clears a hopeless kswapd state if more than one memory node exists,
since allocations could then have fallen back to another node without
triggering direct reclaim here.

Only common memory provide fallbacks, so count N_MEMORY_COMMON nodes.

All memory is common at this point, so behavior is unchanged.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 mm/page_alloc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 580feeb608b7a..6d98e213460a9 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -2949,7 +2949,7 @@ static bool free_frozen_page_commit(struct zone *zone,
 		 * kswapd work again by resetting kswapd_failures.
 		 */
 		if (kswapd_test_hopeless(pgdat) &&
-		    num_node_state(N_MEMORY) > 1)
+		    num_node_state(N_MEMORY_COMMON) > 1)
 			kswapd_clear_hopeless(pgdat, KSWAPD_CLEAR_HOPELESS_PCP);
 	}
 	return ret;
-- 
2.53.1-Meta


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

* [PATCH 15/22] mm/oom_kill: compare OOM constraints against common memory
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
                   ` (13 preceding siblings ...)
  2026-11-14  7:09 ` [PATCH 14/22] mm/page_alloc: count common memory nodes for kswapd recovery Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:19   ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 16/22] tty/sysrq: use common memory for the manual OOM zonelist Gregory Price (Meta)
                   ` (6 subsequent siblings)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

Ordinary allocations reach only common-memory zonelists. Compare policy
and cpuset constraints against N_MEMORY_COMMON, so nodes outside it
neither make an otherwise global policy appear constrained nor inflate
the constrained capacity.

All memory is common at this point, so behavior is unchanged.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 mm/oom_kill.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index 5d48bd862c27b..1ec22bdad1fd4 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -274,9 +274,9 @@ static enum oom_constraint constrained_alloc(struct oom_control *oc)
 	 * get_page_from_freelist().
 	 */
 	if (oc->nodemask &&
-	    !nodes_subset(node_states[N_MEMORY], *oc->nodemask)) {
+	    !nodes_subset(node_states[N_MEMORY_COMMON], *oc->nodemask)) {
 		oc->totalpages = total_swap_pages;
-		for_each_node_mask(nid, *oc->nodemask)
+		for_each_node_mask_state(nid, *oc->nodemask, N_MEMORY_COMMON)
 			oc->totalpages += node_present_pages(nid);
 		return CONSTRAINT_MEMORY_POLICY;
 	}
@@ -289,7 +289,8 @@ static enum oom_constraint constrained_alloc(struct oom_control *oc)
 
 	if (cpuset_limited) {
 		oc->totalpages = total_swap_pages;
-		for_each_node_mask(nid, cpuset_current_mems_allowed)
+		for_each_node_mask_state(nid, cpuset_current_mems_allowed,
+					 N_MEMORY_COMMON)
 			oc->totalpages += node_present_pages(nid);
 		return CONSTRAINT_CPUSET;
 	}
-- 
2.53.1-Meta


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

* [PATCH 16/22] tty/sysrq: use common memory for the manual OOM zonelist
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
                   ` (14 preceding siblings ...)
  2026-11-14  7:09 ` [PATCH 15/22] mm/oom_kill: compare OOM constraints against common memory Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:19   ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 17/22] mm/vmscan: restrict reclaim to common memory Gregory Price (Meta)
                   ` (5 subsequent siblings)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

SysRq-f constructs an OOM request with GFP_KERNEL and supplies a
zonelist for allocation-constraint checks. The request represents an
ordinary allocation, whose fallback domain contains common memory rather
than every node that happens to have memory.

Select the zonelist from the first common-memory node instead of the
first memory node. All memory nodes are common at this point, so
behavior is unchanged.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 drivers/tty/sysrq.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index f4571d05b4962..824f5836b47d7 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -386,7 +386,8 @@ static void moom_callback(struct work_struct *ignored)
 {
 	const gfp_t gfp_mask = GFP_KERNEL;
 	struct oom_control oc = {
-		.zonelist = node_zonelist(first_memory_node, gfp_mask),
+		.zonelist = node_zonelist(first_node_state(N_MEMORY_COMMON),
+					  gfp_mask),
 		.nodemask = NULL,
 		.memcg = NULL,
 		.gfp_mask = gfp_mask,
-- 
2.53.1-Meta


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

* [PATCH 17/22] mm/vmscan: restrict reclaim to common memory
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
                   ` (15 preceding siblings ...)
  2026-11-14  7:09 ` [PATCH 16/22] tty/sysrq: use common memory for the manual OOM zonelist Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:22   ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 18/22] mm/compaction: restrict compaction " Gregory Price (Meta)
                   ` (4 subsequent siblings)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

Reclaim serves the ordinary allocation domain.

Restrict it to N_MEMORY_COMMON:
  - start kswapd only on common nodes, at boot or hotplug.
  - walk only common zones in direct reclaim and when throttling it,
    so no reclaimer waits on a node that has no kswapd.
  - walk common nodes when MGLRU registers an mm and when its debugfs
    interface iterates or validates nodes, using first_node_state()
    and next_node_state() in place of first_memory_node and
    next_memory_node.

The fallback zonelists hold only common memory, and all memory is common
at this point, so behavior is unchanged.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 mm/memory_hotplug.c |  3 ++-
 mm/vmscan.c         | 26 +++++++++++++-------------
 2 files changed, 15 insertions(+), 14 deletions(-)

diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 29ff2c46499b3..410a29732bae7 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1207,7 +1207,8 @@ int online_pages(unsigned long pfn, unsigned long nr_pages,
 	/* reinitialise watermarks and update pcp limits */
 	init_per_zone_wmark_min();
 
-	kswapd_run(nid);
+	if (node_state(nid, N_MEMORY_COMMON))
+		kswapd_run(nid);
 	kcompactd_run(nid);
 
 	if (node_arg.nid >= 0)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 061fbd168d982..bb7b1c50bfb78 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -2932,7 +2932,7 @@ void lru_gen_add_mm(struct mm_struct *mm)
 #endif
 	spin_lock(&mm_list->lock);
 
-	for_each_node_state(nid, N_MEMORY) {
+	for_each_node_state(nid, N_MEMORY_COMMON) {
 		struct lruvec *lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid));
 		struct lru_gen_mm_state *mm_state = get_mm_state(lruvec);
 
@@ -5562,7 +5562,7 @@ static void *lru_gen_seq_start(struct seq_file *m, loff_t *pos)
 	do {
 		int nid;
 
-		for_each_node_state(nid, N_MEMORY) {
+		for_each_node_state(nid, N_MEMORY_COMMON) {
 			if (!nr_to_skip--)
 				return mem_cgroup_lruvec(memcg, NODE_DATA(nid));
 		}
@@ -5587,13 +5587,13 @@ static void *lru_gen_seq_next(struct seq_file *m, void *v, loff_t *pos)
 
 	++*pos;
 
-	nid = next_memory_node(nid);
+	nid = next_node_state(nid, N_MEMORY_COMMON);
 	if (nid == MAX_NUMNODES) {
 		memcg = mem_cgroup_iter(NULL, memcg, NULL);
 		if (!memcg)
 			return NULL;
 
-		nid = first_memory_node;
+		nid = first_node_state(N_MEMORY_COMMON);
 	}
 
 	return mem_cgroup_lruvec(memcg, NODE_DATA(nid));
@@ -5665,7 +5665,7 @@ static int lru_gen_seq_show(struct seq_file *m, void *v)
 	DEFINE_MAX_SEQ(lruvec);
 	DEFINE_MIN_SEQ(lruvec);
 
-	if (nid == first_memory_node) {
+	if (nid == first_node_state(N_MEMORY_COMMON)) {
 		const char *path = memcg ? m->private : "";
 
 #ifdef CONFIG_MEMCG
@@ -5765,7 +5765,7 @@ static int run_cmd(char cmd, u64 memcg_id, int nid, unsigned long seq,
 	int err = -EINVAL;
 	struct mem_cgroup *memcg = NULL;
 
-	if (nid < 0 || nid >= MAX_NUMNODES || !node_state(nid, N_MEMORY))
+	if (nid < 0 || nid >= MAX_NUMNODES || !node_state(nid, N_MEMORY_COMMON))
 		return -EINVAL;
 
 	if (!mem_cgroup_disabled()) {
@@ -6492,8 +6492,8 @@ static void shrink_zones(struct zonelist *zonelist, struct scan_control *sc)
 		sc->reclaim_idx = gfp_zone(sc->gfp_mask);
 	}
 
-	for_each_zone_zonelist_nodemask(zone, z, zonelist,
-					sc->reclaim_idx, sc->nodemask) {
+	for_each_zone_node_state(zone, z, zonelist, sc->reclaim_idx,
+				 sc->nodemask, N_MEMORY_COMMON) {
 		/*
 		 * Take care memory controller reclaiming has small influence
 		 * to global LRU.
@@ -6604,8 +6604,8 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
 	} while (--sc->priority >= 0);
 
 	last_pgdat = NULL;
-	for_each_zone_zonelist_nodemask(zone, z, zonelist, sc->reclaim_idx,
-					sc->nodemask) {
+	for_each_zone_node_state(zone, z, zonelist, sc->reclaim_idx,
+				 sc->nodemask, N_MEMORY_COMMON) {
 		if (zone->zone_pgdat == last_pgdat)
 			continue;
 		last_pgdat = zone->zone_pgdat;
@@ -6756,8 +6756,8 @@ static bool throttle_direct_reclaim(gfp_t gfp_mask, struct zonelist *zonelist,
 	 * for remote pfmemalloc reserves and processes on different nodes
 	 * should make reasonable progress.
 	 */
-	for_each_zone_zonelist_nodemask(zone, z, zonelist,
-					gfp_zone(gfp_mask), nodemask) {
+	for_each_zone_node_state(zone, z, zonelist, gfp_zone(gfp_mask),
+				 nodemask, N_MEMORY_COMMON) {
 		if (zone_idx(zone) > ZONE_NORMAL)
 			continue;
 
@@ -7736,7 +7736,7 @@ static int __init kswapd_init(void)
 {
 	int nid;
 
-	for_each_node_state(nid, N_MEMORY)
+	for_each_node_state(nid, N_MEMORY_COMMON)
  		kswapd_run(nid);
 	register_sysctl_init("vm", vmscan_sysctl_table);
 	return 0;
-- 
2.53.1-Meta


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

* [PATCH 18/22] mm/compaction: restrict compaction to common memory
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
                   ` (16 preceding siblings ...)
  2026-11-14  7:09 ` [PATCH 17/22] mm/vmscan: restrict reclaim to common memory Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:19   ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 19/22] mm/migrate: restrict move_pages() destinations " Gregory Price (Meta)
                   ` (3 subsequent siblings)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

Compaction serves the ordinary allocation domain.

Restrict it to N_MEMORY_COMMON:
  - start kcompactd only on common nodes, at boot and hotplug.
  - walk only common zones in direct compaction and when deciding
    whether more reclaim could let compaction succeed.
  - compact only common nodes for /proc/sys/vm/compact_memory,
    and trigger proactive compaction only on common nodes.

The fallback zonelists hold only common memory, and all memory is common
at this point, so behavior is unchanged.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 mm/compaction.c     | 14 +++++++-------
 mm/memory_hotplug.c |  3 ++-
 2 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/mm/compaction.c b/mm/compaction.c
index 426563acf7acf..6a40e1a9ba78c 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -2496,8 +2496,8 @@ bool compaction_zonelist_suitable(struct alloc_context *ac, int order,
 	 * Make sure at least one zone would pass __compaction_suitable if we continue
 	 * retrying the reclaim.
 	 */
-	for_each_zone_zonelist_nodemask(zone, z, ac->zonelist,
-				ac->highest_zoneidx, ac->nodemask) {
+	for_each_zone_node_state(zone, z, ac->zonelist, ac->highest_zoneidx,
+				 ac->nodemask, N_MEMORY_COMMON) {
 		unsigned long available;
 
 		if (cpusets_enabled() && (alloc_flags & ALLOC_CPUSET) &&
@@ -2855,8 +2855,8 @@ enum compact_result try_to_compact_pages(gfp_t gfp_mask, unsigned int order,
 	trace_mm_compaction_try_to_compact_pages(order, gfp_mask, prio);
 
 	/* Compact each zone in the list */
-	for_each_zone_zonelist_nodemask(zone, z, ac->zonelist,
-					ac->highest_zoneidx, ac->nodemask) {
+	for_each_zone_node_state(zone, z, ac->zonelist, ac->highest_zoneidx,
+				 ac->nodemask, N_MEMORY_COMMON) {
 		enum compact_result status;
 
 		if (cpusets_enabled() &&
@@ -2973,7 +2973,7 @@ static int compact_nodes(void)
 	/* Flush pending updates to the LRU lists */
 	lru_add_drain_all();
 
-	for_each_online_node(nid) {
+	for_each_node_state(nid, N_MEMORY_COMMON) {
 		ret = compact_node(NODE_DATA(nid), false);
 		if (ret)
 			return ret;
@@ -2992,7 +2992,7 @@ static int compaction_proactiveness_sysctl_handler(const struct ctl_table *table
 		return rc;
 
 	if (write && sysctl_compaction_proactiveness) {
-		for_each_online_node(nid) {
+		for_each_node_state(nid, N_MEMORY_COMMON) {
 			pg_data_t *pgdat = NODE_DATA(nid);
 
 			if (pgdat->proactive_compact_trigger)
@@ -3366,7 +3366,7 @@ static int __init kcompactd_init(void)
 {
 	int nid;
 
-	for_each_node_state(nid, N_MEMORY)
+	for_each_node_state(nid, N_MEMORY_COMMON)
 		kcompactd_run(nid);
 	register_sysctl_init("vm", vm_compaction);
 	return 0;
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 410a29732bae7..c8ff60e6417ba 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1209,7 +1209,8 @@ int online_pages(unsigned long pfn, unsigned long nr_pages,
 
 	if (node_state(nid, N_MEMORY_COMMON))
 		kswapd_run(nid);
-	kcompactd_run(nid);
+	if (node_state(nid, N_MEMORY_COMMON))
+		kcompactd_run(nid);
 
 	if (node_arg.nid >= 0)
 		/* First memory added successfully. Notify consumers. */
-- 
2.53.1-Meta


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

* [PATCH 19/22] mm/migrate: restrict move_pages() destinations to common memory
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
                   ` (17 preceding siblings ...)
  2026-11-14  7:09 ` [PATCH 18/22] mm/compaction: restrict compaction " Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:20   ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 20/22] PM: hibernate: size the image from " Gregory Price (Meta)
                   ` (2 subsequent siblings)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

move_pages(2) places user memory on a node the caller picks. As with
memory policies, the destination should be general purpose memory.

Reject a destination outside N_MEMORY_COMMON with -ENODEV, exactly
the same as a node without memory is rejected today.

All memory is common at this point, so behavior is unchanged.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 mm/migrate.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mm/migrate.c b/mm/migrate.c
index 28a8d7734735b..fed910c9ba9ee 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -2400,7 +2400,7 @@ static int do_pages_move(struct mm_struct *mm, nodemask_t task_nodes,
 		err = -ENODEV;
 		if (node < 0 || node >= MAX_NUMNODES)
 			goto out_flush;
-		if (!node_state(node, N_MEMORY))
+		if (!node_state(node, N_MEMORY_COMMON))
 			goto out_flush;
 
 		err = -EACCES;
-- 
2.53.1-Meta


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

* [PATCH 20/22] PM: hibernate: size the image from common memory
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
                   ` (18 preceding siblings ...)
  2026-11-14  7:09 ` [PATCH 19/22] mm/migrate: restrict move_pages() destinations " Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:34     ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 21/22] mm/khugepaged: base the recommended min_free_kbytes on " Gregory Price (Meta)
  2026-11-14  7:09 ` [PATCH 22/22] nodemask: remove the now-unused first_memory_node/next_memory_node Gregory Price (Meta)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

hibernate_preallocate_memory() and enough_free_mem() count the free
pages of every populated zone as room for the hibernation image, but
the pages used to store the image can only come from common memory.

Count free pages only in zones on N_MEMORY_COMMON nodes.

The image metadata estimate is a separate sum.  The bitmaps track the
page frames, and every zone's frames are tracked (regardless of node),
so that sum still needs to be calculated for all populated zones.

Every node with memory is common, so hibernation is unchanged.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 kernel/power/snapshot.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
index d5dba0e50b2eb..8aeb086a3fd3d 100644
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -1866,8 +1866,10 @@ int hibernate_preallocate_memory(void)
 	saveable += save_highmem;
 	highmem = save_highmem;
 	size = 0;
-	for_each_populated_zone(zone) {
+	for_each_populated_zone(zone)
 		size += snapshot_additional_pages(zone);
+	/* Image pages come from ordinary allocations. */
+	for_each_populated_zone_state(zone, N_MEMORY_COMMON) {
 		if (is_highmem(zone))
 			highmem += zone_page_state(zone, NR_FREE_PAGES);
 		else
@@ -2011,9 +2013,10 @@ static int enough_free_mem(unsigned int nr_pages, unsigned int nr_highmem)
 	struct zone *zone;
 	unsigned int free = alloc_normal;
 
-	for_each_populated_zone(zone)
+	for_each_populated_zone_state(zone, N_MEMORY_COMMON) {
 		if (!is_highmem(zone))
 			free += zone_page_state(zone, NR_FREE_PAGES);
+	}
 
 	nr_pages += count_pages_for_highmem(nr_highmem);
 	pr_debug("Normal pages needed: %u + %u, available pages: %u\n",
-- 
2.53.1-Meta


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

* [PATCH 21/22] mm/khugepaged: base the recommended min_free_kbytes on common memory
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
                   ` (19 preceding siblings ...)
  2026-11-14  7:09 ` [PATCH 20/22] PM: hibernate: size the image from " Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:20   ` sashiko-bot
  2026-11-14  7:09 ` [PATCH 22/22] nodemask: remove the now-unused first_memory_node/next_memory_node Gregory Price (Meta)
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

set_recommended_min_free_kbytes() reserves two pageblocks per populated
zone to help THP allocations avoid fragmentation. The reserve serves
ordinary allocations, which use only common memory, so count only the
zones on N_MEMORY_COMMON nodes.

Every node with memory is common, so the recommendation is unchanged.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 mm/khugepaged.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index b4bd29f25c145..fac314b89a234 100644
--- a/mm/khugepaged.c
+++ b/mm/khugepaged.c
@@ -3149,7 +3149,7 @@ void set_recommended_min_free_kbytes(void)
 		goto update_wmarks;
 	}
 
-	for_each_populated_zone(zone) {
+	for_each_populated_zone_state(zone, N_MEMORY_COMMON) {
 		/*
 		 * We don't need to worry about fragmentation of
 		 * ZONE_MOVABLE since it only has movable pages.
-- 
2.53.1-Meta


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

* [PATCH 22/22] nodemask: remove the now-unused first_memory_node/next_memory_node
  2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
                   ` (20 preceding siblings ...)
  2026-11-14  7:09 ` [PATCH 21/22] mm/khugepaged: base the recommended min_free_kbytes on " Gregory Price (Meta)
@ 2026-11-14  7:09 ` Gregory Price (Meta)
  2026-11-14  7:19   ` sashiko-bot
  21 siblings, 1 reply; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:09 UTC (permalink / raw)
  To: linux-mm
  Cc: linux-kernel, kernel-team, linux-doc, driver-core, linux-serial,
	linux-pm, damon, linux-cxl, akpm, david, liam, vbabka, rppt,
	surenb, mhocko, corbet, skhan, rdunlap, gregkh, rafael, dakr,
	johan, jirislaby, kasong, qi.zheng, shakeel.butt, baohua,
	axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou, gourry,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

Both helpers hard-code N_MEMORY, which says only that memory exists.

Every caller now names the state it means, leaving no users of either
helper. Remove them so new code has to pick a node state explicitly
rather than silently getting N_MEMORY.

Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>
---
 include/linux/nodemask.h | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/include/linux/nodemask.h b/include/linux/nodemask.h
index 7ace610bdd654..eea0ff7601303 100644
--- a/include/linux/nodemask.h
+++ b/include/linux/nodemask.h
@@ -457,15 +457,10 @@ static __always_inline unsigned int next_node_state(int nid, enum node_states st
 }
 
 #define first_online_node	first_node_state(N_ONLINE)
-#define first_memory_node	first_node_state(N_MEMORY)
 static __always_inline unsigned int next_online_node(int nid)
 {
 	return next_node_state(nid, N_ONLINE);
 }
-static __always_inline unsigned int next_memory_node(int nid)
-{
-	return next_node_state(nid, N_MEMORY);
-}
 
 static __always_inline void node_set_online(int nid)
 {
@@ -509,9 +504,7 @@ static __always_inline unsigned int next_node_state(int nid, enum node_states st
 }
 
 #define first_online_node	0
-#define first_memory_node	0
 #define next_online_node(nid)	(MAX_NUMNODES)
-#define next_memory_node(nid)	(MAX_NUMNODES)
 
 #define node_set_online(node)	   node_set_state((node), N_ONLINE)
 #define node_set_offline(node)	   node_clear_state((node), N_ONLINE)
-- 
2.53.1-Meta


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

* Re: [PATCH 06/22] mm: base hash distribution on common memory nodes
  2026-11-14  7:09 ` [PATCH 06/22] mm: base hash distribution on common memory nodes Gregory Price (Meta)
@ 2026-11-14  7:15   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:15 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> hashdist is useful when large kernel hash tables can be allocated across
> multiple NUMA nodes. The tables come from ordinary allocations, which
> only N_MEMORY_COMMON nodes serve.
> 
> Count N_MEMORY_COMMON nodes when applying the single-node optimization.
> 
> All memory is common as of now, so behavior is unchanged.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=6


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

* Re: [PATCH 12/22] mm/damon: restrict DAMON-native folio operations to common memory
  2026-11-14  7:09 ` [PATCH 12/22] mm/damon: restrict DAMON-native folio operations to " Gregory Price (Meta)
@ 2026-11-14  7:15   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:15 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> DAMON acquires folios to age them, check their accesses, and apply LRU
> operations and migration. Treat these as general-purpose MM services
> and restrict them to N_MEMORY_COMMON.
> 
> Memory outside N_MEMORY_COMMON can still hold ordinary LRU folios, so
> it would pass the checks that keep ZONE_DEVICE memory out of these
> operations.
> 
> Treat these folios like ZONE_DEVICE memory.
> 
> DAMON cannot acquire such a folio, so it never ages one and virtual
> address monitoring reports it as accessed, and DAMOS cannot isolate
> one for migration.
> 
> Reject migration destinations outside N_MEMORY_COMMON.
> 
> All memory is common at this point, so behavior is unchanged.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=12


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

* Re: [PATCH 07/22] mm/memory_hotplug: migrate offlined pages to common memory
  2026-11-14  7:09 ` [PATCH 07/22] mm/memory_hotplug: migrate offlined pages to common memory Gregory Price (Meta)
@ 2026-11-14  7:16   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:16 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> do_migrate_range() / alloc_migration_target() is only capable of
> allocating memory on N_MEMORY_COMMON nodes. The destination nodemask
> presently starts with N_MEMORY and removes the source-node.
> 
> In the current code, if the destination mask becomes empty, the source
> node is restored to allow migration during partial offlining.
> 
> Consider this configuration:
>   Node0 = N_MEMORY_COMMON
>   Node1 = N_MEMORY, not N_MEMORY_COMMON
> 
> Node 1 keeps the destination mask non-empty, despite being unreachable.
> This leaves migration with no usable destination because the source
> node is not an eligible allocation target.
> 
> Restrict the hot-unplug migration target-list to N_MEMORY_COMMON so it
> reflects the nodes the allocator can reach.
> 
> All memory is common at this point - behavior is unchanged.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=7


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

* Re: [PATCH 08/22] mm/memory-tiers: restrict demotion topology to common memory
  2026-11-14  7:09 ` [PATCH 08/22] mm/memory-tiers: restrict demotion topology " Gregory Price (Meta)
@ 2026-11-14  7:16   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:16 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> Memory outside N_MEMORY_COMMON is outside the ordinary allocation pool
> and cannot be a generic demotion source or destination.
> 
> Build demotion paths and the default DRAM performance reference from
> N_MEMORY_COMMON rather than every node that has memory, and do not
> assign other nodes to a memory tier through the generic hotplug path.
> 
> Removing a node's memory already resets its demotion targets, so a node
> that unplugs and returns outside N_MEMORY_COMMON keeps none.
> 
> All memory nodes currently carry N_MEMORY_COMMON, behavior is unchanged.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=8


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

* Re: [PATCH 11/22] mm/mempolicy: don't NUMA-hint folios outside common memory
  2026-11-14  7:09 ` [PATCH 11/22] mm/mempolicy: don't NUMA-hint folios outside " Gregory Price (Meta)
@ 2026-11-14  7:16   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:16 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> The NUMA balancing scanner installs PROT_NONE only on folios that
> balancing may migrate.
> 
> folio_can_map_prot_numa() already skips ZONE_DEVICE folios. Skip folios
> outside the common memory pool (N_MEMORY_COMMON) as well. Do not inject
> hint faults on those folios or migrate them.
> 
> Convert the zone_device check to a common memory check (which includes
> the zone device check explicitly).
> 
> All memory is common at this point, so behavior is unchanged.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=11


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

* Re: [PATCH 04/22] mm/page_alloc: build zonelists from common memory
  2026-11-14  7:09 ` [PATCH 04/22] mm/page_alloc: build zonelists from common memory Gregory Price (Meta)
@ 2026-11-14  7:16   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:16 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> Zonelists decide which nodes an allocation can reach.
> 
> The fallback lists hold every node in N_MEMORY, and a node's
> __GFP_THISNODE list holds its own zones whatever the node's state.
> 
> Ordinary allocations should reach only common (general purpose) memory.
> 
> Build the fallback zonelists from N_MEMORY_COMMON, and __GFP_THISNODE
> zonelists only for a common node.
> 
> The build_local_zonelist() refactor lets use reuse the code to build
> a local-only zonelist for nodes outside the N_MEMORY_COMMON set later.
> 
> Every node with memory is common, and a memoryless node has no zones to
> list, so the zonelists are unchanged.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=4


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

* Re: [PATCH 03/22] mm: introduce the common-memory node state
  2026-11-14  7:09 ` [PATCH 03/22] mm: introduce the common-memory node state Gregory Price (Meta)
@ 2026-11-14  7:17   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:17 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> N_MEMORY is overloaded with two meanings:
>   1) a node has memory.
>   2) a node's memory is reachable via ordinary allocation or
>      an arbitrary MM service may use that memory.
> 
> Allow N_MEMORY to mean #1, and introduce N_MEMORY_COMMON to mean #2.
> 
> N_MEMORY_COMMON nodes contain "common memory" capable of being managed
> arbitrarily by the kernel MM based solely on zone membership. Following
> commits convert relevant N_MEMORY users to N_MEMORY_COMMON.
> 
> Add folio_is_common_memory() and page_is_common_memory() for MM services
> that walk folios. Following commits convert folio_is_zone_device() and
> page_is_zone_device() callers accordingly.
> 
> Expose the state via `has_common_memory` so userspace can identify
> the memory available to ordinary allocations and common MM services.
> 
> Add has_memory to the docs while we're here.
> 
> Every node with memory is common, so N_MEMORY_COMMON equals N_MEMORY
> and there is no behavioral change.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=3


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

* Re: [PATCH 13/22] mm/hugetlb: restrict pool growth to common memory
  2026-11-14  7:09 ` [PATCH 13/22] mm/hugetlb: restrict pool growth " Gregory Price (Meta)
@ 2026-11-14  7:18   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:18 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> Fresh HugeTLB pool folios come from ordinary allocations, which only
> N_MEMORY_COMMON nodes serve.
> 
> Restrict HugeTLB allocations and pool-growth to N_MEMORY_COMMON nodes.
> 
> Keep the original allowed mask for pool shrinking and demotion so
> existing pool folios can still be found on any online-memory node.
> 
> The result of this change is that hugepage allocations will fail on
> nodes without N_MEMORY_COMMON.  This restriction can be loosened later
> when/if it grows a user.
> 
> All memory is common at this point, so behavior is unchanged.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=13


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

* Re: [PATCH 14/22] mm/page_alloc: count common memory nodes for kswapd recovery
  2026-11-14  7:09 ` [PATCH 14/22] mm/page_alloc: count common memory nodes for kswapd recovery Gregory Price (Meta)
@ 2026-11-14  7:18   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:18 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> When a zone recovers above its high watermark, free_frozen_page_commit()
> clears a hopeless kswapd state if more than one memory node exists,
> since allocations could then have fallen back to another node without
> triggering direct reclaim here.
> 
> Only common memory provide fallbacks, so count N_MEMORY_COMMON nodes.
> 
> All memory is common at this point, so behavior is unchanged.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=14


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

* Re: [PATCH 05/22] mm/mempolicy: keep interleave and tmpfs mount policies on common memory
  2026-11-14  7:09 ` [PATCH 05/22] mm/mempolicy: keep interleave and tmpfs mount policies on " Gregory Price (Meta)
@ 2026-11-14  7:18   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:18 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> Use N_MEMORY_COMMON for boot mempolicy calculations and their per-node
> sysfs entries (weighted interleave weights). 
> 
> Restrict the node set mpol_set_nodemask() applies to set_mempolicy()
> and mbind() masks to N_MEMORY_COMMON nodes.
> 
> Later when an N_MEMORY node is omitted frmo the N_MEMORY_COMMON set,
> they will be stripped from mempolicy nodemasks as they're not eligible
> (similar to memoryless nodes today).
> 
> All memory is common at this point, no behavior change at this point.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=5


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

* Re: [PATCH 15/22] mm/oom_kill: compare OOM constraints against common memory
  2026-11-14  7:09 ` [PATCH 15/22] mm/oom_kill: compare OOM constraints against common memory Gregory Price (Meta)
@ 2026-11-14  7:19   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:19 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> Ordinary allocations reach only common-memory zonelists. Compare policy
> and cpuset constraints against N_MEMORY_COMMON, so nodes outside it
> neither make an otherwise global policy appear constrained nor inflate
> the constrained capacity.
> 
> All memory is common at this point, so behavior is unchanged.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=15


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

* Re: [PATCH 22/22] nodemask: remove the now-unused first_memory_node/next_memory_node
  2026-11-14  7:09 ` [PATCH 22/22] nodemask: remove the now-unused first_memory_node/next_memory_node Gregory Price (Meta)
@ 2026-11-14  7:19   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:19 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> Both helpers hard-code N_MEMORY, which says only that memory exists.
> 
> Every caller now names the state it means, leaving no users of either
> helper. Remove them so new code has to pick a node state explicitly
> rather than silently getting N_MEMORY.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=22


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

* Re: [PATCH 16/22] tty/sysrq: use common memory for the manual OOM zonelist
  2026-11-14  7:09 ` [PATCH 16/22] tty/sysrq: use common memory for the manual OOM zonelist Gregory Price (Meta)
@ 2026-11-14  7:19   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:19 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> SysRq-f constructs an OOM request with GFP_KERNEL and supplies a
> zonelist for allocation-constraint checks. The request represents an
> ordinary allocation, whose fallback domain contains common memory rather
> than every node that happens to have memory.
> 
> Select the zonelist from the first common-memory node instead of the
> first memory node. All memory nodes are common at this point, so
> behavior is unchanged.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=16


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

* Re: [PATCH 18/22] mm/compaction: restrict compaction to common memory
  2026-11-14  7:09 ` [PATCH 18/22] mm/compaction: restrict compaction " Gregory Price (Meta)
@ 2026-11-14  7:19   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:19 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> Compaction serves the ordinary allocation domain.
> 
> Restrict it to N_MEMORY_COMMON:
>   - start kcompactd only on common nodes, at boot and hotplug.
>   - walk only common zones in direct compaction and when deciding
>     whether more reclaim could let compaction succeed.
>   - compact only common nodes for /proc/sys/vm/compact_memory,
>     and trigger proactive compaction only on common nodes.
> 
> The fallback zonelists hold only common memory, and all memory is common
> at this point, so behavior is unchanged.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=18


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

* Re: [PATCH 01/22] mm: add node-state-filtered zone and node iterators
  2026-11-14  7:09 ` [PATCH 01/22] mm: add node-state-filtered zone and node iterators Gregory Price (Meta)
@ 2026-11-14  7:19   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:19 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> Add node-state-filter helper functions/macros so MM walkers can select
> their node eligibility explicitly.
> 
> Add nodemask intersection and nodemask/state iterators using the bitmap
> intersection helpers, without allocating a temporary nodemask.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=1


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

* Re: [PATCH 19/22] mm/migrate: restrict move_pages() destinations to common memory
  2026-11-14  7:09 ` [PATCH 19/22] mm/migrate: restrict move_pages() destinations " Gregory Price (Meta)
@ 2026-11-14  7:20   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:20 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> move_pages(2) places user memory on a node the caller picks. As with
> memory policies, the destination should be general purpose memory.
> 
> Reject a destination outside N_MEMORY_COMMON with -ENODEV, exactly
> the same as a node without memory is rejected today.
> 
> All memory is common at this point, so behavior is unchanged.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=19


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

* Re: [PATCH 21/22] mm/khugepaged: base the recommended min_free_kbytes on common memory
  2026-11-14  7:09 ` [PATCH 21/22] mm/khugepaged: base the recommended min_free_kbytes on " Gregory Price (Meta)
@ 2026-11-14  7:20   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:20 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> set_recommended_min_free_kbytes() reserves two pageblocks per populated
> zone to help THP allocations avoid fragmentation. The reserve serves
> ordinary allocations, which use only common memory, so count only the
> zones on N_MEMORY_COMMON nodes.
> 
> Every node with memory is common, so the recommendation is unchanged.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=21


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

* Re: [PATCH 02/22] mm: publish and clear node memory states via helpers
  2026-11-14  7:09 ` [PATCH 02/22] mm: publish and clear node memory states via helpers Gregory Price (Meta)
@ 2026-11-14  7:20   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:20 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> N_MEMORY, N_NORMAL_MEMORY and N_HIGH_MEMORY are set separately in
> free_area_init(), check_for_memory() and online_pages(), and cleared
> in offline_pages().
> 
> Centralize those updates in node_set/clear_memory_state().
> 
> Behavior is unchanged.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=2


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

* Re: [PATCH 09/22] mm/ksm: restrict merging to common memory
  2026-11-14  7:09 ` [PATCH 09/22] mm/ksm: restrict merging " Gregory Price (Meta)
@ 2026-11-14  7:20   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:20 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> KSM is an opportunistic MM service and should leave memory outside
> the ordinary memory pool alone - as it already does for ZONE_DEVICE
> folios at each acquisition point.
> 
> Use folio_is_common_memory() at those same sites, which still rejects
> ZONE_DEVICE folios.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=9


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

* Re: [PATCH 10/22] mm/khugepaged: restrict collapse to common memory
  2026-11-14  7:09 ` [PATCH 10/22] mm/khugepaged: restrict collapse " Gregory Price (Meta)
@ 2026-11-14  7:21     ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:21 UTC (permalink / raw)
  To: Gregory Price (Meta), linux-mm
  Cc: kernel-team, linux-serial, damon, linux-cxl, akpm, david, liam,
	vbabka, rppt, surenb, mhocko, corbet, skhan, rdunlap, gregkh,
	rafael, dakr, johan, jirislaby, kasong, qi.zheng, shakeel.butt,
	baohua, axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] mm/khugepaged: missing folio_is_common_memory() check in
  collapse_file()
--

commit 5cdd71b37e4c9e4d2c7bf61d7c51d7ba89a07fb5
Author: Gregory Price (Meta) <gourry@gourry.net>

mm/khugepaged: restrict collapse to common memory

This commit replaces the zone_device checks in anonymous THP scan and
isolation paths with page_is_common_memory() and adds a
folio_is_common_memory() check to collapse_scan_file().

> diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> index 4bbe319ef86fa..b4bd29f25c145 100644
> --- a/mm/khugepaged.c
> +++ b/mm/khugepaged.c

[ ... ]

> @@ -2741,6 +2741,12 @@ static enum scan_result collapse_scan_file(struct mm_struct *mm,
>  			break;
>  		}
>  
> +		if (unlikely(!folio_is_common_memory(folio))) {
> +			result = SCAN_PAGE_NULL;
> +			failed_pfn = folio_pfn(folio);
> +			folio_put(folio);
> +			break;
> +		}
>  		node = folio_nid(folio);
>  		if (collapse_scan_abort(node, cc)) {
>  			result = SCAN_SCAN_ABORT;

[Severity: Medium]
Should collapse_file() also check folio_is_common_memory(folio) after
locking each folio, matching __collapse_huge_page_isolate()?

collapse_scan_file() drops its folio reference on each iteration and
skips missing page-cache indices and swapped-out shmem entries, and
collapse_single_pmd() drops mmap_read_lock() before calling
collapse_pmd() -> collapse_file().  Folios that are faulted into holes,
migrated, or instantiated by shmem_get_folio() or
page_cache_sync_readahead() on a node outside N_MEMORY_COMMON after the
pre-scan will still be isolated and collapsed by collapse_file().

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=10

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

* Re: [PATCH 10/22] mm/khugepaged: restrict collapse to common memory
@ 2026-11-14  7:21     ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:21 UTC (permalink / raw)
  To: Gregory Price (Meta), linux-mm
  Cc: kernel-team, linux-serial, damon, linux-cxl, akpm, david, liam,
	vbabka, rppt, surenb, mhocko, corbet, skhan, rdunlap, gregkh,
	rafael, dakr, johan, jirislaby, kasong, qi.zheng, shakeel.butt,
	baohua, axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] mm/khugepaged: missing folio_is_common_memory() check in
  collapse_file()
--

commit 5cdd71b37e4c9e4d2c7bf61d7c51d7ba89a07fb5
Author: Gregory Price (Meta) <gourry@gourry.net>

mm/khugepaged: restrict collapse to common memory

This commit replaces the zone_device checks in anonymous THP scan and
isolation paths with page_is_common_memory() and adds a
folio_is_common_memory() check to collapse_scan_file().

> diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> index 4bbe319ef86fa..b4bd29f25c145 100644
> --- a/mm/khugepaged.c
> +++ b/mm/khugepaged.c

[ ... ]

> @@ -2741,6 +2741,12 @@ static enum scan_result collapse_scan_file(struct mm_struct *mm,
>  			break;
>  		}
>  
> +		if (unlikely(!folio_is_common_memory(folio))) {
> +			result = SCAN_PAGE_NULL;
> +			failed_pfn = folio_pfn(folio);
> +			folio_put(folio);
> +			break;
> +		}
>  		node = folio_nid(folio);
>  		if (collapse_scan_abort(node, cc)) {
>  			result = SCAN_SCAN_ABORT;

[Severity: Medium]
Should collapse_file() also check folio_is_common_memory(folio) after
locking each folio, matching __collapse_huge_page_isolate()?

collapse_scan_file() drops its folio reference on each iteration and
skips missing page-cache indices and swapped-out shmem entries, and
collapse_single_pmd() drops mmap_read_lock() before calling
collapse_pmd() -> collapse_file().  Folios that are faulted into holes,
migrated, or instantiated by shmem_get_folio() or
page_cache_sync_readahead() on a node outside N_MEMORY_COMMON after the
pre-scan will still be isolated and collapsed by collapse_file().

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=10


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

* Re: [PATCH 17/22] mm/vmscan: restrict reclaim to common memory
  2026-11-14  7:09 ` [PATCH 17/22] mm/vmscan: restrict reclaim to common memory Gregory Price (Meta)
@ 2026-11-14  7:22   ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:22 UTC (permalink / raw)
  To: Gregory Price (Meta); +Cc: linux-serial, damon

> Reclaim serves the ordinary allocation domain.
> 
> Restrict it to N_MEMORY_COMMON:
>   - start kswapd only on common nodes, at boot or hotplug.
>   - walk only common zones in direct reclaim and when throttling it,
>     so no reclaimer waits on a node that has no kswapd.
>   - walk common nodes when MGLRU registers an mm and when its debugfs
>     interface iterates or validates nodes, using first_node_state()
>     and next_node_state() in place of first_memory_node and
>     next_memory_node.
> 
> The fallback zonelists hold only common memory, and all memory is common
> at this point, so behavior is unchanged.
> 
> Signed-off-by: Gregory Price (Meta) <gourry@gourry.net>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=17


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

* Re: [PATCH 20/22] PM: hibernate: size the image from common memory
  2026-11-14  7:09 ` [PATCH 20/22] PM: hibernate: size the image from " Gregory Price (Meta)
@ 2026-11-14  7:34     ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:34 UTC (permalink / raw)
  To: Gregory Price (Meta), linux-mm
  Cc: kernel-team, linux-serial, damon, linux-cxl, akpm, david, liam,
	vbabka, rppt, surenb, mhocko, corbet, skhan, rdunlap, gregkh,
	rafael, dakr, johan, jirislaby, kasong, qi.zheng, shakeel.butt,
	baohua, axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] PM: hibernate: count_free_highmem_pages() ignores
  N_MEMORY_COMMON
--

commit 7cc6e673516327174024379e9585052d08c1578f
Author: Gregory Price (Meta) <gourry@gourry.net>

PM: hibernate: size the image from common memory

This commit restricts free page accounting in hibernate_preallocate_memory()
and enough_free_mem() to zones on N_MEMORY_COMMON nodes while keeping the
metadata estimate across all populated zones.

> diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
> index d5dba0e50b2eb..8aeb086a3fd3d 100644
> --- a/kernel/power/snapshot.c
> +++ b/kernel/power/snapshot.c

[ ... ]

> @@ -2011,9 +2013,10 @@ static int enough_free_mem(unsigned int nr_pages, unsigned int nr_highmem)
>  	struct zone *zone;
>  	unsigned int free = alloc_normal;
>  
> -	for_each_populated_zone(zone)
> +	for_each_populated_zone_state(zone, N_MEMORY_COMMON) {
>  		if (!is_highmem(zone))
>  			free += zone_page_state(zone, NR_FREE_PAGES);
> +	}
>  
>  	nr_pages += count_pages_for_highmem(nr_highmem);
>  	pr_debug("Normal pages needed: %u + %u, available pages: %u\n",

[Severity: Medium]
Should count_free_highmem_pages() also iterate over N_MEMORY_COMMON zones?

On CONFIG_HIGHMEM systems, enough_free_mem() calls count_pages_for_highmem(),
which calls count_free_highmem_pages():

kernel/power/snapshot.c:count_free_highmem_pages() {
	struct zone *zone;
	unsigned int cnt = 0;

	for_each_populated_zone(zone)
		if (is_highmem(zone))
			cnt += zone_page_state(zone, NR_FREE_PAGES);

	return cnt;
}

Because count_free_highmem_pages() still sums all populated zones,
enough_free_mem() continues to count free highmem pages on non-common nodes as
available room for the image, undercounting the normal pages needed to back
highmem copies.

In addition, alloc_highmem_pages() and prepare_highmem_image() use
count_free_highmem_pages() to bound loops that call alloc_image_page() or
alloc_page() with __GFP_HIGHMEM and pass the returned page directly to
page_to_pfn() or swsusp_page_is_free() without a NULL check, which can
dereference NULL when highmem on N_MEMORY_COMMON nodes is exhausted while
non-common nodes still have free highmem pages.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=20

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

* Re: [PATCH 20/22] PM: hibernate: size the image from common memory
@ 2026-11-14  7:34     ` sashiko-bot
  0 siblings, 0 replies; 48+ messages in thread
From: sashiko-bot @ 2026-11-14  7:34 UTC (permalink / raw)
  To: Gregory Price (Meta), linux-mm
  Cc: kernel-team, linux-serial, damon, linux-cxl, akpm, david, liam,
	vbabka, rppt, surenb, mhocko, corbet, skhan, rdunlap, gregkh,
	rafael, dakr, johan, jirislaby, kasong, qi.zheng, shakeel.butt,
	baohua, axelrasmussen, yuanchu, weixugc, baoquan.he, baolin.wang,
	yury.norov, linux, pavel, lenb, hannes, brendan.jackman, ziy, sj,
	muchun.song, osalvador, nico.pache, ryan.roberts, dev.jain,
	lance.yang, usama.arif, kas, xu.xin, chengming.zhou,
	joshua.hahnjy, rakie.kim, ying.huang, matthew.brost, byungchul,
	apopple, rientjes

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] PM: hibernate: count_free_highmem_pages() ignores
  N_MEMORY_COMMON
--

commit 7cc6e673516327174024379e9585052d08c1578f
Author: Gregory Price (Meta) <gourry@gourry.net>

PM: hibernate: size the image from common memory

This commit restricts free page accounting in hibernate_preallocate_memory()
and enough_free_mem() to zones on N_MEMORY_COMMON nodes while keeping the
metadata estimate across all populated zones.

> diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
> index d5dba0e50b2eb..8aeb086a3fd3d 100644
> --- a/kernel/power/snapshot.c
> +++ b/kernel/power/snapshot.c

[ ... ]

> @@ -2011,9 +2013,10 @@ static int enough_free_mem(unsigned int nr_pages, unsigned int nr_highmem)
>  	struct zone *zone;
>  	unsigned int free = alloc_normal;
>  
> -	for_each_populated_zone(zone)
> +	for_each_populated_zone_state(zone, N_MEMORY_COMMON) {
>  		if (!is_highmem(zone))
>  			free += zone_page_state(zone, NR_FREE_PAGES);
> +	}
>  
>  	nr_pages += count_pages_for_highmem(nr_highmem);
>  	pr_debug("Normal pages needed: %u + %u, available pages: %u\n",

[Severity: Medium]
Should count_free_highmem_pages() also iterate over N_MEMORY_COMMON zones?

On CONFIG_HIGHMEM systems, enough_free_mem() calls count_pages_for_highmem(),
which calls count_free_highmem_pages():

kernel/power/snapshot.c:count_free_highmem_pages() {
	struct zone *zone;
	unsigned int cnt = 0;

	for_each_populated_zone(zone)
		if (is_highmem(zone))
			cnt += zone_page_state(zone, NR_FREE_PAGES);

	return cnt;
}

Because count_free_highmem_pages() still sums all populated zones,
enough_free_mem() continues to count free highmem pages on non-common nodes as
available room for the image, undercounting the normal pages needed to back
highmem copies.

In addition, alloc_highmem_pages() and prepare_highmem_image() use
count_free_highmem_pages() to bound loops that call alloc_image_page() or
alloc_page() with __GFP_HIGHMEM and pass the returned page directly to
page_to_pfn() or swsusp_page_is_free() without a NULL check, which can
dereference NULL when highmem on N_MEMORY_COMMON nodes is exhausted while
non-common nodes still have free highmem pages.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114070959.3490008-1-gourry@gourry.net?part=20


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

* Re: [PATCH 10/22] mm/khugepaged: restrict collapse to common memory
  2026-11-14  7:21     ` sashiko-bot
  (?)
@ 2026-11-14  7:35     ` Gregory Price (Meta)
  -1 siblings, 0 replies; 48+ messages in thread
From: Gregory Price (Meta) @ 2026-11-14  7:35 UTC (permalink / raw)
  To: sashiko-reviews
  Cc: Gregory Price (Meta), linux-mm, kernel-team, linux-serial, damon,
	linux-cxl, akpm, david, liam, vbabka, rppt, surenb, mhocko,
	corbet, skhan, rdunlap, gregkh, rafael, dakr, johan, jirislaby,
	kasong, qi.zheng, shakeel.butt, baohua, axelrasmussen, yuanchu,
	weixugc, baoquan.he, baolin.wang, yury.norov, linux, pavel, lenb,
	hannes, brendan.jackman, ziy, sj, muchun.song, osalvador,
	nico.pache, ryan.roberts, dev.jain, lance.yang, usama.arif, kas,
	xu.xin, chengming.zhou, joshua.hahnjy, rakie.kim, ying.huang,
	matthew.brost, byungchul, apopple, rientjes

On 2026-11-14 07:21 +0000, sashiko-bot@kernel.org wrote:
> > diff --git a/mm/khugepaged.c b/mm/khugepaged.c
> > index 4bbe319ef86fa..b4bd29f25c145 100644
> > --- a/mm/khugepaged.c
> > +++ b/mm/khugepaged.c
> 
> [ ... ]
> 
> > @@ -2741,6 +2741,12 @@ static enum scan_result collapse_scan_file(struct mm_struct *mm,
> >  			break;
> >  		}
> >  
> > +		if (unlikely(!folio_is_common_memory(folio))) {
> > +			result = SCAN_PAGE_NULL;
> > +			failed_pfn = folio_pfn(folio);
> > +			folio_put(folio);
> > +			break;
> > +		}
> >  		node = folio_nid(folio);
> >  		if (collapse_scan_abort(node, cc)) {
> >  			result = SCAN_SCAN_ABORT;
> 
> [Severity: Medium]
> Should collapse_file() also check folio_is_common_memory(folio) after
> locking each folio, matching __collapse_huge_page_isolate()?
> 
> collapse_scan_file() drops its folio reference on each iteration and
> skips missing page-cache indices and swapped-out shmem entries, and
> collapse_single_pmd() drops mmap_read_lock() before calling
> collapse_pmd() -> collapse_file().  Folios that are faulted into holes,
> migrated, or instantiated by shmem_get_folio() or
> page_cache_sync_readahead() on a node outside N_MEMORY_COMMON after the
> pre-scan will still be isolated and collapsed by collapse_file().


Ah, this is technically a pre-mature report, as this is an unreachable
condition at this state in the series.

I do already have this handled in a phase 3 commit where private memory
becomes migratable, but i suppose this check could be moved ahead just
for cleanliness.

--

diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index fac314b89a234..7a69d6e9b42bb 100644
--- a/mm/khugepaged.c
+++ b/mm/khugepaged.c
@@ -2390,6 +2390,12 @@ static enum scan_result collapse_file(struct mm_struct *mm, unsigned long addr,
                        goto out_unlock;
                }

+               /* Migration may have moved the folio since the scan. */
+               if (unlikely(!folio_is_common_memory(folio))) {
+                       result = SCAN_PAGE_NULL;
+                       goto out_unlock;
+               }
+
                if (!is_shmem && (folio_test_dirty(folio) ||
                                  folio_test_writeback(folio))) {
                        /*

-- 
~Gregory


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

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

Thread overview: 48+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-14  7:09 [PATCH 00/22] mm: introduce N_MEMORY_COMMON Gregory Price (Meta)
2026-11-14  7:09 ` [PATCH 01/22] mm: add node-state-filtered zone and node iterators Gregory Price (Meta)
2026-11-14  7:19   ` sashiko-bot
2026-11-14  7:09 ` [PATCH 02/22] mm: publish and clear node memory states via helpers Gregory Price (Meta)
2026-11-14  7:20   ` sashiko-bot
2026-11-14  7:09 ` [PATCH 03/22] mm: introduce the common-memory node state Gregory Price (Meta)
2026-11-14  7:17   ` sashiko-bot
2026-11-14  7:09 ` [PATCH 04/22] mm/page_alloc: build zonelists from common memory Gregory Price (Meta)
2026-11-14  7:16   ` sashiko-bot
2026-11-14  7:09 ` [PATCH 05/22] mm/mempolicy: keep interleave and tmpfs mount policies on " Gregory Price (Meta)
2026-11-14  7:18   ` sashiko-bot
2026-11-14  7:09 ` [PATCH 06/22] mm: base hash distribution on common memory nodes Gregory Price (Meta)
2026-11-14  7:15   ` sashiko-bot
2026-11-14  7:09 ` [PATCH 07/22] mm/memory_hotplug: migrate offlined pages to common memory Gregory Price (Meta)
2026-11-14  7:16   ` sashiko-bot
2026-11-14  7:09 ` [PATCH 08/22] mm/memory-tiers: restrict demotion topology " Gregory Price (Meta)
2026-11-14  7:16   ` sashiko-bot
2026-11-14  7:09 ` [PATCH 09/22] mm/ksm: restrict merging " Gregory Price (Meta)
2026-11-14  7:20   ` sashiko-bot
2026-11-14  7:09 ` [PATCH 10/22] mm/khugepaged: restrict collapse " Gregory Price (Meta)
2026-11-14  7:21   ` sashiko-bot
2026-11-14  7:21     ` sashiko-bot
2026-11-14  7:35     ` Gregory Price (Meta)
2026-11-14  7:09 ` [PATCH 11/22] mm/mempolicy: don't NUMA-hint folios outside " Gregory Price (Meta)
2026-11-14  7:16   ` sashiko-bot
2026-11-14  7:09 ` [PATCH 12/22] mm/damon: restrict DAMON-native folio operations to " Gregory Price (Meta)
2026-11-14  7:15   ` sashiko-bot
2026-11-14  7:09 ` [PATCH 13/22] mm/hugetlb: restrict pool growth " Gregory Price (Meta)
2026-11-14  7:18   ` sashiko-bot
2026-11-14  7:09 ` [PATCH 14/22] mm/page_alloc: count common memory nodes for kswapd recovery Gregory Price (Meta)
2026-11-14  7:18   ` sashiko-bot
2026-11-14  7:09 ` [PATCH 15/22] mm/oom_kill: compare OOM constraints against common memory Gregory Price (Meta)
2026-11-14  7:19   ` sashiko-bot
2026-11-14  7:09 ` [PATCH 16/22] tty/sysrq: use common memory for the manual OOM zonelist Gregory Price (Meta)
2026-11-14  7:19   ` sashiko-bot
2026-11-14  7:09 ` [PATCH 17/22] mm/vmscan: restrict reclaim to common memory Gregory Price (Meta)
2026-11-14  7:22   ` sashiko-bot
2026-11-14  7:09 ` [PATCH 18/22] mm/compaction: restrict compaction " Gregory Price (Meta)
2026-11-14  7:19   ` sashiko-bot
2026-11-14  7:09 ` [PATCH 19/22] mm/migrate: restrict move_pages() destinations " Gregory Price (Meta)
2026-11-14  7:20   ` sashiko-bot
2026-11-14  7:09 ` [PATCH 20/22] PM: hibernate: size the image from " Gregory Price (Meta)
2026-11-14  7:34   ` sashiko-bot
2026-11-14  7:34     ` sashiko-bot
2026-11-14  7:09 ` [PATCH 21/22] mm/khugepaged: base the recommended min_free_kbytes on " Gregory Price (Meta)
2026-11-14  7:20   ` sashiko-bot
2026-11-14  7:09 ` [PATCH 22/22] nodemask: remove the now-unused first_memory_node/next_memory_node Gregory Price (Meta)
2026-11-14  7:19   ` sashiko-bot

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.