Linux virtualization list
 help / color / mirror / Atom feed
From: "David Hildenbrand (Arm)" <david@kernel.org>
To: Miaohe Lin <linmiaohe@huawei.com>, "Michael S. Tsirkin" <mst@redhat.com>
Cc: "Zi Yan" <ziy@nvidia.com>,
	"Andrew Morton" <akpm@linux-foundation.org>,
	linux-kernel@vger.kernel.org, "Jason Wang" <jasowang@redhat.com>,
	"Xuan Zhuo" <xuanzhuo@linux.alibaba.com>,
	"Eugenio Pérez" <eperezma@redhat.com>,
	"Muchun Song" <muchun.song@linux.dev>,
	"Oscar Salvador" <osalvador@suse.de>,
	"Lorenzo Stoakes" <ljs@kernel.org>,
	"Liam R. Howlett" <liam@infradead.org>,
	"Vlastimil Babka" <vbabka@kernel.org>,
	"Mike Rapoport" <rppt@kernel.org>,
	"Suren Baghdasaryan" <surenb@google.com>,
	"Michal Hocko" <mhocko@suse.com>,
	"Brendan Jackman" <jackmanb@google.com>,
	"Johannes Weiner" <hannes@cmpxchg.org>,
	"Baolin Wang" <baolin.wang@linux.alibaba.com>,
	"Nico Pache" <npache@redhat.com>,
	"Ryan Roberts" <ryan.roberts@arm.com>,
	"Dev Jain" <dev.jain@arm.com>, "Barry Song" <baohua@kernel.org>,
	"Lance Yang" <lance.yang@linux.dev>,
	"Hugh Dickins" <hughd@google.com>,
	"Matthew Brost" <matthew.brost@intel.com>,
	"Joshua Hahn" <joshua.hahnjy@gmail.com>,
	"Rakie Kim" <rakie.kim@sk.com>,
	"Byungchul Park" <byungchul@sk.com>,
	"Gregory Price" <gourry@gourry.net>,
	"Ying Huang" <ying.huang@linux.alibaba.com>,
	"Alistair Popple" <apopple@nvidia.com>,
	"Christoph Lameter" <cl@gentwo.org>,
	"David Rientjes" <rientjes@google.com>,
	"Roman Gushchin" <roman.gushchin@linux.dev>,
	"Harry Yoo" <harry.yoo@oracle.com>,
	"Axel Rasmussen" <axelrasmussen@google.com>,
	"Yuanchu Xie" <yuanchu@google.com>, "Wei Xu" <weixugc@google.com>,
	"Chris Li" <chrisl@kernel.org>,
	"Kairui Song" <kasong@tencent.com>,
	"Kemeng Shi" <shikemeng@huaweicloud.com>,
	"Nhat Pham" <nphamcs@gmail.com>, "Baoquan He" <bhe@redhat.com>,
	virtualization@lists.linux.dev, linux-mm@kvack.org,
	"Andrea Arcangeli" <aarcange@redhat.com>,
	"Naoya Horiguchi" <nao.horiguchi@gmail.com>
Subject: Re: [PATCH splitout] mm: memory-failure: serialize TestSetPageHWPoison with zone->lock
Date: Mon, 15 Jun 2026 12:54:38 +0200	[thread overview]
Message-ID: <be1b20ed-5b75-46b8-b7be-3c9b029f016b@kernel.org> (raw)
In-Reply-To: <984d9775-e17c-0231-b021-126b13a9aa42@huawei.com>

On 6/15/26 05:29, Miaohe Lin wrote:
> On 2026/6/11 21:20, David Hildenbrand (Arm) wrote:
>> On 6/11/26 09:36, Miaohe Lin wrote:
>>>
>>> Agree, it's not worth to do so.
>>>
>>>
>>> Since memory_failure might be the only place, this change would be unacceptable.
>>> We should come up with a better solution. Maybe we can try repeating SetPageHWPoison
>>> and ClearPageHWPoison at a first attempt though it looks somewhat weird to me and makes
>>> code more complicated.
>>
>> And I am fairly sure we could still have some remaining races ... it's shaky.
> 
> I have to agree it's shaky.

