[PATCH v6 3/7] mm/memory-failure: add hwpoison_boot_page() to flag an inherited frame

From: Breno Leitao

Date: Fri Oct 16 2026 - 08:18:48 EST


A kernel that kexecs inherits the frames its predecessor found
hardware-poisoned. It has to carry them as PG_hwpoison in its own memmap,
so that everything that already understands the flag covers them,
including the kexec segment placement check.

Add a helper that sets the flag and counts the frame in
num_poisoned_pages. It runs while the memmap is initialised, before
memory_dev_init() has run. memblk_nr_poison_inc() divides by
sections_per_block, which memory_dev_init() sets, so the helper
increments num_poisoned_pages itself instead of calling
num_poisoned_pages_inc().

An inherited frame is a hardware failure like any other, so the helper
sets hw_memory_failure. Without it, unpoison_memory() accepts the frame,
clears the flag and drops the last reference, which frees a frame that
is still bad.

The helper also counts the frame as managed, as a frame that fails at
run time stays counted. Memory offlining subtracts every frame of the
block that is not PageOffline, so a frame that was never counted leaves
the managed page count too low after an offline.

The per-node memory failure statistics are not updated. This kernel
handled no failure, it only inherited one.

Signed-off-by: Breno Leitao <leitao@xxxxxxxxxx>
---
mm/internal.h | 5 +++++
mm/memory-failure.c | 8 ++++++++
2 files changed, 13 insertions(+)

diff --git a/mm/internal.h b/mm/internal.h
index da14c56fb24e11..4bd3bbce86e0ad 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -1169,6 +1169,7 @@ void SetPageHWPoisonTakenOff(struct page *page);
void ClearPageHWPoisonTakenOff(struct page *page);
bool take_page_off_buddy(struct page *page);
bool put_page_back_buddy(struct page *page);
+void __init hwpoison_boot_page(struct page *page);
struct task_struct *task_early_kill(struct task_struct *tsk, int force_early);
void add_to_kill_ksm(struct task_struct *tsk, const struct page *p,
struct vm_area_struct *vma, struct list_head *to_kill,
@@ -1181,6 +1182,10 @@ static inline int unmap_poisoned_folio(struct folio *folio, unsigned long pfn, b
{
return -EBUSY;
}
+
+static inline void hwpoison_boot_page(struct page *page)
+{
+}
#endif

extern unsigned long __must_check vm_mmap_pgoff(struct file *, unsigned long,
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index a2ca8df501caee..d0d6b184ee469f 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -96,6 +96,14 @@ void num_poisoned_pages_sub(unsigned long pfn, long i)
memblk_nr_poison_sub(pfn, i);
}

+void __init hwpoison_boot_page(struct page *page)
+{
+ SetPageHWPoison(page);
+ atomic_long_inc(&num_poisoned_pages);
+ hw_memory_failure = true;
+ adjust_managed_page_count(page, 1);
+}
+
/*
* Return the first or the last hardware-poisoned online page in [start,
* start + size), or PHYS_ADDR_MAX if the range is clean.

--
2.53.0-Meta