[PATCH v5 7/9] drivers/base/memory: count inherited poisoned frames into the block
From: Breno Leitao
Date: Tue Sep 15 2026 - 10:39:15 EST
A frame a kexec handed over is flagged as it reaches the allocator, long
before its memory block exists, so memblk_nr_poison_inc() had nowhere to
count it. Walk the block once when it is created and take the count from
the page flag instead.
Without it an unpoison later subtracts from a counter that was never
incremented and wraps it, which then refuses memory_block_online() for
good.
Only a block created online needs the walk. A hotplugged one is created
before its pages are, so there is nothing to find, and its frames are
counted by num_poisoned_pages_inc() as they are flagged.
Signed-off-by: Breno Leitao <leitao@xxxxxxxxxx>
---
drivers/base/memory.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 5eead3346f1e32..49a33ddbb2e717 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -220,11 +220,16 @@ int memory_notify(enum memory_block_state state, void *v)
#if defined(CONFIG_MEMORY_FAILURE) && defined(CONFIG_MEMORY_HOTPLUG)
static unsigned long memblk_nr_poison(struct memory_block *mem);
+static void memblk_nr_poison_init(struct memory_block *mem);
#else
static inline unsigned long memblk_nr_poison(struct memory_block *mem)
{
return 0;
}
+
+static inline void memblk_nr_poison_init(struct memory_block *mem)
+{
+}
#endif
/*
@@ -807,6 +812,7 @@ static int add_memory_block(unsigned long block_id, int nid, unsigned long state
mem->state = state;
mem->nid = nid;
INIT_LIST_HEAD(&mem->group_next);
+ memblk_nr_poison_init(mem);
#ifndef CONFIG_NUMA
if (state == MEM_ONLINE)
@@ -1251,4 +1257,33 @@ static unsigned long memblk_nr_poison(struct memory_block *mem)
{
return atomic_long_read(&mem->nr_hwpoison);
}
+
+/*
+ * Frames a kexec handed over are flagged as they reach the allocator, long
+ * before this block exists, so memblk_nr_poison_inc() had nowhere to count
+ * them. Take them from the page flag instead.
+ */
+static void memblk_nr_poison_init(struct memory_block *mem)
+{
+ unsigned long pfn = section_nr_to_pfn(mem->start_section_nr);
+ unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block;
+ unsigned long i, nr_poison = 0;
+
+ /* A hotplugged block is created before its pages are online. */
+ if (mem->state != MEM_ONLINE)
+ return;
+
+ if (!range_contains_poisoned_memory(PFN_PHYS(pfn),
+ nr_pages << PAGE_SHIFT))
+ return;
+
+ for (i = 0; i < nr_pages; i++) {
+ struct page *page = pfn_to_online_page(pfn + i);
+
+ if (page && PageHWPoison(page))
+ nr_poison++;
+ }
+
+ atomic_long_set(&mem->nr_hwpoison, nr_poison);
+}
#endif
--
2.53.0-Meta