Right, just let writing task reschedule after reading the flags,
but before writing the flags.

> Any suggestion for next step?

We have various code that assumes that no concurrent writes are
possible, and consequently, we use no atomics.

__free_pages_prepare() is just one user.

Then we have __folio_set_locked(), __folio_clear_active()
and __folio_clear_unevictable().

But also __folio_mark_uptodate(), which is called rather frequently.

page_cpupid_reset_last() is also a thing, but it mostly falls
under __free_pages_prepare() handling.

... and __split_folio_to_order() also messes with flags directly without atomics.


Many of these are only possible for frozen pages (refcount == 0). I think
only  __folio_set_locked() and __folio_mark_uptodate() are called on
non-frozen pages, when there is the expectation that nobody will concurrently
use atomics that would be bad (e.g., don't trylock if not an lru page).


We don't want to use atomics at these places just to please memory failure code.

Would it be sufficient to know in memory-failure code that concurrent
handling succeeded?


Assume that we enlighten all non-atomics to grab the rcu read lock, such as

diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 7223f6f4e2b4..3c3852b60bbd 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -803,10 +803,30 @@ static inline bool PageUptodate(const struct page *page)
        return folio_test_uptodate(page_folio(page));
 }
 
+#ifdef CONFIG_MEMORY_FAILURE
+static inline void page_flags_modify_nonatomic_begin(void)
+{
+       rcu_read_lock();
+}
+static inline void page_flags_modify_nonatomic_end(void)
+{
+       rcu_read_unlock();
+}
+#else
+static inline void page_flags_modify_nonatomic_begin(void)
+{
+}
+static inline void page_flags_modify_nonatomic_end(void)
+{
+}
+#endif
+
 static __always_inline void __folio_mark_uptodate(struct folio *folio)
 {
        smp_wmb();
+       page_flags_modify_nonatomic_begin();
        __set_bit(PG_uptodate, folio_flags(folio, 0));
+       page_flags_modify_nonatomic_end();
 }
 

And then we have some retry logic such as:

diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 51508a55c405..1123c40aaf43 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -162,6 +162,62 @@ static struct rb_root_cached pfn_space_itree = RB_ROOT_CACHED;
 
 static DEFINE_MUTEX(pfn_space_lock);
 
+static bool page_test_set_hwpoison(struct page *page)
+{
+	lockdep_assert_held(&mf_mutex);
+
+	while (true) {
+		/* Already set -> not our problem. */
+		if (TestSetPageHWPoison(page))
+			return true;
+		/* Make sure concurrent non-atomic writers completed. */
+		synchronize_rcu();
+		/* Setting the flag was sticky. */
+		if (PageHWPoison(page))
+			return false;
+	}
+}
+
+static bool page_test_clear_hwpoison(struct page *page)
+{
+	lockdep_assert_held(&mf_mutex);
+
+	while (true) {
+		/* Already clear -> not our problem. */
+		if (!TestClearPageHWPoison(page))
+			return false;
+		/* Make sure concurrent non-atomic writers completed. */
+		synchronize_rcu();
+		/* Clearing the flag was sticky. */
+		if (!PageHWPoison(page))
+			return true;
+	}
+}
+
+static void page_set_hwpoison(struct page *page)
+{
+	lockdep_assert_held(&mf_mutex);
+
+	while (!PageHWPoison(page)) {
+		SetPageHWPoison(page);
+
+		/* Make sure concurrent non-atomic writers completed. */
+		synchronize_rcu();
+	}
+}
+
+static void page_clear_hwpoison(struct page *page)
+{
+	lockdep_assert_held(&mf_mutex);
+
+	while (PageHWPoison(page)) {
+		ClearPageHWPoison(page);
+
+		/* Make sure concurrent non-atomic writers completed. */
+		synchronize_rcu();
+	}
+}
+
 /*
  * Return values:
  *   1:   the page is dissolved (if needed) and taken off from buddy,
@@ -199,7 +255,7 @@ static bool page_handle_poison(struct page *page, bool hugepage_or_freepage, boo
 			return false;
 	}
 
-	SetPageHWPoison(page);
+	page_set_hwpoison(page);
 	if (release)
 		put_page(page);
 	page_ref_inc(page);
@@ -1744,7 +1800,7 @@ static int mf_generic_kill_procs(unsigned long long pfn, int flags,
 	 * Use this flag as an indication that the dax page has been
 	 * remapped UC to prevent speculative consumption of poison.
 	 */
