path: root/include/linux/hugetlb.h
diff options
authorNaoya Horiguchi <>2021-06-15 18:23:13 -0700
committerLinus Torvalds <>2021-06-16 09:24:42 -0700
commit25182f05ffed0b45602438693e4eed5d7f3ebadd (patch)
tree6ed39c894b33b6e84222639c60e018d4cc46b6c9 /include/linux/hugetlb.h
parent94f0b2d4a1d0c52035aef425da5e022bd2cb1c71 (diff)
mm,hwpoison: fix race with hugetlb page allocation
When hugetlb page fault (under overcommitting situation) and memory_failure() race, VM_BUG_ON_PAGE() is triggered by the following race: CPU0: CPU1: gather_surplus_pages() page = alloc_surplus_huge_page() memory_failure_hugetlb() get_hwpoison_page(page) __get_hwpoison_page(page) get_page_unless_zero(page) zero = put_page_testzero(page) VM_BUG_ON_PAGE(!zero, page) enqueue_huge_page(h, page) put_page(page) __get_hwpoison_page() only checks the page refcount before taking an additional one for memory error handling, which is not enough because there's a time window where compound pages have non-zero refcount during hugetlb page initialization. So make __get_hwpoison_page() check page status a bit more for hugetlb pages with get_hwpoison_huge_page(). Checking hugetlb-specific flags under hugetlb_lock makes sure that the hugetlb page is not transitive. It's notable that another new function, HWPoisonHandlable(), is helpful to prevent a race against other transitive page states (like a generic compound page just before PageHuge becomes true). Link: Fixes: ead07f6a867b ("mm/memory-failure: introduce get_hwpoison_page() for consistent refcount handling") Signed-off-by: Naoya Horiguchi <> Reported-by: Muchun Song <> Acked-by: Mike Kravetz <> Cc: Oscar Salvador <> Cc: Michal Hocko <> Cc: Tony Luck <> Cc: <> [5.12+] Signed-off-by: Andrew Morton <> Signed-off-by: Linus Torvalds <>
Diffstat (limited to 'include/linux/hugetlb.h')
1 files changed, 6 insertions, 0 deletions
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index b92f25ccef58..790ae618548d 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -149,6 +149,7 @@ bool hugetlb_reserve_pages(struct inode *inode, long from, long to,
long hugetlb_unreserve_pages(struct inode *inode, long start, long end,
long freed);
bool isolate_huge_page(struct page *page, struct list_head *list);
+int get_hwpoison_huge_page(struct page *page, bool *hugetlb);
void putback_active_hugepage(struct page *page);
void move_hugetlb_state(struct page *oldpage, struct page *newpage, int reason);
void free_huge_page(struct page *page);
@@ -339,6 +340,11 @@ static inline bool isolate_huge_page(struct page *page, struct list_head *list)
return false;
+static inline int get_hwpoison_huge_page(struct page *page, bool *hugetlb)
+ return 0;
static inline void putback_active_hugepage(struct page *page)