All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v7 00/14] HWPOISON: soft offline rework
@ 2020-09-22 13:56 Oscar Salvador
  2020-09-22 13:56 ` [PATCH v7 01/14] mm,hwpoison: cleanup unused PageHuge() check Oscar Salvador
                   ` (15 more replies)
  0 siblings, 16 replies; 27+ messages in thread
From: Oscar Salvador @ 2020-09-22 13:56 UTC (permalink / raw)
  To: akpm
  Cc: aris, naoya.horiguchi, mhocko, tony.luck, cai, linux-kernel,
	linux-mm, Oscar Salvador

Hi,

This patchset is the latest version of soft offline rework patchset
targetted for v5.9.

This patchset fixes a couple of issues that the patchset Naoya
sent [1] contained due to rebasing problems and a misunterdansting.

Main focus of this series is to stabilize soft offline.  Historically soft
offlined pages have suffered from racy conditions because PageHWPoison is
used to a little too aggressively, which (directly or indirectly) invades
other mm code which cares little about hwpoison.  This results in unexpected
behavior or kernel panic, which is very far from soft offline's "do not
disturb userspace or other kernel component" policy.
An example of this can be found here [2].

Along with several cleanups, this code refactors and changes the way soft
offline work.
Main point of this change set is to contain target page "via buddy allocator"
or in migrating path.
For ther former we first free the target page as we do for normal pages, and
once it has reached buddy and it has been taken off the freelists, we flag it
as HWpoison.
For the latter we never get to release the page in unmap_and_move, so
the page is under our control and we can handle it in hwpoison code.

[1] https://patchwork.kernel.org/cover/11704083/
[2] https://lore.kernel.org/linux-mm/20190826104144.GA7849@linux/T/#u


Naoya Horiguchi (5):
  mm,hwpoison: cleanup unused PageHuge() check
  mm, hwpoison: remove recalculating hpage
  mm,hwpoison-inject: don't pin for hwpoison_filter
  mm,hwpoison: introduce MF_MSG_UNSPLIT_THP
  mm,hwpoison: double-check page count in __get_any_page()

Oscar Salvador (9):
  mm,hwpoison: unexport get_hwpoison_page and make it static
  mm,hwpoison: refactor madvise_inject_error
  mm,hwpoison: kill put_hwpoison_page
  mm,hwpoison: unify THP handling for hard and soft offline
  mm,hwpoison: rework soft offline for free pages
  mm,hwpoison: rework soft offline for in-use pages
  mm,hwpoison: refactor soft_offline_huge_page and __soft_offline_page
  mm,hwpoison: return 0 if the page is already poisoned in soft-offline
  mm,hwpoison: Try to narrow window race for free pages

 include/linux/mm.h         |   3 +-
 include/linux/page-flags.h |   6 +-
 include/ras/ras_event.h    |   3 +
 mm/hwpoison-inject.c       |  18 +--
 mm/madvise.c               |  35 ++---
 mm/memory-failure.c        | 311 +++++++++++++++++--------------------
 mm/migrate.c               |  11 +-
 mm/page_alloc.c            |  71 +++++++--
 8 files changed, 231 insertions(+), 227 deletions(-)

-- 
2.26.2


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v7 01/14] mm,hwpoison: cleanup unused PageHuge() check
  2020-09-22 13:56 [PATCH v7 00/14] HWPOISON: soft offline rework Oscar Salvador
@ 2020-09-22 13:56 ` Oscar Salvador
  2020-09-22 13:56 ` [PATCH v7 02/14] mm, hwpoison: remove recalculating hpage Oscar Salvador
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 27+ messages in thread
From: Oscar Salvador @ 2020-09-22 13:56 UTC (permalink / raw)
  To: akpm
  Cc: aris, naoya.horiguchi, mhocko, tony.luck, cai, linux-kernel,
	linux-mm, Oscar Salvador

From: Naoya Horiguchi <naoya.horiguchi@nec.com>

Drop the PageHuge check, which is dead code since memory_failure() forks
into memory_failure_hugetlb() for hugetlb pages.

memory_failure() and memory_failure_hugetlb() shares some functions like
hwpoison_user_mappings() and identify_page_state(), so they should
properly handle 4kB page, thp, and hugetlb.

Signed-off-by: Naoya Horiguchi <naoya.horiguchi@nec.com>
Signed-off-by: Oscar Salvador <osalvador@suse.de>
Reviewed-by: Mike Kravetz <mike.kravetz@oracle.com>
---
 mm/memory-failure.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 166fad745918..b0eabc312381 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1381,10 +1381,7 @@ int memory_failure(unsigned long pfn, int flags)
 	 * page_remove_rmap() in try_to_unmap_one(). So to determine page status
 	 * correctly, we save a copy of the page flags at this time.
 	 */
-	if (PageHuge(p))
-		page_flags = hpage->flags;
-	else
-		page_flags = p->flags;
+	page_flags = p->flags;
 
 	/*
 	 * unpoison always clear PG_hwpoison inside page lock
-- 
2.26.2


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v7 02/14] mm, hwpoison: remove recalculating hpage
  2020-09-22 13:56 [PATCH v7 00/14] HWPOISON: soft offline rework Oscar Salvador
  2020-09-22 13:56 ` [PATCH v7 01/14] mm,hwpoison: cleanup unused PageHuge() check Oscar Salvador
@ 2020-09-22 13:56 ` Oscar Salvador
  2020-09-22 13:56 ` [PATCH v7 03/14] mm,hwpoison-inject: don't pin for hwpoison_filter Oscar Salvador
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 27+ messages in thread
From: Oscar Salvador @ 2020-09-22 13:56 UTC (permalink / raw)
  To: akpm
  Cc: aris, naoya.horiguchi, mhocko, tony.luck, cai, linux-kernel,
	linux-mm, Oscar Salvador

From: Naoya Horiguchi <naoya.horiguchi@nec.com>

hpage is never used after try_to_split_thp_page() in memory_failure(), so
we don't have to update hpage.  So let's not recalculate/use hpage.

Signed-off-by: Naoya Horiguchi <naoya.horiguchi@nec.com>
Signed-off-by: Oscar Salvador <osalvador@suse.de>
Suggested-by: "Aneesh Kumar K.V" <aneesh.kumar@linux.ibm.com>
Reviewed-by: Mike Kravetz <mike.kravetz@oracle.com>
---
 mm/memory-failure.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index b0eabc312381..0e6edffa91a9 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1341,7 +1341,6 @@ int memory_failure(unsigned long pfn, int flags)
 		}
 		unlock_page(p);
 		VM_BUG_ON_PAGE(!page_count(p), p);
-		hpage = compound_head(p);
 	}
 
 	/*
@@ -1413,11 +1412,8 @@ int memory_failure(unsigned long pfn, int flags)
 	/*
 	 * Now take care of user space mappings.
 	 * Abort on fail: __delete_from_page_cache() assumes unmapped page.
-	 *
-	 * When the raw error page is thp tail page, hpage points to the raw
-	 * page after thp split.
 	 */
-	if (!hwpoison_user_mappings(p, pfn, flags, &hpage)) {
+	if (!hwpoison_user_mappings(p, pfn, flags, &p)) {
 		action_result(pfn, MF_MSG_UNMAP_FAILED, MF_IGNORED);
 		res = -EBUSY;
 		goto out;
-- 
2.26.2


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v7 03/14] mm,hwpoison-inject: don't pin for hwpoison_filter
  2020-09-22 13:56 [PATCH v7 00/14] HWPOISON: soft offline rework Oscar Salvador
  2020-09-22 13:56 ` [PATCH v7 01/14] mm,hwpoison: cleanup unused PageHuge() check Oscar Salvador
  2020-09-22 13:56 ` [PATCH v7 02/14] mm, hwpoison: remove recalculating hpage Oscar Salvador
@ 2020-09-22 13:56 ` Oscar Salvador
  2020-09-22 13:56 ` [PATCH v7 04/14] mm,hwpoison: unexport get_hwpoison_page and make it static Oscar Salvador
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 27+ messages in thread
From: Oscar Salvador @ 2020-09-22 13:56 UTC (permalink / raw)
  To: akpm
  Cc: aris, naoya.horiguchi, mhocko, tony.luck, cai, linux-kernel,
	linux-mm, Oscar Salvador

From: Naoya Horiguchi <naoya.horiguchi@nec.com>

Another memory error injection interface debugfs:hwpoison/corrupt-pfn also
takes bogus refcount for hwpoison_filter().  It's justified because this
does a coarse filter, expecting that memory_failure() redoes the check for
sure.

Signed-off-by: Naoya Horiguchi <naoya.horiguchi@nec.com>
Signed-off-by: Oscar Salvador <osalvador@suse.de>
---
 mm/hwpoison-inject.c | 18 +++++-------------
 1 file changed, 5 insertions(+), 13 deletions(-)

diff --git a/mm/hwpoison-inject.c b/mm/hwpoison-inject.c
index e488876b168a..1ae1ebc2b9b1 100644
--- a/mm/hwpoison-inject.c
+++ b/mm/hwpoison-inject.c
@@ -26,11 +26,6 @@ static int hwpoison_inject(void *data, u64 val)
 
 	p = pfn_to_page(pfn);
 	hpage = compound_head(p);
-	/*
-	 * This implies unable to support free buddy pages.
-	 */
-	if (!get_hwpoison_page(p))
-		return 0;
 
 	if (!hwpoison_filter_enable)
 		goto inject;
@@ -40,23 +35,20 @@ static int hwpoison_inject(void *data, u64 val)
 	 * This implies unable to support non-LRU pages.
 	 */
 	if (!PageLRU(hpage) && !PageHuge(p))
-		goto put_out;
+		return 0;
 
 	/*
-	 * do a racy check with elevated page count, to make sure PG_hwpoison
-	 * will only be set for the targeted owner (or on a free page).
+	 * do a racy check to make sure PG_hwpoison will only be set for
+	 * the targeted owner (or on a free page).
 	 * memory_failure() will redo the check reliably inside page lock.
 	 */
 	err = hwpoison_filter(hpage);
 	if (err)
-		goto put_out;
+		return 0;
 
 inject:
 	pr_info("Injecting memory failure at pfn %#lx\n", pfn);
-	return memory_failure(pfn, MF_COUNT_INCREASED);
-put_out:
-	put_hwpoison_page(p);
-	return 0;
+	return memory_failure(pfn, 0);
 }
 
 static int hwpoison_unpoison(void *data, u64 val)
