From: Naoya Horiguchi <naoya.horiguchi@xxxxxxx> 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 page refcount before taking additional one for memory error handling, which is wrong because there's a time window where compound pages have non-zero refcount during initialization. So makes __get_hwpoison_page() check page status a bit more for a few types of compound pages. PageSlab() check is added because otherwise "non anonymous thp" path is wrongly chosen. Fixes: ead07f6a867b ("mm/memory-failure: introduce get_hwpoison_page() for consistent refcount handling") Signed-off-by: Naoya Horiguchi <naoya.horiguchi@xxxxxxx> Reported-by: Muchun Song <songmuchun@xxxxxxxxxxxxx> Cc: stable@xxxxxxxxxxxxxxx # 5.12+ --- ChangeLog v4: - all hugetlb related check in hugetlb_lock, - fix build error with #ifdef CONFIG_HUGETLB_PAGE --- mm/memory-failure.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git v5.12/mm/memory-failure.c v5.12_patched/mm/memory-failure.c index a3659619d293..761f982b6d7b 100644 --- v5.12/mm/memory-failure.c +++ v5.12_patched/mm/memory-failure.c @@ -1094,6 +1094,16 @@ static int page_action(struct page_state *ps, struct page *p, static int __get_hwpoison_page(struct page *page) { struct page *head = compound_head(page); + int ret = 0; + +#ifdef CONFIG_HUGETLB_PAGE + spin_lock(&hugetlb_lock); + if (PageHuge(head) && (HPageFreed(head) || HPageMigratable(head))) + ret = get_page_unless_zero(head); + spin_unlock(&hugetlb_lock); + if (ret > 0) + return ret; +#endif if (!PageHuge(head) && PageTransHuge(head)) { /* -- 2.25.1