-	SetPageHWPoison(&folio->page);
+	page_set_hwpoison(&folio->page);
 
 	/*
 	 * Unlike System-RAM there is no possibility to swap in a
@@ -1789,7 +1845,7 @@ int mf_dax_kill_procs(struct address_space *mapping, pgoff_t index,
 			goto unlock;
 
 		if (!pre_remove)
-			SetPageHWPoison(page);
+			page_set_hwpoison(page);
 
 		/*
 		 * The pre_remove case is revoking access, the memory is still
@@ -1866,7 +1922,7 @@ static unsigned long __folio_free_raw_hwp(struct folio *folio, bool move_flag)
 	head = llist_del_all(raw_hwp_list_head(folio));
 	llist_for_each_entry_safe(p, next, head, node) {
 		if (move_flag)
-			SetPageHWPoison(p->page);
+			page_set_hwpoison(p->page);
 		else
 			num_poisoned_pages_sub(page_to_pfn(p->page), 1);
 		kfree(p);
@@ -2380,7 +2436,7 @@ int memory_failure(unsigned long pfn, int flags)
 	if (res != -ENOENT)
 		goto unlock_mutex;
 
-	if (TestSetPageHWPoison(p)) {
+	if (page_test_set_hwpoison(p)) {
 		res = -EHWPOISON;
 		if (flags & MF_ACTION_REQUIRED)
 			res = kill_accessing_process(current, pfn, flags);
@@ -2410,7 +2466,7 @@ int memory_failure(unsigned long pfn, int flags)
 			} else {
 				/* We lost the race, try again */
 				if (retry) {
-					ClearPageHWPoison(p);
+					page_clear_hwpoison(p);
 					retry = false;
 					goto try_again;
 				}
@@ -2431,7 +2487,7 @@ int memory_failure(unsigned long pfn, int flags)
 	/* filter pages that are protected from hwpoison test by users */
 	folio_lock(folio);
 	if (hwpoison_filter(p)) {
-		ClearPageHWPoison(p);
+		page_clear_hwpoison(p);
 		folio_unlock(folio);
 		folio_put(folio);
 		res = -EOPNOTSUPP;
@@ -2751,7 +2807,7 @@ int unpoison_memory(unsigned long pfn)
 		}
 
 		folio_put(folio);