-- 
2.26.2


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v7 04/14] mm,hwpoison: unexport get_hwpoison_page and make it static
  2020-09-22 13:56 [PATCH v7 00/14] HWPOISON: soft offline rework Oscar Salvador
                   ` (2 preceding siblings ...)
  2020-09-22 13:56 ` [PATCH v7 03/14] mm,hwpoison-inject: don't pin for hwpoison_filter Oscar Salvador
@ 2020-09-22 13:56 ` Oscar Salvador
  2020-09-23  7:24   ` HORIGUCHI NAOYA(堀口 直也)
  2020-09-22 13:56 ` [PATCH v7 05/14] mm,hwpoison: refactor madvise_inject_error Oscar Salvador
                   ` (11 subsequent siblings)
  15 siblings, 1 reply; 27+ messages in thread
From: Oscar Salvador @ 2020-09-22 13:56 UTC (permalink / raw)
  To: akpm
  Cc: aris, naoya.horiguchi, mhocko, tony.luck, cai, linux-kernel,
	linux-mm, Oscar Salvador

Since get_hwpoison_page is only used in memory-failure code now, let us
un-export it and make it private to that code.

Signed-off-by: Oscar Salvador <osalvador@suse.de>
---
 include/linux/mm.h  | 1 -
 mm/memory-failure.c | 3 +--
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index 185fcef0deb6..20ac1653fb81 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -3015,7 +3015,6 @@ extern int memory_failure(unsigned long pfn, int flags);
 extern void memory_failure_queue(unsigned long pfn, int flags);
 extern void memory_failure_queue_kick(int cpu);
 extern int unpoison_memory(unsigned long pfn);
-extern int get_hwpoison_page(struct page *page);
 #define put_hwpoison_page(page)	put_page(page)
 extern int sysctl_memory_failure_early_kill;
 extern int sysctl_memory_failure_recovery;
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 0e6edffa91a9..f8cff387451e 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -924,7 +924,7 @@ static int page_action(struct page_state *ps, struct page *p,
  * Return: return 0 if failed to grab the refcount, otherwise true (some
  * non-zero value.)
  */
-int get_hwpoison_page(struct page *page)
+static int get_hwpoison_page(struct page *page)
 {
 	struct page *head = compound_head(page);
 
@@ -953,7 +953,6 @@ int get_hwpoison_page(struct page *page)
 
 	return 0;
 }
-EXPORT_SYMBOL_GPL(get_hwpoison_page);
 
 /*
  * Do all that is necessary to remove user space mappings. Unmap
-- 
2.26.2


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v7 05/14] mm,hwpoison: refactor madvise_inject_error
  2020-09-22 13:56 [PATCH v7 00/14] HWPOISON: soft offline rework Oscar Salvador
                   ` (3 preceding siblings ...)
  2020-09-22 13:56 ` [PATCH v7 04/14] mm,hwpoison: unexport get_hwpoison_page and make it static Oscar Salvador
@ 2020-09-22 13:56 ` Oscar Salvador
  2020-09-23  7:24   ` HORIGUCHI NAOYA(堀口 直也)
  2020-09-22 13:56 ` [PATCH v7 06/14] mm,hwpoison: kill put_hwpoison_page Oscar Salvador
                   ` (10 subsequent siblings)
  15 siblings, 1 reply; 27+ messages in thread
From: Oscar Salvador @ 2020-09-22 13:56 UTC (permalink / raw)
  To: akpm
  Cc: aris, naoya.horiguchi, mhocko, tony.luck, cai, linux-kernel,
	linux-mm, Oscar Salvador

Make a proper if-else condition for {hard,soft}-offline.

[akpm: refactor comment]
Signed-off-by: Oscar Salvador <osalvador@suse.de>
---
 mm/madvise.c | 30 ++++++++++++++----------------
 1 file changed, 14 insertions(+), 16 deletions(-)

diff --git a/mm/madvise.c b/mm/madvise.c
index 9b065d412e5f..f3a86d547ceb 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -872,7 +872,6 @@ static long madvise_remove(struct vm_area_struct *vma,
 static int madvise_inject_error(int behavior,
 		unsigned long start, unsigned long end)
 {
-	struct page *page;
 	struct zone *zone;
 	unsigned long size;
 
@@ -881,6 +880,7 @@ static int madvise_inject_error(int behavior,
 
 
 	for (; start < end; start += size) {
+		struct page *page;
 		unsigned long pfn;
 		int ret;
 
@@ -903,25 +903,23 @@ static int madvise_inject_error(int behavior,
 
 		if (behavior == MADV_SOFT_OFFLINE) {
 			pr_info("Soft offlining pfn %#lx at process virtual address %#lx\n",
-					pfn, start);
+				 pfn, start);
 
 			ret = soft_offline_page(pfn, MF_COUNT_INCREASED);
-			if (ret)
-				return ret;
-			continue;
+		} else {
+			pr_info("Injecting memory failure for pfn %#lx at process virtual address %#lx\n",
+				 pfn, start);
+			/*
+			 * Drop the page reference taken by
+			 * get_user_pages_fast(). In the absence of
+			 * MF_COUNT_INCREASED the memory_failure() routine is
+			 * responsible for pinning the page to prevent it
+			 * from being released back to the page allocator.
+			 */
+			put_page(page);
+			ret = memory_failure(pfn, 0);
 		}
 
-		pr_info("Injecting memory failure for pfn %#lx at process virtual address %#lx\n",
-				pfn, start);
-
-		/*
-		 * Drop the page reference taken by get_user_pages_fast(). In
-		 * the absence of MF_COUNT_INCREASED the memory_failure()
-		 * routine is responsible for pinning the page to prevent it
-		 * from being released back to the page allocator.
-		 */
-		put_page(page);
-		ret = memory_failure(pfn, 0);
 		if (ret)
 			return ret;
 	}
-- 
2.26.2


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v7 06/14] mm,hwpoison: kill put_hwpoison_page
  2020-09-22 13:56 [PATCH v7 00/14] HWPOISON: soft offline rework Oscar Salvador
                   ` (4 preceding siblings ...)
  2020-09-22 13:56 ` [PATCH v7 05/14] mm,hwpoison: refactor madvise_inject_error Oscar Salvador
@ 2020-09-22 13:56 ` Oscar Salvador
  2020-09-23  7:24   ` HORIGUCHI NAOYA(堀口 直也)
  2020-09-22 13:56 ` [PATCH v7 07/14] mm,hwpoison: unify THP handling for hard and soft offline Oscar Salvador
                   ` (9 subsequent siblings)
  15 siblings, 1 reply; 27+ messages in thread
From: Oscar Salvador @ 2020-09-22 13:56 UTC (permalink / raw)
  To: akpm
  Cc: aris, naoya.horiguchi, mhocko, tony.luck, cai, linux-kernel,
	linux-mm, Oscar Salvador

After commit 4e41a30c6d50 ("mm: hwpoison: adjust for new thp
refcounting"), put_hwpoison_page got reduced to a put_page.  Let us just
use put_page instead.

Signed-off-by: Oscar Salvador <osalvador@suse.de>
---
 include/linux/mm.h  |  1 -
 mm/memory-failure.c | 30 +++++++++++++++---------------
 2 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index 20ac1653fb81..61a80384afd3 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -3015,7 +3015,6 @@ extern int memory_failure(unsigned long pfn, int flags);
 extern void memory_failure_queue(unsigned long pfn, int flags);
 extern void memory_failure_queue_kick(int cpu);
 extern int unpoison_memory(unsigned long pfn);
-#define put_hwpoison_page(page)	put_page(page)
 extern int sysctl_memory_failure_early_kill;
 extern int sysctl_memory_failure_recovery;
 extern void shake_page(struct page *p, int access);
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index f8cff387451e..ed54c27ad668 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1143,7 +1143,7 @@ static int memory_failure_hugetlb(unsigned long pfn, int flags)
 		pr_err("Memory failure: %#lx: just unpoisoned\n", pfn);
 		num_poisoned_pages_dec();
 		unlock_page(head);
-		put_hwpoison_page(head);
+		put_page(head);
 		return 0;
 	}
 
@@ -1335,7 +1335,7 @@ int memory_failure(unsigned long pfn, int flags)
 					pfn);
 			if (TestClearPageHWPoison(p))
 				num_poisoned_pages_dec();
-			put_hwpoison_page(p);
+			put_page(p);
 			return -EBUSY;
 		}
 		unlock_page(p);
@@ -1388,14 +1388,14 @@ int memory_failure(unsigned long pfn, int flags)
 		pr_err("Memory failure: %#lx: just unpoisoned\n", pfn);
 		num_poisoned_pages_dec();
 		unlock_page(p);
-		put_hwpoison_page(p);
+		put_page(p);
 		return 0;
 	}
 	if (hwpoison_filter(p)) {
 		if (TestClearPageHWPoison(p))
 			num_poisoned_pages_dec();
 		unlock_page(p);
-		put_hwpoison_page(p);
+		put_page(p);
 		return 0;
 	}
 
@@ -1629,9 +1629,9 @@ int unpoison_memory(unsigned long pfn)
 	}
 	unlock_page(page);
 
-	put_hwpoison_page(page);
+	put_page(page);
 	if (freeit && !(pfn == my_zero_pfn(0) && page_count(p) == 1))
-		put_hwpoison_page(page);
+		put_page(page);
 
 	return 0;
 }
@@ -1692,7 +1692,7 @@ static int get_any_page(struct page *page, unsigned long pfn, int flags)
 		/*
 		 * Try to free it.
 		 */
-		put_hwpoison_page(page);
+		put_page(page);
 		shake_page(page, 1);
 
 		/*
@@ -1701,7 +1701,7 @@ static int get_any_page(struct page *page, unsigned long pfn, int flags)
 		ret = __get_any_page(page, pfn, 0);
 		if (ret == 1 && !PageLRU(page)) {
 			/* Drop page reference which is from __get_any_page() */
-			put_hwpoison_page(page);
+			put_page(page);
 			pr_info("soft_offline: %#lx: unknown non LRU page type %lx (%pGp)\n",
 				pfn, page->flags, &page->flags);
 			return -EIO;
