summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/page-flags.h5
-rw-r--r--mm/memory-failure.c43
-rw-r--r--mm/migrate.c11
-rw-r--r--mm/page_alloc.c39
4 files changed, 28 insertions, 70 deletions
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index a02b6d0221db..4f6ba9379112 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -431,14 +431,9 @@ PAGEFLAG_FALSE(Uncached)
PAGEFLAG(HWPoison, hwpoison, PF_ANY)
TESTSCFLAG(HWPoison, hwpoison, PF_ANY)
#define __PG_HWPOISON (1UL << PG_hwpoison)
-extern bool set_hwpoison_free_buddy_page(struct page *page);
extern bool take_page_off_buddy(struct page *page);
#else
PAGEFLAG_FALSE(HWPoison)
-static inline bool set_hwpoison_free_buddy_page(struct page *page)
-{
- return 0;
-}
#define __PG_HWPOISON 0
#endif
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;
}
diff --git a/mm/migrate.c b/mm/migrate.c
index f94d7c7eeddf..4cf1af88c1dd 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -1223,16 +1223,11 @@ out:
* we want to retry.
*/
if (rc == MIGRATEPAGE_SUCCESS) {
- put_page(page);
- if (reason == MR_MEMORY_FAILURE) {
+ if (reason != MR_MEMORY_FAILURE)
/*
- * Set PG_HWPoison on just freed page
- * intentionally. Although it's rather weird,
- * it's how HWPoison flag works at the moment.
+ * We release the page in page_handle_poison.
*/
- if (set_hwpoison_free_buddy_page(page))
- num_poisoned_pages_inc();
- }
+ put_page(page);
} else {
if (rc != -EAGAIN) {
if (likely(!__PageMovable(page))) {
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 12dcfb3dc588..7fa55d9a3fe4 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1174,6 +1174,17 @@ static __always_inline bool free_pages_prepare(struct page *page,
trace_mm_page_free(page, order);
+ if (unlikely(PageHWPoison(page)) && !order) {
+ /*
+ * Do not let hwpoison pages hit pcplists/buddy
+ * Untie memcg state and reset page's owner
+ */
+ if (memcg_kmem_enabled() && PageKmemcg(page))
+ __memcg_kmem_uncharge_page(page, order);
+ reset_page_owner(page, order);
+ return false;
+ }
+
/*
* Check tail pages before head page information is cleared to
* avoid checking PageCompound for order-0 pages.
@@ -8844,32 +8855,4 @@ bool take_page_off_buddy(struct page *page)
spin_unlock_irqrestore(&zone->lock, flags);
return ret;
}
-
-/*
- * Set PG_hwpoison flag if a given page is confirmed to be a free page. This
- * test is performed under the zone lock to prevent a race against page
- * allocation.
- */
-bool set_hwpoison_free_buddy_page(struct page *page)
-{
- struct zone *zone = page_zone(page);
- unsigned long pfn = page_to_pfn(page);
- unsigned long flags;
- unsigned int order;
- bool hwpoisoned = false;
-
- spin_lock_irqsave(&zone->lock, flags);
- for (order = 0; order < MAX_ORDER; order++) {
- struct page *page_head = page - (pfn & ((1 << order) - 1));
-
- if (PageBuddy(page_head) && page_order(page_head) >= order) {
- if (!TestSetPageHWPoison(page))
- hwpoisoned = true;
- break;
- }
- }
- spin_unlock_irqrestore(&zone->lock, flags);
-
- return hwpoisoned;
-}
#endif