Commit 8d22ba1b authored by Wu Fengguang's avatar Wu Fengguang Committed by Andi Kleen

HWPOISON: detect free buddy pages explicitly

Most free pages in the buddy system have no PG_buddy set.
Introduce is_free_buddy_page() for detecting them reliably.

CC: Nick Piggin <npiggin@suse.de>
CC: Mel Gorman <mel@linux.vnet.ibm.com>
Signed-off-by: default avatarWu Fengguang <fengguang.wu@intel.com>
Signed-off-by: default avatarAndi Kleen <ak@linux.intel.com>
parent 95d01fc6
...@@ -50,6 +50,9 @@ extern void putback_lru_page(struct page *page); ...@@ -50,6 +50,9 @@ extern void putback_lru_page(struct page *page);
*/ */
extern void __free_pages_bootmem(struct page *page, unsigned int order); extern void __free_pages_bootmem(struct page *page, unsigned int order);
extern void prep_compound_page(struct page *page, unsigned long order); extern void prep_compound_page(struct page *page, unsigned long order);
#ifdef CONFIG_MEMORY_FAILURE
extern bool is_free_buddy_page(struct page *page);
#endif
/* /*
......
...@@ -807,8 +807,13 @@ int __memory_failure(unsigned long pfn, int trapno, int flags) ...@@ -807,8 +807,13 @@ int __memory_failure(unsigned long pfn, int trapno, int flags)
*/ */
if (!(flags & MF_COUNT_INCREASED) && if (!(flags & MF_COUNT_INCREASED) &&
!get_page_unless_zero(compound_head(p))) { !get_page_unless_zero(compound_head(p))) {
action_result(pfn, "free or high order kernel", IGNORED); if (is_free_buddy_page(p)) {
return PageBuddy(compound_head(p)) ? 0 : -EBUSY; action_result(pfn, "free buddy", DELAYED);
return 0;
} else {
action_result(pfn, "high order kernel", IGNORED);
return -EBUSY;
}
} }
/* /*
......
...@@ -5081,3 +5081,24 @@ __offline_isolated_pages(unsigned long start_pfn, unsigned long end_pfn) ...@@ -5081,3 +5081,24 @@ __offline_isolated_pages(unsigned long start_pfn, unsigned long end_pfn)
spin_unlock_irqrestore(&zone->lock, flags); spin_unlock_irqrestore(&zone->lock, flags);
} }
#endif #endif
#ifdef CONFIG_MEMORY_FAILURE
bool is_free_buddy_page(struct page *page)
{
struct zone *zone = page_zone(page);
unsigned long pfn = page_to_pfn(page);
unsigned long flags;
int order;
spin_lock_irqsave(&zone->lock, flags);
for (order = 0; order < MAX_ORDER; order++) {
struct page *page_head = page - (pfn & ((1 << order) - 1));
if (PageBuddy(page_head) && page_order(page_head) >= order)
break;
}
spin_unlock_irqrestore(&zone->lock, flags);
return order < MAX_ORDER;
}
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment