summaryrefslogtreecommitdiff
path: root/mm/memory-failure.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/memory-failure.c')
-rw-r--r--mm/memory-failure.c43
1 files changed, 14 insertions, 29 deletions
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index af7fb23b93ae..962129c50be2 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -65,9 +65,11 @@ int sysctl_memory_failure_recovery __read_mostly = 1;
atomic_long_t num_poisoned_pages __read_mostly = ATOMIC_LONG_INIT(0);
-static void page_handle_poison(struct page *page)
+static void page_handle_poison(struct page *page, bool release)
{
SetPageHWPoison(page);
+ if (release)
+ put_page(page);
page_ref_inc(page);
num_poisoned_pages_inc();
}
@@ -1765,19 +1767,13 @@ static int soft_offline_huge_page(struct page *page, int flags)
ret = -EIO;
} else {
/*
- * We set PG_hwpoison only when the migration source hugepage
- * was successfully dissolved, because otherwise hwpoisoned
- * hugepage remains on free hugepage list, then userspace will
- * find it as SIGBUS by allocation failure. That's not expected
- * in soft-offlining.
+ * We set PG_hwpoison only when we were able to take the page
+ * off the buddy.
*/
- ret = dissolve_free_huge_page(page);
- if (!ret) {
- if (set_hwpoison_free_buddy_page(page))
- num_poisoned_pages_inc();
- else
- ret = -EBUSY;
- }
+ if (!dissolve_free_huge_page(page) && take_page_off_buddy(page))
+ page_handle_poison(page, false);
+ else
+ ret = -EBUSY;
}
return ret;
}
@@ -1812,10 +1808,8 @@ static int __soft_offline_page(struct page *page, int flags)
* would need to fix isolation locking first.
*/
if (ret == 1) {
- put_page(page);
pr_info("soft_offline: %#lx: invalidated\n", pfn);
- SetPageHWPoison(page);
- num_poisoned_pages_inc();
+ page_handle_poison(page, true);
return 0;
}
@@ -1846,7 +1840,9 @@ static int __soft_offline_page(struct page *page, int flags)
list_add(&page->lru, &pagelist);
ret = migrate_pages(&pagelist, new_page, NULL, MPOL_MF_MOVE_ALL,
MIGRATE_SYNC, MR_MEMORY_FAILURE);
- if (ret) {
+ if (!ret) {
+ page_handle_poison(page, true);
+ } else {
if (!list_empty(&pagelist))
putback_movable_pages(&pagelist);
@@ -1865,27 +1861,16 @@ static int __soft_offline_page(struct page *page, int flags)
static int soft_offline_in_use_page(struct page *page, int flags)
{
int ret;
- int mt;
struct page *hpage = compound_head(page);
if (!PageHuge(page) && PageTransHuge(hpage))
if (try_to_split_thp_page(page, "soft offline") < 0)
return -EBUSY;
- /*
- * Setting MIGRATE_ISOLATE here ensures that the page will be linked
- * to free list immediately (not via pcplist) when released after
- * successful page migration. Otherwise we can't guarantee that the
- * page is really free after put_page() returns, so
- * set_hwpoison_free_buddy_page() highly likely fails.
- */
- mt = get_pageblock_migratetype(page);
- set_pageblock_migratetype(page, MIGRATE_ISOLATE);
if (PageHuge(page))
ret = soft_offline_huge_page(page, flags);
else
ret = __soft_offline_page(page, flags);
- set_pageblock_migratetype(page, mt);
return ret;
}
@@ -1894,7 +1879,7 @@ static int soft_offline_free_page(struct page *page)
int rc = -EBUSY;
if (!dissolve_free_huge_page(page) && take_page_off_buddy(page)) {
- page_handle_poison(page);
+ page_handle_poison(page, false);
rc = 0;
}