Commit a6d30dddae4648837be5a0c0cb2c0ae9ad0377db

Authored by Jin Dongming
Committed by Linus Torvalds
1 parent efeda7a41e

thp: fix the wrong reported address of hwpoisoned hugepages

When the tail page of THP is poisoned, the head page will be poisoned too.
 And the wrong address, address of head page, will be sent with sigbus
always.

So when the poisoned page is used by Guest OS which is running on KVM,
after the address changing(hva->gpa) by qemu, the unexpected process on
Guest OS will be killed by sigbus.

What we expected is that the process using the poisoned tail page could be
killed on Guest OS, but not that the process using the healthy head page
is killed.

Since it is not good to poison the healthy page, avoid poisoning other
than the page which is really poisoned.
  (While we poison all pages in a huge page in case of hugetlb,
   we can do this for THP thanks to split_huge_page().)

Here we fix two parts:
  1. Isolate the poisoned page only to make sure
     the reported address is the address of poisoned page.
  2. make the poisoned page work as the poisoned regular page.

[akpm@linux-foundation.org: fix spello in comment]
Signed-off-by: Jin Dongming <jin.dongming@np.css.fujitsu.com>
Reviewed-by: Hidetoshi Seto <seto.hidetoshi@jp.fujitsu.com>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: Andi Kleen <andi@firstfloor.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 2 changed files with 28 additions and 6 deletions Side-by-side Diff

... ... @@ -1162,7 +1162,12 @@
1162 1162 /* after clearing PageTail the gup refcount can be released */
1163 1163 smp_mb();
1164 1164  
1165   - page_tail->flags &= ~PAGE_FLAGS_CHECK_AT_PREP;
  1165 + /*
  1166 + * retain hwpoison flag of the poisoned tail page:
  1167 + * fix for the unsuitable process killed on Guest Machine(KVM)
  1168 + * by the memory-failure.
  1169 + */
  1170 + page_tail->flags &= ~PAGE_FLAGS_CHECK_AT_PREP | __PG_HWPOISON;
1166 1171 page_tail->flags |= (page->flags &
1167 1172 ((1L << PG_referenced) |
1168 1173 (1L << PG_swapbacked) |
... ... @@ -854,6 +854,7 @@
854 854 int ret;
855 855 int kill = 1;
856 856 struct page *hpage = compound_head(p);
  857 + struct page *ppage;
857 858  
858 859 if (PageReserved(p) || PageSlab(p))
859 860 return SWAP_SUCCESS;
... ... @@ -894,6 +895,14 @@
894 895 }
895 896 }
896 897  
  898 + /*
  899 + * ppage: poisoned page
  900 + * if p is regular page(4k page)
  901 + * ppage == real poisoned page;
  902 + * else p is hugetlb or THP, ppage == head page.
  903 + */
  904 + ppage = hpage;
  905 +
897 906 if (PageTransHuge(hpage)) {
898 907 /*
899 908 * Verify that this isn't a hugetlbfs head page, the check for
... ... @@ -919,6 +928,8 @@
919 928 BUG_ON(!PageHWPoison(p));
920 929 return SWAP_FAIL;
921 930 }
  931 + /* THP is split, so ppage should be the real poisoned page. */
  932 + ppage = p;
922 933 }
923 934 }
924 935  
925 936  
926 937  
927 938  
... ... @@ -931,13 +942,19 @@
931 942 * there's nothing that can be done.
932 943 */
933 944 if (kill)
934   - collect_procs(hpage, &tokill);
  945 + collect_procs(ppage, &tokill);
935 946  
936   - ret = try_to_unmap(hpage, ttu);
  947 + if (hpage != ppage)
  948 + lock_page_nosync(ppage);
  949 +
  950 + ret = try_to_unmap(ppage, ttu);
937 951 if (ret != SWAP_SUCCESS)
938 952 printk(KERN_ERR "MCE %#lx: failed to unmap page (mapcount=%d)\n",
939   - pfn, page_mapcount(hpage));
  953 + pfn, page_mapcount(ppage));
940 954  
  955 + if (hpage != ppage)
  956 + unlock_page(ppage);
  957 +
941 958 /*
942 959 * Now that the dirty bit has been propagated to the
943 960 * struct page and all unmaps done we can decide if
... ... @@ -947,7 +964,7 @@
947 964 * use a more force-full uncatchable kill to prevent
948 965 * any accesses to the poisoned memory.
949 966 */
950   - kill_procs_ao(&tokill, !!PageDirty(hpage), trapno,
  967 + kill_procs_ao(&tokill, !!PageDirty(ppage), trapno,
951 968 ret != SWAP_SUCCESS, p, pfn);
952 969  
953 970 return ret;
... ... @@ -1090,7 +1107,7 @@
1090 1107 * For error on the tail page, we should set PG_hwpoison
1091 1108 * on the head page to show that the hugepage is hwpoisoned
1092 1109 */
1093   - if (PageTail(p) && TestSetPageHWPoison(hpage)) {
  1110 + if (PageHuge(p) && PageTail(p) && TestSetPageHWPoison(hpage)) {
1094 1111 action_result(pfn, "hugepage already hardware poisoned",
1095 1112 IGNORED);
1096 1113 unlock_page(hpage);