diff options
Diffstat (limited to 'mm/memory-failure.c')
-rw-r--r-- | mm/memory-failure.c | 27 |
1 files changed, 22 insertions, 5 deletions
diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 1e9c30b241c3..04158d6f44d4 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c | |||
@@ -854,6 +854,7 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn, | |||
854 | int ret; | 854 | int ret; |
855 | int kill = 1; | 855 | int kill = 1; |
856 | struct page *hpage = compound_head(p); | 856 | struct page *hpage = compound_head(p); |
857 | struct page *ppage; | ||
857 | 858 | ||
858 | if (PageReserved(p) || PageSlab(p)) | 859 | if (PageReserved(p) || PageSlab(p)) |
859 | return SWAP_SUCCESS; | 860 | return SWAP_SUCCESS; |
@@ -894,6 +895,14 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn, | |||
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 | if (PageTransHuge(hpage)) { | 906 | if (PageTransHuge(hpage)) { |
898 | /* | 907 | /* |
899 | * Verify that this isn't a hugetlbfs head page, the check for | 908 | * Verify that this isn't a hugetlbfs head page, the check for |
@@ -919,6 +928,8 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn, | |||
919 | BUG_ON(!PageHWPoison(p)); | 928 | BUG_ON(!PageHWPoison(p)); |
920 | return SWAP_FAIL; | 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 | ||
@@ -931,12 +942,18 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn, | |||
931 | * there's nothing that can be done. | 942 | * there's nothing that can be done. |
932 | */ | 943 | */ |
933 | if (kill) | 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 | if (ret != SWAP_SUCCESS) | 951 | if (ret != SWAP_SUCCESS) |
938 | printk(KERN_ERR "MCE %#lx: failed to unmap page (mapcount=%d)\n", | 952 | printk(KERN_ERR "MCE %#lx: failed to unmap page (mapcount=%d)\n", |
939 | pfn, page_mapcount(hpage)); | 953 | pfn, page_mapcount(ppage)); |
954 | |||
955 | if (hpage != ppage) | ||
956 | unlock_page(ppage); | ||
940 | 957 | ||
941 | /* | 958 | /* |
942 | * Now that the dirty bit has been propagated to the | 959 | * Now that the dirty bit has been propagated to the |
@@ -947,7 +964,7 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn, | |||
947 | * use a more force-full uncatchable kill to prevent | 964 | * use a more force-full uncatchable kill to prevent |
948 | * any accesses to the poisoned memory. | 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 | ret != SWAP_SUCCESS, p, pfn); | 968 | ret != SWAP_SUCCESS, p, pfn); |
952 | 969 | ||
953 | return ret; | 970 | return ret; |
@@ -1090,7 +1107,7 @@ int __memory_failure(unsigned long pfn, int trapno, int flags) | |||
1090 | * For error on the tail page, we should set PG_hwpoison | 1107 | * For error on the tail page, we should set PG_hwpoison |
1091 | * on the head page to show that the hugepage is hwpoisoned | 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 | action_result(pfn, "hugepage already hardware poisoned", | 1111 | action_result(pfn, "hugepage already hardware poisoned", |
1095 | IGNORED); | 1112 | IGNORED); |
1096 | unlock_page(hpage); | 1113 | unlock_page(hpage); |