Re: [PATCH v8 0/8] mm: Hot page tracking and promotion infrastructure

From: Davidlohr Bueso

Date: Sun Sep 27 2026 - 19:21:54 EST


On Wed, 16 Sep 2026, Bharata B Rao wrote:

On 12-Sep-26 2:38 AM, Joshua Hahn wrote:
- I also found a bug where the kmigrated thread isn't created for nodes
that are onlined later after boot, like CXL nodes that get onlined
with memory later on. I've been using a small hack commit in
pghot_memhp_callback that just checks for the MEM_ONLINE case and
spawns a kmigrated thread.

I will take care of this scenario (CXL node online after boot) in my next posting.

I was also running into this for chmu integration. I have been carrying the
below in my own testing, fwiw.

Thanks,
Davidlohr

-------------8<------------------------------------------
diff --git a/mm/pghot.c b/mm/pghot.c
index c7f523a61683..2f1e16e5091d 100644
--- a/mm/pghot.c
+++ b/mm/pghot.c
@@ -602,6 +609,18 @@ static int kmigrated_run(int nid)
return 0;
}
+static void kmigrated_stop(int nid)
+{
+ pg_data_t *pgdat = NODE_DATA(nid);
+ struct task_struct *t;
+
+ t = pgdat->kmigrated;
+ if (t) {
+ kthread_stop(t);
+ pgdat->kmigrated = NULL;
+ }
+}
+
static void pghot_free_hot_map(struct mem_section *ms)
{
struct pghot_hot_map *hot_map;
@@ -707,6 +726,58 @@ static struct notifier_block pghot_mem_notifier = {
.priority = DEFAULT_CALLBACK_PRI,
};
+/*
+ * The first memory block of a node is onlined before the node gets
+ * assigned to a memory tier (memory tiers react to
+ * NODE_ADDED_FIRST_MEMORY at MEMTIER_HOTPLUG_PRI), so at
+ * MEM_GOING_ONLINE time for that block node_is_toptier() still
+ * returns true and pghot_online_sec_hotmap() skips it. Backfill the
+ * hot maps once the node tier is known.
+ */
+static void pghot_alloc_node_hotmaps(int nid)
+{
+ unsigned long section_nr, s_begin, start_pfn;
+ struct mem_section *ms;
+ struct page *page;
+
+ s_begin = next_present_section_nr(-1);
+ for_each_present_section_nr(s_begin, section_nr) {
+ ms = __nr_to_section(section_nr);
+ start_pfn = section_nr_to_pfn(section_nr);
+
+ page = pfn_to_online_page(start_pfn);
+ if (!page || page_to_nid(page) != nid)
+ continue;
+
+ if (rcu_access_pointer(ms->hot_map))
+ continue;
+
+ if (pghot_alloc_hot_map(ms, nid))
+ pr_warn("pghot: no hot map for section %lu on node %d\n",
+ section_nr, nid);
+ }
+}
+
+static int pghot_node_callback(struct notifier_block *self,
+ unsigned long action, void *arg)
+{
+ struct node_notify *nn = arg;
+
+ switch (action) {
+ case NODE_ADDED_FIRST_MEMORY:
+ if (node_is_toptier(nn->nid))
+ break;
+ pghot_alloc_node_hotmaps(nn->nid);
+ kmigrated_run(nn->nid);
+ break;
+ case NODE_REMOVED_LAST_MEMORY:
+ kmigrated_stop(nn->nid);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
static void pghot_destroy_hot_map(void)
{
unsigned long section_nr, s_begin;
@@ -891,6 +962,11 @@ static int __init pghot_init(void)
if (ret)
goto out_stop_kthread;
}
+
+ ret = hotplug_node_notifier(pghot_node_callback, 0);
+ if (ret)
+ goto out_stop_kthread;
+
pghot_sysctl_init();
pghot_debug_init();
pghot_src_enabled_init();
--
2.39.5