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 <[email protected]>
Signed-off-by: Breno Leitao <[email protected]>
---
 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


Reply via email to