mm: hwpoison: fix thp split handing in soft_offline_in_use_page()
authorzhongjiang <zhongjiang@huawei.com>
Tue, 5 Mar 2019 23:41:16 +0000 (15:41 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 6 Mar 2019 05:07:13 +0000 (21:07 -0800)
When soft_offline_in_use_page() runs on a thp tail page after pmd is
split, we trigger the following VM_BUG_ON_PAGE():

  Memory failure: 0x3755ff: non anonymous thp
  __get_any_page: 0x3755ff: unknown zero refcount page type 2fffff80000000
  Soft offlining pfn 0x34d805 at process virtual address 0x20fff000
  page:ffffea000d360140 count:0 mapcount:0 mapping:0000000000000000 index:0x1
  flags: 0x2fffff80000000()
  raw: 002fffff80000000 ffffea000d360108 ffffea000d360188 0000000000000000
  raw: 0000000000000001 0000000000000000 00000000ffffffff 0000000000000000
  page dumped because: VM_BUG_ON_PAGE(page_ref_count(page) == 0)
  ------------[ cut here ]------------
  kernel BUG at ./include/linux/mm.h:519!

soft_offline_in_use_page() passed refcount and page lock from tail page
to head page, which is not needed because we can pass any subpage to
split_huge_page().

Naoya had fixed a similar issue in c3901e722b29 ("mm: hwpoison: fix thp
split handling in memory_failure()").  But he missed fixing soft
offline.

Link: http://lkml.kernel.org/r/1551452476-24000-1-git-send-email-zhongjiang@huawei.com
Fixes: 61f5d698cc97 ("mm: re-enable THP")
Signed-off-by: zhongjiang <zhongjiang@huawei.com>
Acked-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: <stable@vger.kernel.org> [4.5+]
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
mm/memory-failure.c

index 831be5ff5f4df4bee8b9996b99705beeeeb45571..fc8b51744579c8fee5c588ee26164027f05c2a5d 100644 (file)
@@ -1825,19 +1825,17 @@ static int soft_offline_in_use_page(struct page *page, int flags)
        struct page *hpage = compound_head(page);
 
        if (!PageHuge(page) && PageTransHuge(hpage)) {
-               lock_page(hpage);
-               if (!PageAnon(hpage) || unlikely(split_huge_page(hpage))) {
-                       unlock_page(hpage);
-                       if (!PageAnon(hpage))
+               lock_page(page);
+               if (!PageAnon(page) || unlikely(split_huge_page(page))) {
+                       unlock_page(page);
+                       if (!PageAnon(page))
                                pr_info("soft offline: %#lx: non anonymous thp\n", page_to_pfn(page));
                        else
                                pr_info("soft offline: %#lx: thp split failed\n", page_to_pfn(page));
-                       put_hwpoison_page(hpage);
+                       put_hwpoison_page(page);
                        return -EBUSY;
                }
-               unlock_page(hpage);
-               get_hwpoison_page(page);
-               put_hwpoison_page(hpage);
+               unlock_page(page);
        }
 
        /*