[PATCH v3 4/5] mm/memory-failure: add a helper to poison a frame at boot

From: Breno Leitao

Date: Wed Aug 26 2026 - 08:06:14 EST


A kernel that inherits a record of hardware-poisoned frames from an
earlier kernel needs a way to apply it. memory_failure() does not fit: it
takes mf_mutex, prints a line per frame, and a 2M unit is 512 of them.

Add hwpoison_boot_pfn(). A frame that is free takes the same route
memory_failure() takes for a free page: take_page_off_buddy(), the flag,
the refcount and the counter. A frame the kernel is already sitting on is
only flagged, since it cannot be taken away from whoever reserved it.
free_pages_prepare() drops such a frame if it is ever handed back, so it
does not reach the allocator either way.

The accounting cannot go through num_poisoned_pages_inc(). Its per memory
block half divides by sections_per_block, which memory_dev_init() only
sets up from driver_init(), so where this runs it is still zero and the
boot dies on a divide by zero. Bump the global counter directly; the block
counters stay short by these frames.

Signed-off-by: Breno Leitao <leitao@xxxxxxxxxx>
---
include/linux/mm.h | 1 +
mm/memory-failure.c | 24 ++++++++++++++++++++++++
2 files changed, 25 insertions(+)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index 32bb723ffbb92..52a00a0e090c7 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -5104,6 +5104,7 @@ extern const struct attribute_group memory_failure_attr_group;
extern void memory_failure_queue(unsigned long pfn, int flags);
void num_poisoned_pages_inc(unsigned long pfn);
void num_poisoned_pages_sub(unsigned long pfn, long i);
+bool __init hwpoison_boot_pfn(unsigned long pfn);
#else
static inline void memory_failure_queue(unsigned long pfn, int flags)
{
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 357a72ffda625..713fb2f8e0332 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -97,6 +97,30 @@ void num_poisoned_pages_sub(unsigned long pfn, long i)
memblk_nr_poison_sub(pfn, i);
}

+static void update_per_node_mf_stats(unsigned long pfn, enum mf_result result);
+
+bool __init hwpoison_boot_pfn(unsigned long pfn)
+{
+ struct page *page = pfn_to_online_page(pfn);
+
+ if (!page || PageHWPoison(page))
+ return false;
+
+ if (is_free_buddy_page(page)) {
+ if (!take_page_off_buddy(page))
+ return false;
+ page_ref_inc(page);
+ } else if (!PageReserved(page)) {
+ return false;
+ }
+
+ SetPageHWPoison(page);
+ update_per_node_mf_stats(pfn, MF_RECOVERED);
+ atomic_long_inc(&num_poisoned_pages);
+
+ return true;
+}
+
/**
* MF_ATTR_RO - Create sysfs entry for each memory failure statistics.
* @_name: name of the file in the per NUMA sysfs directory.

--
2.53.0-Meta