diff options
Diffstat (limited to 'mm')
-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 e4683459ab26..8f13b26a5b93 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c | |||
@@ -1021,6 +1021,7 @@ int memory_failure(unsigned long pfn, int trapno, int flags) | |||
1021 | struct page *hpage; | 1021 | struct page *hpage; |
1022 | int res; | 1022 | int res; |
1023 | unsigned int nr_pages; | 1023 | unsigned int nr_pages; |
1024 | unsigned long page_flags; | ||
1024 | 1025 | ||
1025 | if (!sysctl_memory_failure_recovery) | 1026 | if (!sysctl_memory_failure_recovery) |
1026 | panic("Memory failure from trap %d on page %lx", trapno, pfn); | 1027 | panic("Memory failure from trap %d on page %lx", trapno, pfn); |
@@ -1129,6 +1130,15 @@ int memory_failure(unsigned long pfn, int trapno, int flags) | |||
1129 | lock_page(hpage); | 1130 | lock_page(hpage); |
1130 | 1131 | ||
1131 | /* | 1132 | /* |
1133 | * We use page flags to determine what action should be taken, but | ||
1134 | * the flags can be modified by the error containment action. One | ||
1135 | * example is an mlocked page, where PG_mlocked is cleared by | ||
1136 | * page_remove_rmap() in try_to_unmap_one(). So to determine page status | ||
1137 | * correctly, we save a copy of the page flags at this time. | ||
1138 | */ | ||
1139 | page_flags = p->flags; | ||
1140 | |||
1141 | /* | ||
1132 | * unpoison always clear PG_hwpoison inside page lock | 1142 | * unpoison always clear PG_hwpoison inside page lock |
1133 | */ | 1143 | */ |
1134 | if (!PageHWPoison(p)) { | 1144 | if (!PageHWPoison(p)) { |
@@ -1186,12 +1196,19 @@ int memory_failure(unsigned long pfn, int trapno, int flags) | |||
1186 | } | 1196 | } |
1187 | 1197 | ||
1188 | res = -EBUSY; | 1198 | res = -EBUSY; |
1189 | for (ps = error_states;; ps++) { | 1199 | /* |
1190 | if ((p->flags & ps->mask) == ps->res) { | 1200 | * The first check uses the current page flags which may not have any |
1191 | res = page_action(ps, p, pfn); | 1201 | * relevant information. The second check with the saved page flagss is |
1202 | * carried out only if the first check can't determine the page status. | ||
1203 | */ | ||
1204 | for (ps = error_states;; ps++) | ||
1205 | if ((p->flags & ps->mask) == ps->res) | ||
1192 | break; | 1206 | break; |
1193 | } | 1207 | if (!ps->mask) |
1194 | } | 1208 | for (ps = error_states;; ps++) |
1209 | if ((page_flags & ps->mask) == ps->res) | ||
1210 | break; | ||
1211 | res = page_action(ps, p, pfn); | ||
1195 | out: | 1212 | out: |
1196 | unlock_page(hpage); | 1213 | unlock_page(hpage); |
1197 | return res; | 1214 | return res; |