[PATCH v5 9/9] mm/memory-failure: keep inherited poisoned frames out of the buddy allocator

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


When the pages are being given to the allocator, check if they are
poisoned, and mark them as such.

Similar to unaccepted memory, hook it in __free_pages_core(), and thus
the frames never enter the allocator, rather than being taken back out
of it.

A block runs up to MAX_PAGE_ORDER and a unit is 2MB, so hold back only
the frames the table covers and free the rest one at a time. The
allocator merges them back up, so a unit costs the frames it names, not
the whole block.

The frames that are freed go back through accept_and_free_block(), so a
clean frame in a block that is still unaccepted is accepted first.

A frame is flagged before its memory block exists, so the per block
counter is seeded from the page flag when the block is created rather
than incremented here.

Suggested-by: Kiryl Shutsemau <kas at kernel.org>
Signed-off-by: Breno Leitao <leitao at debian.org>
---
 mm/page_alloc.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index b07b5f4751cb95..cca67a2702a421 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1597,6 +1597,25 @@ static void __meminit accept_and_free_block(struct page *page,
 	__free_pages_ok(page, order, FPI_TO_TAIL);
 }
 
+static void __meminit free_poisoned_block(struct page *page, unsigned int order,
+					  enum meminit_context context)
+{
+	unsigned long i, nr_pages = 1UL << order;
+
+	for (i = 0; i < nr_pages; i++) {
+		struct page *p = page + i;
+		phys_addr_t phys = page_to_phys(p);
+
+		if (range_contains_poisoned_memory(phys, PAGE_SIZE)) {
+			hwpoison_boot_page(p, context);
+			continue;
+		}
+
+		/* this part of the block is not poisoned */
+		accept_and_free_block(p, 0);
+	}
+}
+
 void __meminit __free_pages_core(struct page *page, unsigned int order,
 		enum meminit_context context)
 {
@@ -1631,6 +1650,13 @@ void __meminit __free_pages_core(struct page *page, unsigned int order,
 		atomic_long_add(nr_pages, &page_zone(page)->managed_pages);
 	}
 
+	/* First: a block parked by __free_unaccepted() never returns here. */
+	if (range_contains_poisoned_memory(page_to_phys(page),
+					   PAGE_SIZE << order)) {
+		free_poisoned_block(page, order, context);
+		return;
+	}
+
 	accept_and_free_block(page, order);
 }
 

-- 
2.53.0-Meta




More information about the kexec mailing list