@@ -1724,7 +1724,7 @@ static int soft_offline_huge_page(struct page *page, int flags)
 	lock_page(hpage);
 	if (PageHWPoison(hpage)) {
 		unlock_page(hpage);
-		put_hwpoison_page(hpage);
+		put_page(hpage);
 		pr_info("soft offline: %#lx hugepage already poisoned\n", pfn);
 		return -EBUSY;
 	}
@@ -1735,7 +1735,7 @@ static int soft_offline_huge_page(struct page *page, int flags)
 	 * get_any_page() and isolate_huge_page() takes a refcount each,
 	 * so need to drop one here.
 	 */
-	put_hwpoison_page(hpage);
+	put_page(hpage);
 	if (!ret) {
 		pr_info("soft offline: %#lx hugepage failed to isolate\n", pfn);
 		return -EBUSY;
@@ -1784,7 +1784,7 @@ static int __soft_offline_page(struct page *page, int flags)
 	wait_on_page_writeback(page);
 	if (PageHWPoison(page)) {
 		unlock_page(page);
-		put_hwpoison_page(page);
+		put_page(page);
 		pr_info("soft offline: %#lx page already poisoned\n", pfn);
 		return -EBUSY;
 	}
@@ -1799,7 +1799,7 @@ static int __soft_offline_page(struct page *page, int flags)
 	 * would need to fix isolation locking first.
 	 */
 	if (ret == 1) {
-		put_hwpoison_page(page);
+		put_page(page);
 		pr_info("soft_offline: %#lx: invalidated\n", pfn);
 		SetPageHWPoison(page);
 		num_poisoned_pages_inc();
@@ -1819,7 +1819,7 @@ static int __soft_offline_page(struct page *page, int flags)
 	 * Drop page reference which is came from get_any_page()
 	 * successful isolate_lru_page() already took another one.
 	 */
-	put_hwpoison_page(page);
+	put_page(page);
 	if (!ret) {
 		LIST_HEAD(pagelist);
 		/*
@@ -1863,7 +1863,7 @@ static int soft_offline_in_use_page(struct page *page, int flags)
 				pr_info("soft offline: %#lx: non anonymous thp\n", page_to_pfn(page));
 			else
 				pr_info("soft offline: %#lx: thp split failed\n", page_to_pfn(page));
-			put_hwpoison_page(page);
+			put_page(page);
 			return -EBUSY;
 		}
 		unlock_page(page);
@@ -1936,7 +1936,7 @@ int soft_offline_page(unsigned long pfn, int flags)
 	if (PageHWPoison(page)) {
 		pr_info("soft offline: %#lx page already poisoned\n", pfn);
 		if (flags & MF_COUNT_INCREASED)
-			put_hwpoison_page(page);
+			put_page(page);
 		return -EBUSY;
 	}
 
-- 
2.26.2


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v7 07/14] mm,hwpoison: unify THP handling for hard and soft offline
  2020-09-22 13:56 [PATCH v7 00/14] HWPOISON: soft offline rework Oscar Salvador
                   ` (5 preceding siblings ...)
  2020-09-22 13:56 ` [PATCH v7 06/14] mm,hwpoison: kill put_hwpoison_page Oscar Salvador
@ 2020-09-22 13:56 ` Oscar Salvador
  2020-09-23  7:26   ` HORIGUCHI NAOYA(堀口 直也)
  2020-09-22 13:56 ` [PATCH v7 08/14] mm,hwpoison: rework soft offline for free pages Oscar Salvador
                   ` (8 subsequent siblings)
  15 siblings, 1 reply; 27+ messages in thread
From: Oscar Salvador @ 2020-09-22 13:56 UTC (permalink / raw)
  To: akpm
  Cc: aris, naoya.horiguchi, mhocko, tony.luck, cai, linux-kernel,
	linux-mm, Oscar Salvador

Place the THP's page handling in a helper and use it from both hard and
soft-offline machinery, so we get rid of some duplicated code.

Signed-off-by: Oscar Salvador <osalvador@suse.de>
---
 mm/memory-failure.c | 48 +++++++++++++++++++++------------------------
 1 file changed, 22 insertions(+), 26 deletions(-)

diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index ed54c27ad668..9d80beb841a3 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1102,6 +1102,25 @@ static int identify_page_state(unsigned long pfn, struct page *p,
 	return page_action(ps, p, pfn);
 }
 
+static int try_to_split_thp_page(struct page *page, const char *msg)
+{
+	lock_page(page);
+	if (!PageAnon(page) || unlikely(split_huge_page(page))) {
+		unsigned long pfn = page_to_pfn(page);
+
+		unlock_page(page);
+		if (!PageAnon(page))
+			pr_info("%s: %#lx: non anonymous thp\n", msg, pfn);
+		else
+			pr_info("%s: %#lx: thp split failed\n", msg, pfn);
+		put_page(page);
+		return -EBUSY;
+	}
+	unlock_page(page);
+
+	return 0;
+}
+
 static int memory_failure_hugetlb(unsigned long pfn, int flags)
 {
 	struct page *p = pfn_to_page(pfn);
@@ -1324,21 +1343,8 @@ int memory_failure(unsigned long pfn, int flags)
 	}
 
 	if (PageTransHuge(hpage)) {
-		lock_page(p);
-		if (!PageAnon(p) || unlikely(split_huge_page(p))) {
-			unlock_page(p);
-			if (!PageAnon(p))
-				pr_err("Memory failure: %#lx: non anonymous thp\n",
-					pfn);
-			else
-				pr_err("Memory failure: %#lx: thp split failed\n",
-					pfn);
-			if (TestClearPageHWPoison(p))
-				num_poisoned_pages_dec();
-			put_page(p);
+		if (try_to_split_thp_page(p, "Memory Failure") < 0)
 			return -EBUSY;
-		}
-		unlock_page(p);
 		VM_BUG_ON_PAGE(!page_count(p), p);
 	}
 
@@ -1855,19 +1861,9 @@ static int soft_offline_in_use_page(struct page *page, int flags)
 	int mt;
 	struct page *hpage = compound_head(page);
 
-	if (!PageHuge(page) && PageTransHuge(hpage)) {
-		lock_page(page);
-		if (!PageAnon(page) || unlikely(split_huge_page(page))) {
-			unlock_page(page);
-			if (!PageAnon(page))
-				pr_info("soft offline: %#lx: non anonymous thp\n", page_to_pfn(page));
-			else
-				pr_info("soft offline: %#lx: thp split failed\n", page_to_pfn(page));
-			put_page(page);
+	if (!PageHuge(page) && PageTransHuge(hpage))
+		if (try_to_split_thp_page(page, "soft offline") < 0)
 			return -EBUSY;
-		}
-		unlock_page(page);
-	}
 
 	/*
 	 * Setting MIGRATE_ISOLATE here ensures that the page will be linked
-- 
2.26.2


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v7 08/14] mm,hwpoison: rework soft offline for free pages
  2020-09-22 13:56 [PATCH v7 00/14] HWPOISON: soft offline rework Oscar Salvador
                   ` (6 preceding siblings ...)
  2020-09-22 13:56 ` [PATCH v7 07/14] mm,hwpoison: unify THP handling for hard and soft offline Oscar Salvador
@ 2020-09-22 13:56 ` Oscar Salvador
  2020-09-23  7:27   ` HORIGUCHI NAOYA(堀口 直也)
  2020-09-22 13:56 ` [PATCH v7 09/14] mm,hwpoison: rework soft offline for in-use pages Oscar Salvador
                   ` (7 subsequent siblings)
  15 siblings, 1 reply; 27+ messages in thread
From: Oscar Salvador @ 2020-09-22 13:56 UTC (permalink / raw)
  To: akpm
  Cc: aris, naoya.horiguchi, mhocko, tony.luck, cai, linux-kernel,
	linux-mm, Oscar Salvador

When trying to soft-offline a free page, we need to first take it off the
buddy allocator.
Once we know is out of reach, we can safely flag it as poisoned.

take_page_off_buddy will be used to take a page meant to be poisoned off
the buddy allocator. take_page_off_buddy calls break_down_buddy_pages,
which splits a higher-order page in case our page belongs to one.

Once the page is under our control, we call page_handle_poison to set it
as poisoned and grab a refcount on it.

Signed-off-by: Oscar Salvador <osalvador@suse.de>
---
 include/linux/page-flags.h |  1 +
 mm/memory-failure.c        | 18 ++++++----
 mm/page_alloc.c            | 68 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 81 insertions(+), 6 deletions(-)

diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 7182103583d2..d0fdb59794d8 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -429,6 +429,7 @@ 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)
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 9d80beb841a3..de274356f8c7 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -65,6 +65,13 @@ 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)
+{
+	SetPageHWPoison(page);
+	page_ref_inc(page);
+	num_poisoned_pages_inc();
+}
+
 #if defined(CONFIG_HWPOISON_INJECT) || defined(CONFIG_HWPOISON_INJECT_MODULE)
 
 u32 hwpoison_filter_enable = 0;
@@ -1884,14 +1891,13 @@ static int soft_offline_in_use_page(struct page *page, int flags)
 
 static int soft_offline_free_page(struct page *page)
 {
-	int rc = dissolve_free_huge_page(page);
+	int rc = -EBUSY;
 
-	if (!rc) {
-		if (set_hwpoison_free_buddy_page(page))
-			num_poisoned_pages_inc();
-		else
-			rc = -EBUSY;
+	if (!dissolve_free_huge_page(page) && take_page_off_buddy(page)) {
+		page_handle_poison(page);
+		rc = 0;
 	}
+
 	return rc;
 }
 
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 3c30be2e8e76..e02da891d8a9 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -8758,6 +8758,74 @@ bool is_free_buddy_page(struct page *page)
 }
 
 #ifdef CONFIG_MEMORY_FAILURE
+/*
+ * Break down a higher-order page in sub-pages, and keep our target out of
+ * buddy allocator.
+ */
+static void break_down_buddy_pages(struct zone *zone, struct page *page,
+				   struct page *target, int low, int high,
+				   int migratetype)
+{
+	unsigned long size = 1 << high;
+	struct page *current_buddy, *next_page;
+
+	while (high > low) {
+		high--;
+		size >>= 1;
+
+		if (target >= &page[size]) {
+			next_page = page + size;
+			current_buddy = page;
+		} else {
+			next_page = page;
+			current_buddy = page + size;
+		}
+
+		if (set_page_guard(zone, current_buddy, high, migratetype))
+			continue;
+
+		if (current_buddy != target) {
+			add_to_free_list(current_buddy, zone, high, migratetype);
+			set_page_order(current_buddy, high);
+			page = next_page;
+		}
+	}
+}
+
+/*
+ * Take a page that will be marked as poisoned off the buddy allocator.
+ */
+bool take_page_off_buddy(struct page *page)
+{
+	struct zone *zone = page_zone(page);
+	unsigned long pfn = page_to_pfn(page);
+	unsigned long flags;
+	unsigned int order;
+	bool ret = false;
+
+	spin_lock_irqsave(&zone->lock, flags);
+	for (order = 0; order < MAX_ORDER; order++) {
+		struct page *page_head = page - (pfn & ((1 << order) - 1));
+		int buddy_order = page_order(page_head);
+
+		if (PageBuddy(page_head) && buddy_order >= order) {
+			unsigned long pfn_head = page_to_pfn(page_head);
+			int migratetype = get_pfnblock_migratetype(page_head,
+								   pfn_head);
+
+			del_page_from_free_list(page_head, zone, buddy_order);
+			break_down_buddy_pages(zone, page_head, page, 0,
+						buddy_order, migratetype);
+			ret = true;
+			break;
+		}
+		if (page_count(page_head) > 0)
+			break;
+	}
+	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
-- 
2.26.2


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v7 09/14] mm,hwpoison: rework soft offline for in-use pages
  2020-09-22 13:56 [PATCH v7 00/14] HWPOISON: soft offline rework Oscar Salvador
                   ` (7 preceding siblings ...)
  2020-09-22 13:56 ` [PATCH v7 08/14] mm,hwpoison: rework soft offline for free pages Oscar Salvador
@ 2020-09-22 13:56 ` Oscar Salvador
  2020-09-23  7:30   ` HORIGUCHI NAOYA(堀口 直也)
  2020-09-22 13:56 ` [PATCH v7 10/14] mm,hwpoison: refactor soft_offline_huge_page and __soft_offline_page Oscar Salvador
                   ` (6 subsequent siblings)
  15 siblings, 1 reply; 27+ messages in thread
From: Oscar Salvador @ 2020-09-22 13:56 UTC (permalink / raw)
  To: akpm
  Cc: aris, naoya.horiguchi, mhocko, tony.luck, cai, linux-kernel,
	linux-mm, Oscar Salvador

This patch changes the way we set and handle in-use poisoned pages.  Until
now, poisoned pages were released to the buddy allocator, trusting that
the checks that take place at allocation time would act as a safe net
and would skip that page.

This has proved to be wrong, as we got some pfn walkers out there, like
compaction, that all they care is the page to be in a buddy freelist.

Although this might not be the only user, having poisoned pages in the
buddy allocator seems a bad idea as we should only have free pages that
are ready and meant to be used as such.

Before explaining the taken approach, let us break down the kind of pages
we can soft offline.

- Anonymous THP (after the split, they end up being 4K pages)
- Hugetlb
- Order-0 pages (that can be either migrated or invalited)

* Normal pages (order-0 and anon-THP)

  - If they are clean and unmapped page cache pages, we invalidate
    then by means of invalidate_inode_page().
  - If they are mapped/dirty, we do the isolate-and-migrate dance.

Either way, do not call put_page directly from those paths.
Instead, we keep the page and send it to page_handle_poison to perform the
right handling.

page_handle_poison sets the HWPoison flag and does the last put_page.

Down the chain, we placed a check for HWPoison page in
free_pages_prepare, that just skips any poisoned page, so those pages
do not end up in any pcplist/freelist.

After that, we set the refcount on the page to 1 and we increment
the poisoned pages counter.

If we see that the check in free_pages_prepare creates trouble, we can
always do what we do for free pages:

  - wait until the page hits buddy's freelists
  - take it off, and flag it

The downside of the above approach is that we could race with an
allocation, so by the time we  want to take the page off the buddy, the
page has been already allocated so we cannot soft offline it.
But the user could always retry it.

* Hugetlb pages

  - We isolate-and-migrate them

After the migration has been successful, we call dissolve_free_huge_page,
and we set HWPoison on the page if we succeed.
Hugetlb has a slightly different handling though.

While for non-hugetlb pages we cared about closing the race with an
allocation, doing so for hugetlb pages requires quite some additional
and intrusive code (we would need to hook in free_huge_page and some other
places).
So I decided to not make the code overly complicated and just fail
normally if the page we allocated in the meantime.

We can always build on top of this.

As a bonus, because of the way we handle now in-use pages, we no longer
need the put-as-isolation-migratetype dance, that was guarding for poisoned
pages to end up in pcplists.

Signed-off-by: Oscar Salvador <osalvador@suse.de>
---
 include/linux/page-flags.h |  5 -----
 mm/memory-failure.c        | 43 +++++++++++++-------------------------
 mm/migrate.c               | 11 +++-------
 mm/page_alloc.c            | 39 ++++++++++------------------------
 4 files changed, 28 insertions(+), 70 deletions(-)

diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index d0fdb59794d8..fbbb841a9346 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -428,14 +428,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 de274356f8c7..cc8757282860 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 7eb82af9f49a..64750ef021c6 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -1223,16 +1223,11 @@ static int unmap_and_move(new_page_t get_new_page,
 	 * 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 e02da891d8a9..9a1d1e3cc099 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1173,6 +1173,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.
@@ -8825,32 +8836,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
-- 
2.26.2


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v7 10/14] mm,hwpoison: refactor soft_offline_huge_page and __soft_offline_page
  2020-09-22 13:56 [PATCH v7 00/14] HWPOISON: soft offline rework Oscar Salvador
                   ` (8 preceding siblings ...)
  2020-09-22 13:56 ` [PATCH v7 09/14] mm,hwpoison: rework soft offline for in-use pages Oscar Salvador
@ 2020-09-22 13:56 ` Oscar Salvador
  2020-09-23  7:35   ` HORIGUCHI NAOYA(堀口 直也)
  2020-09-22 13:56 ` [PATCH v7 11/14] mm,hwpoison: return 0 if the page is already poisoned in soft-offline Oscar Salvador
                   ` (5 subsequent siblings)
  15 siblings, 1 reply; 27+ messages in thread
From: Oscar Salvador @ 2020-09-22 13:56 UTC (permalink / raw)
  To: akpm
  Cc: aris, naoya.horiguchi, mhocko, tony.luck, cai, linux-kernel,
	linux-mm, Oscar Salvador

Merging soft_offline_huge_page and __soft_offline_page let us get rid of
quite some duplicated code, and makes the code much easier to follow.

Now, __soft_offline_page will handle both normal and hugetlb pages.

Signed-off-by: Oscar Salvador <osalvador@suse.de>
---
 mm/memory-failure.c | 182 ++++++++++++++++++++------------------------
 1 file changed, 82 insertions(+), 100 deletions(-)

diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index cc8757282860..7c122cca9f31 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -65,13 +65,31 @@ 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, bool release)
+static bool page_handle_poison(struct page *page, bool hugepage_or_freepage, bool release)
 {
+	if (hugepage_or_freepage) {
+		/*
+		 * Doing this check for free pages is also fine since dissolve_free_huge_page
+		 * returns 0 for non-hugetlb pages as well.
+		 */
+		if (dissolve_free_huge_page(page) || !take_page_off_buddy(page))
+			/*
+			 * We could fail to take off the target page from buddy
+			 * for example due to racy page allocaiton, but that's
+			 * acceptable because soft-offlined page is not broken
+			 * and if someone really want to use it, they should
+			 * take it.
+			 */
+			return false;
+	}
+
 	SetPageHWPoison(page);
 	if (release)
 		put_page(page);
 	page_ref_inc(page);
 	num_poisoned_pages_inc();
+
+	return true;
 }
 
 #if defined(CONFIG_HWPOISON_INJECT) || defined(CONFIG_HWPOISON_INJECT_MODULE)
@@ -1725,63 +1743,51 @@ static int get_any_page(struct page *page, unsigned long pfn, int flags)
 	return ret;
 }
 
-static int soft_offline_huge_page(struct page *page, int flags)
+static bool isolate_page(struct page *page, struct list_head *pagelist)
 {
-	int ret;
-	unsigned long pfn = page_to_pfn(page);
-	struct page *hpage = compound_head(page);
-	LIST_HEAD(pagelist);
+	bool isolated = false;
+	bool lru = PageLRU(page);
 
-	/*
-	 * This double-check of PageHWPoison is to avoid the race with
-	 * memory_failure(). See also comment in __soft_offline_page().
-	 */
-	lock_page(hpage);
-	if (PageHWPoison(hpage)) {
-		unlock_page(hpage);
-		put_page(hpage);
-		pr_info("soft offline: %#lx hugepage already poisoned\n", pfn);
-		return -EBUSY;
+	if (PageHuge(page)) {
+		isolated = isolate_huge_page(page, pagelist);
+	} else {
+		if (lru)
+			isolated = !isolate_lru_page(page);
+		else
+			isolated = !isolate_movable_page(page, ISOLATE_UNEVICTABLE);
+
+		if (isolated)
+			list_add(&page->lru, pagelist);
 	}
-	unlock_page(hpage);
 
-	ret = isolate_huge_page(hpage, &pagelist);
+	if (isolated && lru)
+		inc_node_page_state(page, NR_ISOLATED_ANON +
+				    page_is_file_lru(page));
+
 	/*
-	 * get_any_page() and isolate_huge_page() takes a refcount each,
-	 * so need to drop one here.
+	 * If we succeed to isolate the page, we grabbed another refcount on
+	 * the page, so we can safely drop the one we got from get_any_pages().
+	 * If we failed to isolate the page, it means that we cannot go further
+	 * and we will return an error, so drop the reference we got from
+	 * get_any_pages() as well.
 	 */
-	put_page(hpage);
-	if (!ret) {
-		pr_info("soft offline: %#lx hugepage failed to isolate\n", pfn);
-		return -EBUSY;
-	}
-
-	ret = migrate_pages(&pagelist, new_page, NULL, MPOL_MF_MOVE_ALL,
-				MIGRATE_SYNC, MR_MEMORY_FAILURE);
-	if (ret) {
-		pr_info("soft offline: %#lx: hugepage migration failed %d, type %lx (%pGp)\n",
-			pfn, ret, page->flags, &page->flags);
-		if (!list_empty(&pagelist))
-			putback_movable_pages(&pagelist);
-		if (ret > 0)
-			ret = -EIO;
-	} else {
-		/*
-		 * We set PG_hwpoison only when we were able to take the page
-		 * off the buddy.
-		 */
-		if (!dissolve_free_huge_page(page) && take_page_off_buddy(page))
-			page_handle_poison(page, false);
-		else
-			ret = -EBUSY;
-	}
-	return ret;
+	put_page(page);
+	return isolated;
 }
 
-static int __soft_offline_page(struct page *page, int flags)
+/*
+ * __soft_offline_page handles hugetlb-pages and non-hugetlb pages.
+ * If the page is a non-dirty unmapped page-cache page, it simply invalidates.
+ * If the page is mapped, it migrates the contents over.
+ */
+static int __soft_offline_page(struct page *page)
 {
-	int ret;
+	int ret = 0;
 	unsigned long pfn = page_to_pfn(page);
+	struct page *hpage = compound_head(page);
+	char const *msg_page[] = {"page", "hugepage"};
+	bool huge = PageHuge(page);
+	LIST_HEAD(pagelist);
 
 	/*
 	 * Check PageHWPoison again inside page lock because PageHWPoison
@@ -1790,98 +1796,74 @@ static int __soft_offline_page(struct page *page, int flags)
 	 * so there's no race between soft_offline_page() and memory_failure().
 	 */
 	lock_page(page);
-	wait_on_page_writeback(page);
+	if (!PageHuge(page))
+		wait_on_page_writeback(page);
 	if (PageHWPoison(page)) {
 		unlock_page(page);
 		put_page(page);
 		pr_info("soft offline: %#lx page already poisoned\n", pfn);
 		return -EBUSY;
 	}
-	/*
-	 * Try to invalidate first. This should work for
-	 * non dirty unmapped page cache pages.
-	 */
-	ret = invalidate_inode_page(page);
+
+	if (!PageHuge(page))
+		/*
+		 * Try to invalidate first. This should work for
+		 * non dirty unmapped page cache pages.
+		 */
+		ret = invalidate_inode_page(page);
 	unlock_page(page);
+
 	/*
 	 * RED-PEN would be better to keep it isolated here, but we
 	 * would need to fix isolation locking first.
 	 */
-	if (ret == 1) {
+	if (ret) {
 		pr_info("soft_offline: %#lx: invalidated\n", pfn);
-		page_handle_poison(page, true);
+		page_handle_poison(page, false, true);
 		return 0;
 	}
 
-	/*
-	 * Simple invalidation didn't work.
-	 * Try to migrate to a new page instead. migrate.c
-	 * handles a large number of cases for us.
-	 */
-	if (PageLRU(page))
-		ret = isolate_lru_page(page);
-	else
-		ret = isolate_movable_page(page, ISOLATE_UNEVICTABLE);
-	/*
-	 * Drop page reference which is came from get_any_page()
-	 * successful isolate_lru_page() already took another one.
-	 */
-	put_page(page);
-	if (!ret) {
-		LIST_HEAD(pagelist);
-		/*
-		 * After isolated lru page, the PageLRU will be cleared,
-		 * so use !__PageMovable instead for LRU page's mapping
-		 * cannot have PAGE_MAPPING_MOVABLE.
-		 */
-		if (!__PageMovable(page))
-			inc_node_page_state(page, NR_ISOLATED_ANON +
-						page_is_file_lru(page));
-		list_add(&page->lru, &pagelist);
+	if (isolate_page(hpage, &pagelist)) {
 		ret = migrate_pages(&pagelist, new_page, NULL, MPOL_MF_MOVE_ALL,
 					MIGRATE_SYNC, MR_MEMORY_FAILURE);
 		if (!ret) {
-			page_handle_poison(page, true);
+			bool release = !huge;
+
+			if (!page_handle_poison(page, huge, release))
+				ret = -EBUSY;
 		} else {
 			if (!list_empty(&pagelist))
 				putback_movable_pages(&pagelist);
 
-			pr_info("soft offline: %#lx: migration failed %d, type %lx (%pGp)\n",
-				pfn, ret, page->flags, &page->flags);
+			pr_info("soft offline: %#lx: %s migration failed %d, type %lx (%pGp)\n",
+				pfn, msg_page[huge], ret, page->flags, &page->flags);
 			if (ret > 0)
 				ret = -EIO;
 		}
 	} else {
-		pr_info("soft offline: %#lx: isolation failed: %d, page count %d, type %lx (%pGp)\n",
-			pfn, ret, page_count(page), page->flags, &page->flags);
+		pr_info("soft offline: %#lx: %s isolation failed: %d, page count %d, type %lx (%pGp)\n",
+			pfn, msg_page[huge], ret, page_count(page), page->flags, &page->flags);
+		ret = -EBUSY;
 	}
 	return ret;
 }
 
-static int soft_offline_in_use_page(struct page *page, int flags)
+static int soft_offline_in_use_page(struct page *page)
 {
-	int ret;
 	struct page *hpage = compound_head(page);
 
 	if (!PageHuge(page) && PageTransHuge(hpage))
 		if (try_to_split_thp_page(page, "soft offline") < 0)
 			return -EBUSY;
-
-	if (PageHuge(page))
-		ret = soft_offline_huge_page(page, flags);
-	else
-		ret = __soft_offline_page(page, flags);
-	return ret;
+	return __soft_offline_page(page);
 }
 
 static int soft_offline_free_page(struct page *page)
 {
-	int rc = -EBUSY;
+	int rc = 0;
 
-	if (!dissolve_free_huge_page(page) && take_page_off_buddy(page)) {
-		page_handle_poison(page, false);
-		rc = 0;
-	}
+	if (!page_handle_poison(page, true, false))
+		rc = -EBUSY;
 
 	return rc;
 }
@@ -1932,7 +1914,7 @@ int soft_offline_page(unsigned long pfn, int flags)
 	put_online_mems();
 
 	if (ret > 0)
-		ret = soft_offline_in_use_page(page, flags);
+		ret = soft_offline_in_use_page(page);
 	else if (ret == 0)
 		ret = soft_offline_free_page(page);
 
-- 
2.26.2


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v7 11/14] mm,hwpoison: return 0 if the page is already poisoned in soft-offline
  2020-09-22 13:56 [PATCH v7 00/14] HWPOISON: soft offline rework Oscar Salvador
                   ` (9 preceding siblings ...)
  2020-09-22 13:56 ` [PATCH v7 10/14] mm,hwpoison: refactor soft_offline_huge_page and __soft_offline_page Oscar Salvador
@ 2020-09-22 13:56 ` Oscar Salvador
  2020-09-23  7:39   ` HORIGUCHI NAOYA(堀口 直也)
  2020-09-22 13:56 ` [PATCH v7 12/14] mm,hwpoison: introduce MF_MSG_UNSPLIT_THP Oscar Salvador
                   ` (4 subsequent siblings)
  15 siblings, 1 reply; 27+ messages in thread
From: Oscar Salvador @ 2020-09-22 13:56 UTC (permalink / raw)
  To: akpm
  Cc: aris, naoya.horiguchi, mhocko, tony.luck, cai, linux-kernel,
	linux-mm, Oscar Salvador

Currently, there is an inconsistency when calling soft-offline from
different paths on a page that is already poisoned.

1) madvise:

        madvise_inject_error skips any poisoned page and continues
        the loop.
        If that was the only page to madvise, it returns 0.

2) /sys/devices/system/memory/:

        When calling soft_offline_page_store()->soft_offline_page(),
        we return -EBUSY in case the page is already poisoned.
        This is inconsistent with a) the above example and b)
        memory_failure, where we return 0 if the page was poisoned.

Fix this by dropping the PageHWPoison() check in madvise_inject_error, and
let soft_offline_page return 0 if it finds the page already poisoned.

Please, note that this represents a user-api change, since now the return
error when calling soft_offline_page_store()->soft_offline_page() will be
different.

Signed-off-by: Oscar Salvador <osalvador@suse.de>
---
 mm/madvise.c        | 5 -----
 mm/memory-failure.c | 4 ++--
 2 files changed, 2 insertions(+), 7 deletions(-)

diff --git a/mm/madvise.c b/mm/madvise.c
index f3a86d547ceb..5ab978b828f0 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -896,11 +896,6 @@ static int madvise_inject_error(int behavior,
 		 */
 		size = page_size(compound_head(page));
 
-		if (PageHWPoison(page)) {
-			put_page(page);
-			continue;
-		}
-
 		if (behavior == MADV_SOFT_OFFLINE) {
 			pr_info("Soft offlining pfn %#lx at process virtual address %#lx\n",
 				 pfn, start);
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 7c122cca9f31..087f070b06b5 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1802,7 +1802,7 @@ static int __soft_offline_page(struct page *page)
 		unlock_page(page);
 		put_page(page);
 		pr_info("soft offline: %#lx page already poisoned\n", pfn);
-		return -EBUSY;
+		return 0;
 	}
 
 	if (!PageHuge(page))
@@ -1906,7 +1906,7 @@ int soft_offline_page(unsigned long pfn, int flags)
 		pr_info("soft offline: %#lx page already poisoned\n", pfn);
 		if (flags & MF_COUNT_INCREASED)
 			put_page(page);
-		return -EBUSY;
+		return 0;
 	}
 
 	get_online_mems();