-		if (TestClearPageHWPoison(p)) {
+		if (page_test_clear_hwpoison(p)) {
 			folio_put(folio);
 			ret = 0;
 		}


Maybe that would work. There would still be issues to solve

(a) We don't hold the mf_mutex on all call paths, but we really need it so a
page_test_set_hwpoison() cannot race in weird ways with the other primitives I think.

(b) There are some leftover SetPageHWPoison etc. instances. The ones in
arch/x86/kernel/cpu/mce/core.c likely cannot grab the mutex, but maybe they are
corner cases either way and we can document the situation.


Further, while I assume the synchronize_rcu() on the MCE path should be fine
(who cares about performance there?), I don't know if the added RCU read lock
on some paths could be noticable.

So one idea worth discussing, but I am sure there are more problems.

-- 
Cheers,

David

  reply	other threads:[~2026-06-15 10:54 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-09 10:12 [PATCH splitout] mm: memory-failure: serialize TestSetPageHWPoison with zone->lock Michael S. Tsirkin
2026-06-09 12:50 ` David Hildenbrand (Arm)
2026-06-09 16:12 ` Zi Yan
2026-06-09 18:10 ` Andrew Morton
2026-06-09 18:38   ` David Hildenbrand (Arm)
2026-06-09 18:39     ` Zi Yan
2026-06-09 18:52       ` Zi Yan
2026-06-09 20:34         ` Michael S. Tsirkin
2026-06-09 20:54           ` Zi Yan
2026-06-09 21:00             ` Michael S. Tsirkin
2026-06-10  7:24               ` Miaohe Lin
2026-06-10  7:35                 ` Michael S. Tsirkin
2026-06-10 21:18                 ` Michael S. Tsirkin
2026-06-11  3:35                   ` Miaohe Lin
2026-06-11  5:43                     ` Michael S. Tsirkin
2026-06-11  7:36                       ` Miaohe Lin
2026-06-11 13:20                         ` David Hildenbrand (Arm)
2026-06-15  3:29                           ` Miaohe Lin
2026-06-15 10:54                             ` David Hildenbrand (Arm) [this message]
2026-06-16  6:32                               ` Miaohe Lin
2026-06-16  6:56                                 ` David Hildenbrand (Arm)
2026-06-16 11:40                                   ` Miaohe Lin
2026-06-16 12:18                                     ` David Hildenbrand (Arm)
2026-06-11  6:33     ` Michael S. Tsirkin
2026-06-11 11:33       ` David Hildenbrand (Arm)
2026-06-09 20:24   ` Michael S. Tsirkin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=be1b20ed-5b75-46b8-b7be-3c9b029f016b@kernel.org \
    --to=david@kernel.org \
    --cc=aarcange@redhat.com \
    --cc=akpm@linux-foundation.org \
    --cc=apopple@nvidia.com \
    --cc=axelrasmussen@google.com \
    --cc=baohua@kernel.org \
    --cc=baolin.wang@linux.alibaba.com \
    --cc=bhe@redhat.com \
    --cc=byungchul@sk.com \
    --cc=chrisl@kernel.org \
    --cc=cl@gentwo.org \
    --cc=dev.jain@arm.com \
    --cc=eperezma@redhat.com \
    --cc=gourry@gourry.net \
    --cc=hannes@cmpxchg.org \
    --cc=harry.yoo@oracle.com \
    --cc=hughd@google.com \
    --cc=jackmanb@google.com \
    --cc=jasowang@redhat.com \
    --cc=joshua.hahnjy@gmail.com \
    --cc=kasong@tencent.com \
    --cc=lance.yang@linux.dev \
    --cc=liam@infradead.org \
    --cc=linmiaohe@huawei.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=ljs@kernel.org \
    --cc=matthew.brost@intel.com \
    --cc=mhocko@suse.com \
    --cc=mst@redhat.com \
    --cc=muchun.song@linux.dev \
    --cc=nao.horiguchi@gmail.com \
    --cc=npache@redhat.com \
    --cc=nphamcs@gmail.com \
    --cc=osalvador@suse.de \
    --cc=rakie.kim@sk.com \
    --cc=rientjes@google.com \
    --cc=roman.gushchin@linux.dev \
    --cc=rppt@kernel.org \
    --cc=ryan.roberts@arm.com \
    --cc=shikemeng@huaweicloud.com \
    --cc=surenb@google.com \
    --cc=vbabka@kernel.org \
    --cc=virtualization@lists.linux.dev \
    --cc=weixugc@google.com \
    --cc=xuanzhuo@linux.alibaba.com \
    --cc=ying.huang@linux.alibaba.com \
    --cc=yuanchu@google.com \
    --cc=ziy@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox