diff options
Diffstat (limited to 'mm/memory-failure.c')
-rw-r--r-- | mm/memory-failure.c | 33 |
1 files changed, 32 insertions, 1 deletions
diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 5c7158a11592..333f87da1845 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c | |||
@@ -983,7 +983,10 @@ int __memory_failure(unsigned long pfn, int trapno, int flags) | |||
983 | * We need/can do nothing about count=0 pages. | 983 | * We need/can do nothing about count=0 pages. |
984 | * 1) it's a free page, and therefore in safe hand: | 984 | * 1) it's a free page, and therefore in safe hand: |
985 | * prep_new_page() will be the gate keeper. | 985 | * prep_new_page() will be the gate keeper. |
986 | * 2) it's part of a non-compound high order page. | 986 | * 2) it's a free hugepage, which is also safe: |
987 | * an affected hugepage will be dequeued from hugepage freelist, | ||
988 | * so there's no concern about reusing it ever after. | ||
989 | * 3) it's part of a non-compound high order page. | ||
987 | * Implies some kernel user: cannot stop them from | 990 | * Implies some kernel user: cannot stop them from |
988 | * R/W the page; let's pray that the page has been | 991 | * R/W the page; let's pray that the page has been |
989 | * used and will be freed some time later. | 992 | * used and will be freed some time later. |
@@ -995,6 +998,24 @@ int __memory_failure(unsigned long pfn, int trapno, int flags) | |||
995 | if (is_free_buddy_page(p)) { | 998 | if (is_free_buddy_page(p)) { |
996 | action_result(pfn, "free buddy", DELAYED); | 999 | action_result(pfn, "free buddy", DELAYED); |
997 | return 0; | 1000 | return 0; |
1001 | } else if (PageHuge(hpage)) { | ||
1002 | /* | ||
1003 | * Check "just unpoisoned", "filter hit", and | ||
1004 | * "race with other subpage." | ||
1005 | */ | ||
1006 | lock_page_nosync(hpage); | ||
1007 | if (!PageHWPoison(hpage) | ||
1008 | || (hwpoison_filter(p) && TestClearPageHWPoison(p)) | ||
1009 | || (p != hpage && TestSetPageHWPoison(hpage))) { | ||
1010 | atomic_long_sub(nr_pages, &mce_bad_pages); | ||
1011 | return 0; | ||
1012 | } | ||
1013 | set_page_hwpoison_huge_page(hpage); | ||
1014 | res = dequeue_hwpoisoned_huge_page(hpage); | ||
1015 | action_result(pfn, "free huge", | ||
1016 | res ? IGNORED : DELAYED); | ||
1017 | unlock_page(hpage); | ||
1018 | return res; | ||
998 | } else { | 1019 | } else { |
999 | action_result(pfn, "high order kernel", IGNORED); | 1020 | action_result(pfn, "high order kernel", IGNORED); |
1000 | return -EBUSY; | 1021 | return -EBUSY; |
@@ -1156,6 +1177,16 @@ int unpoison_memory(unsigned long pfn) | |||
1156 | nr_pages = 1 << compound_order(page); | 1177 | nr_pages = 1 << compound_order(page); |
1157 | 1178 | ||
1158 | if (!get_page_unless_zero(page)) { | 1179 | if (!get_page_unless_zero(page)) { |
1180 | /* | ||
1181 | * Since HWPoisoned hugepage should have non-zero refcount, | ||
1182 | * race between memory failure and unpoison seems to happen. | ||
1183 | * In such case unpoison fails and memory failure runs | ||
1184 | * to the end. | ||
1185 | */ | ||
1186 | if (PageHuge(page)) { | ||
1187 | pr_debug("MCE: Memory failure is now running on free hugepage %#lx\n", pfn); | ||
1188 | return 0; | ||
1189 | } | ||
1159 | if (TestClearPageHWPoison(p)) | 1190 | if (TestClearPageHWPoison(p)) |
1160 | atomic_long_sub(nr_pages, &mce_bad_pages); | 1191 | atomic_long_sub(nr_pages, &mce_bad_pages); |
1161 | pr_debug("MCE: Software-unpoisoned free page %#lx\n", pfn); | 1192 | pr_debug("MCE: Software-unpoisoned free page %#lx\n", pfn); |