[PATCH v5 8/9] mm/memory-failure: add hwpoison_boot_page() to flag an inherited frame

Breno Leitao leitao at debian.org
Tue Sep 15 05:53:42 PDT 2026


A frame the previous kernel recorded as poisoned has to be flagged before
it reaches the allocator, which is long before memory_failure() can run.
Add a helper that leaves it in the state a frame poisoned by this kernel
would be in, so everything that already understands PG_hwpoison covers it,
the kexec segment placement check included.

num_poisoned_pages_inc() does not work at boot: its per memory block half
looks the block up by pfn, and memory_dev_init() has not run, so it
divides by zero. Take only the global counter there. A hotplugged block is
already there, so that path takes both counters as usual.

The caller comes later in this series.

Signed-off-by: Breno Leitao <leitao at debian.org>
---
 include/linux/mm.h  |  7 +++++++
 mm/memory-failure.c | 26 ++++++++++++++++++++++++++
 2 files changed, 33 insertions(+)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index b68824fcfbef19..8039830998dd4b 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -5225,6 +5225,8 @@ 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);
+void __meminit hwpoison_boot_page(struct page *page,
+				  enum meminit_context context);
 phys_addr_t range_first_hwpoison(phys_addr_t start, unsigned long size);
 phys_addr_t range_last_hwpoison(phys_addr_t start, unsigned long size);
 #else
@@ -5232,6 +5234,11 @@ static inline void memory_failure_queue(unsigned long pfn, int flags)
 {
 }
 
+static inline void hwpoison_boot_page(struct page *page,
+				      enum meminit_context context)
+{
+}
+
 static inline void num_poisoned_pages_inc(unsigned long pfn)
 {
 }
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index d9b8be696aac38..60e9682434700b 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -137,6 +137,32 @@ phys_addr_t range_last_hwpoison(phys_addr_t start, unsigned long size)
 	return range_hwpoison(start, size, false);
 }
 
+static void update_per_node_mf_stats(unsigned long pfn, enum mf_result result);
+
+void __meminit hwpoison_boot_page(struct page *page,
+				  enum meminit_context context)
+{
+	unsigned long pfn = page_to_pfn(page);
+
+	if (PageHWPoison(page))
+		return;
+
+	SetPageHWPoison(page);
+	set_page_count(page, 1);
+	/* The page has been completely isolated == MF_RECOVERED */
+	update_per_node_mf_stats(pfn, MF_RECOVERED);
+
+	/*
+	 * The per memory block half of num_poisoned_pages_inc() has no block to
+	 * find at boot, and divides by zero looking for one. A hotplugged block
+	 * is already there.
+	 */
+	if (context == MEMINIT_HOTPLUG)
+		num_poisoned_pages_inc(pfn);
+	else
+		atomic_long_inc(&num_poisoned_pages);
+}
+
 /**
  * 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




More information about the kexec mailing list