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