-- 
2.26.2


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v7 12/14] mm,hwpoison: introduce MF_MSG_UNSPLIT_THP
  2020-09-22 13:56 [PATCH v7 00/14] HWPOISON: soft offline rework Oscar Salvador
                   ` (10 preceding siblings ...)
  2020-09-22 13:56 ` [PATCH v7 11/14] mm,hwpoison: return 0 if the page is already poisoned in soft-offline Oscar Salvador
@ 2020-09-22 13:56 ` Oscar Salvador
  2020-09-22 13:56 ` [PATCH v7 13/14] mm,hwpoison: double-check page count in __get_any_page() Oscar Salvador
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 27+ messages in thread
From: Oscar Salvador @ 2020-09-22 13:56 UTC (permalink / raw)
  To: akpm
  Cc: aris, naoya.horiguchi, mhocko, tony.luck, cai, linux-kernel,
	linux-mm, Oscar Salvador

From: Naoya Horiguchi <naoya.horiguchi@nec.com>

memory_failure() is supposed to call action_result() when it handles a
memory error event, but there's one missing case.  So let's add it.

I find that include/ras/ras_event.h has some other MF_MSG_* undefined, so
this patch also adds them.

Signed-off-by: Naoya Horiguchi <naoya.horiguchi@nec.com>
Signed-off-by: Oscar Salvador <osalvador@suse.de>
---
 include/linux/mm.h      | 1 +
 include/ras/ras_event.h | 3 +++
 mm/memory-failure.c     | 5 ++++-
 3 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index 61a80384afd3..2c09b3f2d912 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -3054,6 +3054,7 @@ enum mf_action_page_type {
 	MF_MSG_BUDDY,
 	MF_MSG_BUDDY_2ND,
 	MF_MSG_DAX,
+	MF_MSG_UNSPLIT_THP,
 	MF_MSG_UNKNOWN,
 };
 
