From: Hao Li <hao.li@linux.dev>
To: david@kernel.org, osalvador@suse.de, akpm@linux-foundation.org
Cc: vbabka@suse.cz, harry.yoo@oracle.com, linux-mm@kvack.org,
linux-kernel@vger.kernel.org, linux-cxl@vger.kernel.org,
Hao Li <hao.li@linux.dev>
Subject: [PATCH] mm/memory_hotplug: maintain N_NORMAL_MEMORY during hotplug
Date: Fri, 27 Mar 2026 20:42:47 +0800 [thread overview]
Message-ID: <20260327124412.469833-1-hao.li@linux.dev> (raw)
N_NORMAL_MEMORY is initialized from zone population at boot, but memory
hotplug currently only updates N_MEMORY. As a result, a node that gains
normal memory via hotplug can remain invisible to users iterating over
N_NORMAL_MEMORY, while a node that loses its last normal memory can stay
incorrectly marked as such.
Restore N_NORMAL_MEMORY maintenance directly in online_pages() and
offline_pages(). Set the bit when a node that currently lacks normal
memory onlines pages into a zone <= ZONE_NORMAL, and clear it when
offlining removes the last present pages from zones <= ZONE_NORMAL.
This restores the intended semantics without bringing back the old
status_change_nid_normal notifier plumbing which was removed in
8d2882a8edb8.
Current users that benefit include list_lru, zswap, nfsd filecache,
hugetlb_cgroup, and has_normal_memory sysfs reporting.
Signed-off-by: Hao Li <hao.li@linux.dev>
---
This patch also prepares for a subsequent SLUB change that makes
can_free_to_pcs() rely on N_NORMAL_MEMORY to decide whether an object can be
freed to the sheaf.
---
mm/memory_hotplug.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index bc805029da51..5498744aa1f1 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1155,6 +1155,7 @@ int online_pages(unsigned long pfn, unsigned long nr_pages,
int need_zonelists_rebuild = 0;
unsigned long flags;
int ret;
+ bool need_set_normal_memory = false;
/*
* {on,off}lining is constrained to full memory sections (or more
@@ -1180,6 +1181,9 @@ int online_pages(unsigned long pfn, unsigned long nr_pages,
if (ret)
goto failed_addition;
}
+ /* Adding normal memory to the node for the first time */
+ if (!node_state(nid, N_NORMAL_MEMORY) && zone_idx(zone) <= ZONE_NORMAL)
+ need_set_normal_memory = true;
ret = memory_notify(MEM_GOING_ONLINE, &mem_arg);
ret = notifier_to_errno(ret);
@@ -1209,6 +1213,8 @@ int online_pages(unsigned long pfn, unsigned long nr_pages,
if (node_arg.nid >= 0)
node_set_state(nid, N_MEMORY);
+ if (need_set_normal_memory)
+ node_set_state(nid, N_NORMAL_MEMORY);
if (need_zonelists_rebuild)
build_all_zonelists(NULL);
@@ -1908,6 +1914,9 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages,
unsigned long flags;
char *reason;
int ret;
+ bool need_clear_normal_memory = false;
+ unsigned long node_normal_pages = 0;
+ enum zone_type zt;
/*
* {on,off}lining is constrained to full memory sections (or more
@@ -1977,6 +1986,13 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages,
goto failed_removal_isolated;
}
}
+ /*
+ * Check whether this operation removes the node's last normal memory.
+ */
+ for (zt = 0; zt <= ZONE_NORMAL; zt++)
+ node_normal_pages += pgdat->node_zones[zt].present_pages;
+ if (nr_pages >= node_normal_pages && zone_idx(zone) <= ZONE_NORMAL)
+ need_clear_normal_memory = true;
ret = memory_notify(MEM_GOING_OFFLINE, &mem_arg);
ret = notifier_to_errno(ret);
@@ -2055,6 +2071,12 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages,
/* reinitialise watermarks and update pcp limits */
init_per_zone_wmark_min();
+ /*
+ * Clear N_NORMAL_MEMORY first to avoid the transient state
+ * "!N_MEMORY && N_NORMAL_MEMORY".
+ */
+ if (need_clear_normal_memory)
+ node_clear_state(node, N_NORMAL_MEMORY);
/*
* Make sure to mark the node as memory-less before rebuilding the zone
* list. Otherwise this node would still appear in the fallback lists.
--
2.50.1
next reply other threads:[~2026-03-27 12:44 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-27 12:42 Hao Li [this message]
2026-03-27 14:38 ` [PATCH] mm/memory_hotplug: maintain N_NORMAL_MEMORY during hotplug Joshua Hahn
2026-03-27 14:44 ` Vlastimil Babka (SUSE)
2026-03-27 15:22 ` David Hildenbrand (Arm)
2026-03-28 4:09 ` Hao Li
2026-03-27 16:35 ` Joshua Hahn
2026-03-28 4:03 ` Hao Li
2026-03-30 11:43 ` Vlastimil Babka (SUSE)
2026-03-27 15:28 ` David Hildenbrand (Arm)
2026-03-28 4:12 ` Hao Li
2026-03-28 3:47 ` Hao Li
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260327124412.469833-1-hao.li@linux.dev \
--to=hao.li@linux.dev \
--cc=akpm@linux-foundation.org \
--cc=david@kernel.org \
--cc=harry.yoo@oracle.com \
--cc=linux-cxl@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=osalvador@suse.de \
--cc=vbabka@suse.cz \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.