diff --git a/include/ras/ras_event.h b/include/ras/ras_event.h
index 36c5c5e38c1d..0bdbc0d17d2f 100644
--- a/include/ras/ras_event.h
+++ b/include/ras/ras_event.h
@@ -361,6 +361,7 @@ TRACE_EVENT(aer_event,
 	EM ( MF_MSG_POISONED_HUGE, "huge page already hardware poisoned" )	\
 	EM ( MF_MSG_HUGE, "huge page" )					\
 	EM ( MF_MSG_FREE_HUGE, "free huge page" )			\
+	EM ( MF_MSG_NON_PMD_HUGE, "non-pmd-sized huge page" )		\
 	EM ( MF_MSG_UNMAP_FAILED, "unmapping failed page" )		\
 	EM ( MF_MSG_DIRTY_SWAPCACHE, "dirty swapcache page" )		\
 	EM ( MF_MSG_CLEAN_SWAPCACHE, "clean swapcache page" )		\
@@ -373,6 +374,8 @@ TRACE_EVENT(aer_event,
 	EM ( MF_MSG_TRUNCATED_LRU, "already truncated LRU page" )	\
 	EM ( MF_MSG_BUDDY, "free buddy page" )				\
 	EM ( MF_MSG_BUDDY_2ND, "free buddy page (2nd try)" )		\
+	EM ( MF_MSG_DAX, "dax page" )					\
+	EM ( MF_MSG_UNSPLIT_THP, "unsplit thp" )			\
 	EMe ( MF_MSG_UNKNOWN, "unknown page" )
 
 /*
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 087f070b06b5..963fd9af23ab 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -582,6 +582,7 @@ static const char * const action_page_types[] = {
 	[MF_MSG_BUDDY]			= "free buddy page",
 	[MF_MSG_BUDDY_2ND]		= "free buddy page (2nd try)",
 	[MF_MSG_DAX]			= "dax page",
+	[MF_MSG_UNSPLIT_THP]		= "unsplit thp",
 	[MF_MSG_UNKNOWN]		= "unknown page",
 };
 
@@ -1370,8 +1371,10 @@ int memory_failure(unsigned long pfn, int flags)
 	}
 
 	if (PageTransHuge(hpage)) {
-		if (try_to_split_thp_page(p, "Memory Failure") < 0)
+		if (try_to_split_thp_page(p, "Memory Failure") < 0) {
+			action_result(pfn, MF_MSG_UNSPLIT_THP, MF_IGNORED);
 			return -EBUSY;
+		}
 		VM_BUG_ON_PAGE(!page_count(p), p);
 	}
 
-- 
2.26.2


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v7 13/14] mm,hwpoison: double-check page count in __get_any_page()
  2020-09-22 13:56 [PATCH v7 00/14] HWPOISON: soft offline rework Oscar Salvador
                   ` (11 preceding siblings ...)
  2020-09-22 13:56 ` [PATCH v7 12/14] mm,hwpoison: introduce MF_MSG_UNSPLIT_THP Oscar Salvador
@ 2020-09-22 13:56 ` Oscar Salvador
  2020-09-22 13:56 ` [PATCH v7 14/14] mm,hwpoison: Try to narrow window race for free pages Oscar Salvador
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 27+ messages in thread
From: Oscar Salvador @ 2020-09-22 13:56 UTC (permalink / raw)
  To: akpm
  Cc: aris, naoya.horiguchi, mhocko, tony.luck, cai, linux-kernel, linux-mm

From: Naoya Horiguchi <naoya.horiguchi@nec.com>

Soft offlining could fail with EIO due to the race condition with hugepage
migration.  This issuse became visible due to the change by previous patch
that makes soft offline handler take page refcount by its own.  We have no
way to directly pin zero refcount page, and the page considered as a zero
refcount page could be allocated just after the first check.

This patch adds the second check to find the race and gives us chance to
handle it more reliably.

Signed-off-by: Naoya Horiguchi <naoya.horiguchi@nec.com>
Reported-by: Qian Cai <cai@lca.pw>
---
 mm/memory-failure.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 963fd9af23ab..46b1821d2817 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1707,6 +1707,9 @@ static int __get_any_page(struct page *p, unsigned long pfn, int flags)
 		} else if (is_free_buddy_page(p)) {
 			pr_info("%s: %#lx free buddy page\n", __func__, pfn);
 			ret = 0;
+		} else if (page_count(p)) {
+			/* raced with allocation */
+			ret = -EBUSY;
 		} else {
 			pr_info("%s: %#lx: unknown zero refcount page type %lx\n",
 				__func__, pfn, p->flags);
@@ -1723,6 +1726,9 @@ static int get_any_page(struct page *page, unsigned long pfn, int flags)
 {
 	int ret = __get_any_page(page, pfn, flags);
 
+	if (ret == -EBUSY)
+		ret = __get_any_page(page, pfn, flags);
+
 	if (ret == 1 && !PageHuge(page) &&
 	    !PageLRU(page) && !__PageMovable(page)) {
 		/*
-- 
2.26.2


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v7 14/14] mm,hwpoison: Try to narrow window race for free pages
  2020-09-22 13:56 [PATCH v7 00/14] HWPOISON: soft offline rework Oscar Salvador
                   ` (12 preceding siblings ...)
  2020-09-22 13:56 ` [PATCH v7 13/14] mm,hwpoison: double-check page count in __get_any_page() Oscar Salvador
@ 2020-09-22 13:56 ` Oscar Salvador
  2020-09-23  7:40   ` HORIGUCHI NAOYA(堀口 直也)
  2020-09-22 17:03 ` [PATCH v7 00/14] HWPOISON: soft offline rework Andrew Morton
  2020-09-23 13:29 ` Aristeu Rozanski
  15 siblings, 1 reply; 27+ messages in thread
From: Oscar Salvador @ 2020-09-22 13:56 UTC (permalink / raw)
  To: akpm
  Cc: aris, naoya.horiguchi, mhocko, tony.luck, cai, linux-kernel,
	linux-mm, Oscar Salvador

Aristeu Rozanski reported that a customer test case started
to report -EBUSY after the hwpoison rework patchset.

There is a race window between spotting a free page and taking it off
its buddy freelist, so it might be that by the time we try to take it off,
the page has been already allocated.

This patch tries to handle such race window by trying to handle the new
type of page again if the page was allocated under us.

Signed-off-by: Oscar Salvador <osalvador@suse.de>
Reported-by: Aristeu Rozanski <aris@ruivo.org>
Tested-by: Aristeu Rozanski <aris@ruivo.org>
---
 mm/memory-failure.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 46b1821d2817..8f23d3c7a0a2 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -1903,6 +1903,7 @@ int soft_offline_page(unsigned long pfn, int flags)
 {
 	int ret;
 	struct page *page;
+	bool try_again = true;
 
 	if (!pfn_valid(pfn))
 		return -ENXIO;
@@ -1918,6 +1919,7 @@ int soft_offline_page(unsigned long pfn, int flags)
 		return 0;
 	}
 
+retry:
 	get_online_mems();
 	ret = get_any_page(page, pfn, flags);
 	put_online_mems();
@@ -1925,7 +1927,10 @@ int soft_offline_page(unsigned long pfn, int flags)
 	if (ret > 0)
 		ret = soft_offline_in_use_page(page);
 	else if (ret == 0)
-		ret = soft_offline_free_page(page);
+		if (soft_offline_free_page(page) && try_again) {
+			try_again = false;
+			goto retry;
+		}
 
 	return ret;
 }
-- 
2.26.2


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v7 00/14] HWPOISON: soft offline rework
  2020-09-22 13:56 [PATCH v7 00/14] HWPOISON: soft offline rework Oscar Salvador
                   ` (13 preceding siblings ...)
  2020-09-22 13:56 ` [PATCH v7 14/14] mm,hwpoison: Try to narrow window race for free pages Oscar Salvador
@ 2020-09-22 17:03 ` Andrew Morton
  2020-09-22 17:56   ` osalvador
  2020-09-23 13:29 ` Aristeu Rozanski
  15 siblings, 1 reply; 27+ messages in thread
From: Andrew Morton @ 2020-09-22 17:03 UTC (permalink / raw)
  To: Oscar Salvador
  Cc: aris, naoya.horiguchi, mhocko, tony.luck, cai, linux-kernel, linux-mm

On Tue, 22 Sep 2020 15:56:36 +0200 Oscar Salvador <osalvador@suse.de> wrote:

> This patchset is the latest version of soft offline rework patchset
> targetted for v5.9.

Thanks.

Where do we now stand with the followon patches:

mmhwpoison-take-free-pages-off-the-buddy-freelists.patch
mmhwpoison-drain-pcplists-before-bailing-out-for-non-buddy-zero-refcount-page.patch
mmhwpoison-drop-unneeded-pcplist-draining.patch
mmhwpoison-drop-unneeded-pcplist-draining-fix.patch
mmhwpoison-remove-stale-code.patch

I don't have a record of these having been reviewed?

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v7 00/14] HWPOISON: soft offline rework
  2020-09-22 17:03 ` [PATCH v7 00/14] HWPOISON: soft offline rework Andrew Morton
@ 2020-09-22 17:56   ` osalvador
  0 siblings, 0 replies; 27+ messages in thread
From: osalvador @ 2020-09-22 17:56 UTC (permalink / raw)
  To: Andrew Morton
  Cc: aris, naoya.horiguchi, mhocko, tony.luck, cai, linux-kernel, linux-mm

On 2020-09-22 19:03, Andrew Morton wrote:
> On Tue, 22 Sep 2020 15:56:36 +0200 Oscar Salvador <osalvador@suse.de> 
> wrote:
> 
>> This patchset is the latest version of soft offline rework patchset
>> targetted for v5.9.
> 
> Thanks.
> 
> Where do we now stand with the followon patches:
> 
> mmhwpoison-take-free-pages-off-the-buddy-freelists.patch
> mmhwpoison-drain-pcplists-before-bailing-out-for-non-buddy-zero-refcount-page.patch
> mmhwpoison-drop-unneeded-pcplist-draining.patch
> mmhwpoison-drop-unneeded-pcplist-draining-fix.patch
> mmhwpoison-remove-stale-code.patch
> 
> I don't have a record of these having been reviewed?

Hi Andrew,

I would drop those for now as they depend on this work, and I would 
rather have this patchset settled first.

Once things are calm, I will resend the other ones and I will ask Naoya 
to review it.

Thanks!


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v7 04/14] mm,hwpoison: unexport get_hwpoison_page and make it static
  2020-09-22 13:56 ` [PATCH v7 04/14] mm,hwpoison: unexport get_hwpoison_page and make it static Oscar Salvador
@ 2020-09-23  7:24   ` HORIGUCHI NAOYA(堀口 直也)
  0 siblings, 0 replies; 27+ messages in thread
From: HORIGUCHI NAOYA(堀口 直也) @ 2020-09-23  7:24 UTC (permalink / raw)
  To: Oscar Salvador; +Cc: akpm, aris, mhocko, tony.luck, cai, linux-kernel, linux-mm

On Tue, Sep 22, 2020 at 03:56:40PM +0200, Oscar Salvador wrote:
> Since get_hwpoison_page is only used in memory-failure code now, let us
> un-export it and make it private to that code.
> 
> Signed-off-by: Oscar Salvador <osalvador@suse.de>

Acked-by: Naoya Horiguchi <naoya.horiguchi@nec.com>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v7 05/14] mm,hwpoison: refactor madvise_inject_error
  2020-09-22 13:56 ` [PATCH v7 05/14] mm,hwpoison: refactor madvise_inject_error Oscar Salvador
@ 2020-09-23  7:24   ` HORIGUCHI NAOYA(堀口 直也)
  0 siblings, 0 replies; 27+ messages in thread
From: HORIGUCHI NAOYA(堀口 直也) @ 2020-09-23  7:24 UTC (permalink / raw)
  To: Oscar Salvador; +Cc: akpm, aris, mhocko, tony.luck, cai, linux-kernel, linux-mm

On Tue, Sep 22, 2020 at 03:56:41PM +0200, Oscar Salvador wrote:
> Make a proper if-else condition for {hard,soft}-offline.
> 
> [akpm: refactor comment]
> Signed-off-by: Oscar Salvador <osalvador@suse.de>

Acked-by: Naoya Horiguchi <naoya.horiguchi@nec.com>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v7 06/14] mm,hwpoison: kill put_hwpoison_page
  2020-09-22 13:56 ` [PATCH v7 06/14] mm,hwpoison: kill put_hwpoison_page Oscar Salvador
@ 2020-09-23  7:24   ` HORIGUCHI NAOYA(堀口 直也)
  0 siblings, 0 replies; 27+ messages in thread
From: HORIGUCHI NAOYA(堀口 直也) @ 2020-09-23  7:24 UTC (permalink / raw)
  To: Oscar Salvador; +Cc: akpm, aris, mhocko, tony.luck, cai, linux-kernel, linux-mm

On Tue, Sep 22, 2020 at 03:56:42PM +0200, Oscar Salvador wrote:
> After commit 4e41a30c6d50 ("mm: hwpoison: adjust for new thp
> refcounting"), put_hwpoison_page got reduced to a put_page.  Let us just
> use put_page instead.
> 
> Signed-off-by: Oscar Salvador <osalvador@suse.de>

Acked-by: Naoya Horiguchi <naoya.horiguchi@nec.com>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v7 07/14] mm,hwpoison: unify THP handling for hard and soft offline
  2020-09-22 13:56 ` [PATCH v7 07/14] mm,hwpoison: unify THP handling for hard and soft offline Oscar Salvador
@ 2020-09-23  7:26   ` HORIGUCHI NAOYA(堀口 直也)
  0 siblings, 0 replies; 27+ messages in thread
From: HORIGUCHI NAOYA(堀口 直也) @ 2020-09-23  7:26 UTC (permalink / raw)
  To: Oscar Salvador; +Cc: akpm, aris, mhocko, tony.luck, cai, linux-kernel, linux-mm

On Tue, Sep 22, 2020 at 03:56:43PM +0200, Oscar Salvador wrote:
> Place the THP's page handling in a helper and use it from both hard and
> soft-offline machinery, so we get rid of some duplicated code.
> 
> Signed-off-by: Oscar Salvador <osalvador@suse.de>

Acked-by: Naoya Horiguchi <naoya.horiguchi@nec.com>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v7 08/14] mm,hwpoison: rework soft offline for free pages
  2020-09-22 13:56 ` [PATCH v7 08/14] mm,hwpoison: rework soft offline for free pages Oscar Salvador
@ 2020-09-23  7:27   ` HORIGUCHI NAOYA(堀口 直也)
  0 siblings, 0 replies; 27+ messages in thread
From: HORIGUCHI NAOYA(堀口 直也) @ 2020-09-23  7:27 UTC (permalink / raw)
  To: Oscar Salvador; +Cc: akpm, aris, mhocko, tony.luck, cai, linux-kernel, linux-mm

On Tue, Sep 22, 2020 at 03:56:44PM +0200, Oscar Salvador wrote:
> When trying to soft-offline a free page, we need to first take it off the
> buddy allocator.
> Once we know is out of reach, we can safely flag it as poisoned.
> 
> take_page_off_buddy will be used to take a page meant to be poisoned off
> the buddy allocator. take_page_off_buddy calls break_down_buddy_pages,
> which splits a higher-order page in case our page belongs to one.
> 
> Once the page is under our control, we call page_handle_poison to set it
> as poisoned and grab a refcount on it.
> 
> Signed-off-by: Oscar Salvador <osalvador@suse.de>

Acked-by: Naoya Horiguchi <naoya.horiguchi@nec.com>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v7 09/14] mm,hwpoison: rework soft offline for in-use pages
  2020-09-22 13:56 ` [PATCH v7 09/14] mm,hwpoison: rework soft offline for in-use pages Oscar Salvador
@ 2020-09-23  7:30   ` HORIGUCHI NAOYA(堀口 直也)
  0 siblings, 0 replies; 27+ messages in thread
From: HORIGUCHI NAOYA(堀口 直也) @ 2020-09-23  7:30 UTC (permalink / raw)
  To: Oscar Salvador; +Cc: akpm, aris, mhocko, tony.luck, cai, linux-kernel, linux-mm

On Tue, Sep 22, 2020 at 03:56:45PM +0200, Oscar Salvador wrote:
> This patch changes the way we set and handle in-use poisoned pages.  Until
> now, poisoned pages were released to the buddy allocator, trusting that
> the checks that take place at allocation time would act as a safe net
> and would skip that page.
> 
> This has proved to be wrong, as we got some pfn walkers out there, like
> compaction, that all they care is the page to be in a buddy freelist.
> 
> Although this might not be the only user, having poisoned pages in the
> buddy allocator seems a bad idea as we should only have free pages that
> are ready and meant to be used as such.
> 
> Before explaining the taken approach, let us break down the kind of pages
> we can soft offline.
> 
> - Anonymous THP (after the split, they end up being 4K pages)
> - Hugetlb
> - Order-0 pages (that can be either migrated or invalited)
> 
> * Normal pages (order-0 and anon-THP)
> 
>   - If they are clean and unmapped page cache pages, we invalidate
>     then by means of invalidate_inode_page().
>   - If they are mapped/dirty, we do the isolate-and-migrate dance.
> 
> Either way, do not call put_page directly from those paths.
> Instead, we keep the page and send it to page_handle_poison to perform the
> right handling.
> 
> page_handle_poison sets the HWPoison flag and does the last put_page.
> 
> Down the chain, we placed a check for HWPoison page in
> free_pages_prepare, that just skips any poisoned page, so those pages
> do not end up in any pcplist/freelist.
> 
> After that, we set the refcount on the page to 1 and we increment
> the poisoned pages counter.
> 
> If we see that the check in free_pages_prepare creates trouble, we can
> always do what we do for free pages:
> 
>   - wait until the page hits buddy's freelists
>   - take it off, and flag it
> 
> The downside of the above approach is that we could race with an
> allocation, so by the time we  want to take the page off the buddy, the
> page has been already allocated so we cannot soft offline it.
> But the user could always retry it.
> 
> * Hugetlb pages
> 
>   - We isolate-and-migrate them
> 
> After the migration has been successful, we call dissolve_free_huge_page,
> and we set HWPoison on the page if we succeed.
> Hugetlb has a slightly different handling though.
> 
> While for non-hugetlb pages we cared about closing the race with an
> allocation, doing so for hugetlb pages requires quite some additional
> and intrusive code (we would need to hook in free_huge_page and some other
> places).
> So I decided to not make the code overly complicated and just fail
> normally if the page we allocated in the meantime.
> 
> We can always build on top of this.
> 
> As a bonus, because of the way we handle now in-use pages, we no longer
> need the put-as-isolation-migratetype dance, that was guarding for poisoned
> pages to end up in pcplists.
> 
> Signed-off-by: Oscar Salvador <osalvador@suse.de>

Acked-by: Naoya Horiguchi <naoya.horiguchi@nec.com>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v7 10/14] mm,hwpoison: refactor soft_offline_huge_page and __soft_offline_page
  2020-09-22 13:56 ` [PATCH v7 10/14] mm,hwpoison: refactor soft_offline_huge_page and __soft_offline_page Oscar Salvador
@ 2020-09-23  7:35   ` HORIGUCHI NAOYA(堀口 直也)
  0 siblings, 0 replies; 27+ messages in thread
From: HORIGUCHI NAOYA(堀口 直也) @ 2020-09-23  7:35 UTC (permalink / raw)
  To: Oscar Salvador; +Cc: akpm, aris, mhocko, tony.luck, cai, linux-kernel, linux-mm

On Tue, Sep 22, 2020 at 03:56:46PM +0200, Oscar Salvador wrote:
> Merging soft_offline_huge_page and __soft_offline_page let us get rid of
> quite some duplicated code, and makes the code much easier to follow.
> 
> Now, __soft_offline_page will handle both normal and hugetlb pages.
> 
> Signed-off-by: Oscar Salvador <osalvador@suse.de>

Acked-by: Naoya Horiguchi <naoya.horiguchi@nec.com>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v7 11/14] mm,hwpoison: return 0 if the page is already poisoned in soft-offline
  2020-09-22 13:56 ` [PATCH v7 11/14] mm,hwpoison: return 0 if the page is already poisoned in soft-offline Oscar Salvador
@ 2020-09-23  7:39   ` HORIGUCHI NAOYA(堀口 直也)
  0 siblings, 0 replies; 27+ messages in thread
From: HORIGUCHI NAOYA(堀口 直也) @ 2020-09-23  7:39 UTC (permalink / raw)
  To: Oscar Salvador; +Cc: akpm, aris, mhocko, tony.luck, cai, linux-kernel, linux-mm

On Tue, Sep 22, 2020 at 03:56:47PM +0200, Oscar Salvador wrote:
> Currently, there is an inconsistency when calling soft-offline from
> different paths on a page that is already poisoned.
> 
> 1) madvise:
> 
>         madvise_inject_error skips any poisoned page and continues
>         the loop.
>         If that was the only page to madvise, it returns 0.
> 
> 2) /sys/devices/system/memory/:
> 
>         When calling soft_offline_page_store()->soft_offline_page(),
>         we return -EBUSY in case the page is already poisoned.
>         This is inconsistent with a) the above example and b)
>         memory_failure, where we return 0 if the page was poisoned.
> 
> Fix this by dropping the PageHWPoison() check in madvise_inject_error, and
> let soft_offline_page return 0 if it finds the page already poisoned.
> 
> Please, note that this represents a user-api change, since now the return
> error when calling soft_offline_page_store()->soft_offline_page() will be
> different.
> 
> Signed-off-by: Oscar Salvador <osalvador@suse.de>

Acked-by: Naoya Horiguchi <naoya.horiguchi@nec.com>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v7 14/14] mm,hwpoison: Try to narrow window race for free pages
  2020-09-22 13:56 ` [PATCH v7 14/14] mm,hwpoison: Try to narrow window race for free pages Oscar Salvador
@ 2020-09-23  7:40   ` HORIGUCHI NAOYA(堀口 直也)
  0 siblings, 0 replies; 27+ messages in thread
From: HORIGUCHI NAOYA(堀口 直也) @ 2020-09-23  7:40 UTC (permalink / raw)
  To: Oscar Salvador; +Cc: akpm, aris, mhocko, tony.luck, cai, linux-kernel, linux-mm

On Tue, Sep 22, 2020 at 03:56:50PM +0200, Oscar Salvador wrote:
> Aristeu Rozanski reported that a customer test case started
> to report -EBUSY after the hwpoison rework patchset.
> 
> There is a race window between spotting a free page and taking it off
> its buddy freelist, so it might be that by the time we try to take it off,
> the page has been already allocated.
> 
> This patch tries to handle such race window by trying to handle the new
> type of page again if the page was allocated under us.
> 
> Signed-off-by: Oscar Salvador <osalvador@suse.de>
> Reported-by: Aristeu Rozanski <aris@ruivo.org>
> Tested-by: Aristeu Rozanski <aris@ruivo.org>

Acked-by: Naoya Horiguchi <naoya.horiguchi@nec.com>

> ---
>  mm/memory-failure.c | 7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/mm/memory-failure.c b/mm/memory-failure.c
> index 46b1821d2817..8f23d3c7a0a2 100644
> --- a/mm/memory-failure.c
> +++ b/mm/memory-failure.c
> @@ -1903,6 +1903,7 @@ int soft_offline_page(unsigned long pfn, int flags)
>  {
>  	int ret;
>  	struct page *page;
> +	bool try_again = true;
>  
>  	if (!pfn_valid(pfn))
>  		return -ENXIO;
> @@ -1918,6 +1919,7 @@ int soft_offline_page(unsigned long pfn, int flags)
>  		return 0;
>  	}
>  
> +retry:
>  	get_online_mems();
>  	ret = get_any_page(page, pfn, flags);
>  	put_online_mems();
> @@ -1925,7 +1927,10 @@ int soft_offline_page(unsigned long pfn, int flags)
>  	if (ret > 0)
>  		ret = soft_offline_in_use_page(page);
>  	else if (ret == 0)
> -		ret = soft_offline_free_page(page);
> +		if (soft_offline_free_page(page) && try_again) {
> +			try_again = false;
> +			goto retry;
> +		}
>  
>  	return ret;
>  }
> -- 
> 2.26.2
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v7 00/14] HWPOISON: soft offline rework
  2020-09-22 13:56 [PATCH v7 00/14] HWPOISON: soft offline rework Oscar Salvador
                   ` (14 preceding siblings ...)
  2020-09-22 17:03 ` [PATCH v7 00/14] HWPOISON: soft offline rework Andrew Morton
@ 2020-09-23 13:29 ` Aristeu Rozanski
  15 siblings, 0 replies; 27+ messages in thread
From: Aristeu Rozanski @ 2020-09-23 13:29 UTC (permalink / raw)
  To: Oscar Salvador
  Cc: akpm, naoya.horiguchi, mhocko, tony.luck, cai, linux-kernel, linux-mm

Hi Oscar,

On Tue, Sep 22, 2020 at 03:56:36PM +0200, Oscar Salvador wrote:
> This patchset is the latest version of soft offline rework patchset
> targetted for v5.9.
> 
> This patchset fixes a couple of issues that the patchset Naoya
> sent [1] contained due to rebasing problems and a misunterdansting.
> 
> Main focus of this series is to stabilize soft offline.  Historically soft
> offlined pages have suffered from racy conditions because PageHWPoison is
> used to a little too aggressively, which (directly or indirectly) invades
> other mm code which cares little about hwpoison.  This results in unexpected
> behavior or kernel panic, which is very far from soft offline's "do not
> disturb userspace or other kernel component" policy.
> An example of this can be found here [2].
> 
> Along with several cleanups, this code refactors and changes the way soft
> offline work.
> Main point of this change set is to contain target page "via buddy allocator"
> or in migrating path.
> For ther former we first free the target page as we do for normal pages, and
> once it has reached buddy and it has been taken off the freelists, we flag it
> as HWpoison.
> For the latter we never get to release the page in unmap_and_move, so
> the page is under our control and we can handle it in hwpoison code.
> 
> [1] https://patchwork.kernel.org/cover/11704083/
> [2] https://lore.kernel.org/linux-mm/20190826104144.GA7849@linux/T/#u

FWIW, tested again with these patches in the ppc64 box and they work.
I see that you added my Tested-by in the last patch but in any case:

Tested-by: Aristeu Rozanski <aris@ruivo.org>

-- 
Aristeu


^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2020-09-23 13:29 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-22 13:56 [PATCH v7 00/14] HWPOISON: soft offline rework Oscar Salvador
2020-09-22 13:56 ` [PATCH v7 01/14] mm,hwpoison: cleanup unused PageHuge() check Oscar Salvador
2020-09-22 13:56 ` [PATCH v7 02/14] mm, hwpoison: remove recalculating hpage Oscar Salvador
2020-09-22 13:56 ` [PATCH v7 03/14] mm,hwpoison-inject: don't pin for hwpoison_filter Oscar Salvador
2020-09-22 13:56 ` [PATCH v7 04/14] mm,hwpoison: unexport get_hwpoison_page and make it static Oscar Salvador
2020-09-23  7:24   ` HORIGUCHI NAOYA(堀口 直也)
2020-09-22 13:56 ` [PATCH v7 05/14] mm,hwpoison: refactor madvise_inject_error Oscar Salvador
2020-09-23  7:24   ` HORIGUCHI NAOYA(堀口 直也)
2020-09-22 13:56 ` [PATCH v7 06/14] mm,hwpoison: kill put_hwpoison_page Oscar Salvador
2020-09-23  7:24   ` HORIGUCHI NAOYA(堀口 直也)
2020-09-22 13:56 ` [PATCH v7 07/14] mm,hwpoison: unify THP handling for hard and soft offline Oscar Salvador
2020-09-23  7:26   ` HORIGUCHI NAOYA(堀口 直也)
2020-09-22 13:56 ` [PATCH v7 08/14] mm,hwpoison: rework soft offline for free pages Oscar Salvador
2020-09-23  7:27   ` HORIGUCHI NAOYA(堀口 直也)
2020-09-22 13:56 ` [PATCH v7 09/14] mm,hwpoison: rework soft offline for in-use pages Oscar Salvador
2020-09-23  7:30   ` HORIGUCHI NAOYA(堀口 直也)
2020-09-22 13:56 ` [PATCH v7 10/14] mm,hwpoison: refactor soft_offline_huge_page and __soft_offline_page Oscar Salvador
2020-09-23  7:35   ` HORIGUCHI NAOYA(堀口 直也)
2020-09-22 13:56 ` [PATCH v7 11/14] mm,hwpoison: return 0 if the page is already poisoned in soft-offline Oscar Salvador
2020-09-23  7:39   ` HORIGUCHI NAOYA(堀口 直也)
2020-09-22 13:56 ` [PATCH v7 12/14] mm,hwpoison: introduce MF_MSG_UNSPLIT_THP Oscar Salvador
2020-09-22 13:56 ` [PATCH v7 13/14] mm,hwpoison: double-check page count in __get_any_page() Oscar Salvador
2020-09-22 13:56 ` [PATCH v7 14/14] mm,hwpoison: Try to narrow window race for free pages Oscar Salvador
2020-09-23  7:40   ` HORIGUCHI NAOYA(堀口 直也)
2020-09-22 17:03 ` [PATCH v7 00/14] HWPOISON: soft offline rework Andrew Morton
2020-09-22 17:56   ` osalvador
2020-09-23 13:29 ` Aristeu Rozanski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.