Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Kiryl Shutsemau (Meta)" <kas@kernel.org>,
	"Andrew Morton" <akpm@linux-foundation.org>,
	"Linus Torvalds" <torvalds@linux-foundation.org>,
	"David Hildenbrand" <david@kernel.org>,
	"Matthew Wilcox" <willy@infradead.org>,
	"Vlastimil Babka" <vbabka@kernel.org>,
	"Lorenzo Stoakes" <ljs@kernel.org>, "Jan Kara" <jack@suse.cz>,
	"Hugh Dickins" <hughd@google.com>,
	"Christian Brauner" <brauner@kernel.org>,
	"Alexander Viro" <viro@zeniv.linux.org.uk>
Cc: "Miklos Szeredi" <miklos@szeredi.hu>,
	"Miaohe Lin" <linmiaohe@huawei.com>,
	"Naoya Horiguchi" <nao.horiguchi@gmail.com>,
	"Oscar Salvador" <osalvador@suse.de>,
	"Liam R . Howlett" <liam@infradead.org>,
	"Mike Rapoport" <rppt@kernel.org>,
	"Suren Baghdasaryan" <surenb@google.com>,
	"Michal Hocko" <mhocko@suse.com>,
	"Brendan Jackman" <brendan.jackman@linux.dev>,
	"Johannes Weiner" <hannes@cmpxchg.org>, "Zi Yan" <ziy@nvidia.com>,
	"Baolin Wang" <baolin.wang@linux.alibaba.com>,
	"Nico Pache" <nico.pache@linux.dev>,
	"Ryan Roberts" <ryan.roberts@arm.com>,
	"Dev Jain" <dev.jain@arm.com>, "Barry Song" <baohua@kernel.org>,
	"Lance Yang" <lance.yang@linux.dev>,
	"Usama Arif" <usama.arif@linux.dev>,
	"Kairui Song" <kasong@tencent.com>,
	"Qi Zheng" <qi.zheng@linux.dev>,
	"Shakeel Butt" <shakeel.butt@linux.dev>,
	"Axel Rasmussen" <axelrasmussen@google.com>,
	"Yuanchu Xie" <yuanchu@google.com>, "Wei Xu" <weixugc@google.com>,
	"Baoquan He" <baoquan.he@linux.dev>,
	"Paul E . McKenney" <paulmck@kernel.org>,
	"Pedro Falcato" <pfalcato@suse.de>,
	"Harry Yoo" <harry@kernel.org>,
	"Luis Chamberlain" <mcgrof@kernel.org>,
	"Ilya Gladyshev" <ilya.gladyshev@linux.dev>,
	"Jaegeuk Kim" <jaegeuk@kernel.org>,
	linux-mm@kvack.org, kernel-team@meta.com
Subject: Re: [RFC PATCH 08/18] mm/memory-failure: handle folios with PG_rcu_free
Date: Tue, 20 Oct 2026 15:52:37 +0000	[thread overview]
Message-ID: <sashiko-outbox-179306@kernel.org> (raw)
In-Reply-To: <20261019154913.1765974-9-kas@kernel.org>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] mm: PG_rcu_free folios freed in get_any_page() are not drained on retry
- [High] mm: mf_free_rcu_pages() skips rcu_free_sync() during rcu_free_pages()
- [High] mm/memory-failure: missing synchronize_rcu() when kill_accessing_process() returns 0
--

commit 0752670541e1c89c99d63ab6b10ada844735c05b
Author: Kiryl Shutsemau (Meta) <kas@kernel.org>

mm/memory-failure: handle folios with PG_rcu_free

This commit updates memory_failure() and soft_offline_page() to handle
folios with PG_rcu_free by draining pages waiting for an RCU grace period
before attempting to grab the page, and waiting for a grace period before
returning success from memory_failure().

> diff --git a/mm/memory-failure.c b/mm/memory-failure.c
> index 7dde7c3c9a0c5..dc66cac05b1f5 100644
> --- a/mm/memory-failure.c
> +++ b/mm/memory-failure.c
> @@ -1565,6 +1565,21 @@ static int get_hwpoison_page(struct page *p, unsigned long flags)
>  	return ret;
>  }
>  
> +/* A page waiting for a grace period is not in the buddy yet: free it */
> +static void mf_free_rcu_pages(struct page *p)
> +{
> +	if (!IS_ENABLED(CONFIG_PAGE_RCU_FREE))
> +		return;
> +	if (page_count(p))
> +		return;
> +	if (!zone_page_state_snapshot(page_zone(p), NR_FREE_RCU_PAGES))
> +		return;

[Severity: High]
Can checking NR_FREE_RCU_PAGES here cause mf_free_rcu_pages() to skip
rcu_free_sync() while a target folio is still in the middle of being
freed?

In mm/page_alloc.c:rcu_free_pages(), NR_FREE_RCU_PAGES is decremented
before free_unref_folios() runs __free_pages_prepare() and returns the
folios to the buddy allocator:

mm/page_alloc.c:rcu_free_pages() {
	list_for_each_entry_safe(folio, next, list, lru) {
		nr += folio_nr_pages(folio);
		mod_zone_page_state(folio_zone(folio), NR_FREE_RCU_PAGES,
				    -folio_nr_pages(folio));
		if (!folio_batch_add(&fbatch, folio)) {
			free_unref_folios(&fbatch);
			cond_resched_tasks_rcu_qs();
		}
	}
	if (folio_batch_count(&fbatch))
		free_unref_folios(&fbatch);
}

Similarly, in folios_put_refs(), folio->_refcount is decremented to 0
before __page_cache_release() and mem_cgroup_uncharge_folios() run, prior
to rcu_free_defer() incrementing NR_FREE_RCU_PAGES.

If the last RCU-freed folios in the zone are in either window when
mf_free_rcu_pages() runs on another CPU, page_count(p) is 0,
is_free_buddy_page(p) is false, and NR_FREE_RCU_PAGES is 0. That causes
mf_free_rcu_pages() to return early without calling rcu_free_sync() (which
otherwise waits on rcu_free_drain() and flushes r->rwork.work and
r->expedite).

In memory_failure(), TestSetPageHWPoison(p) then sets PG_hwpoison before
free_unref_folios() runs __free_pages_prepare(). For an order-0 folio,
__free_pages_prepare() sees PageHWPoison(page) && !order and drops the
page instead of placing it in the buddy allocator, causing
get_hwpoison_page() to see !is_free_buddy_page(p) and ignore the error
(MF_IGNORED). For a large folio entering rcu_free_defer() after
page_count(p) became 0, it is queued on rcu_free_pcpu with PG_hwpoison set
and later freed back to the buddy allocator.

> +	if (is_free_buddy_page(p))
> +		return;
> +
> +	rcu_free_sync();
> +}
> +
>  /*
>   * The caller must guarantee the folio isn't large folio, except hugetlb.
>   * try_to_unmap() can't handle it.
> @@ -2440,6 +2455,7 @@ int memory_failure(unsigned long pfn, int flags)
>  	if (res != -ENOENT)
>  		goto unlock_mutex;
>  
> +	mf_free_rcu_pages(p);
>  	if (TestSetPageHWPoison(p)) {

[Severity: High]
What happens if a folio with PG_rcu_free still has a reference when
mf_free_rcu_pages() runs (so page_count(p) > 0 returns early), and its
last reference is dropped inside get_hwpoison_page() -> get_any_page()?

Inside get_any_page(), the last reference on a PG_rcu_free folio can drop
to 0 in a few ways:
1. When MF_COUNT_INCREASED is set (for example via madvise_inject_error()),
   page_count(p) >= 1 during mf_free_rcu_pages(). If the page is not
   immediately HWPoisonHandlable() (such as !PageLRU(p) because it was
   truncated while in a per-CPU lru_add batch), get_any_page() calls
   put_page(p) and shake_page(p) and jumps to try_again.
2. Without MF_COUNT_INCREASED, if a truncated PG_rcu_free folio is still in
   a per-CPU folio_batch, __get_hwpoison_page() returns -EBUSY and
   get_any_page() calls shake_page(p) -> lru_add_drain_all(), which frees
   the folio.
3. A concurrent task drops the last reference on the folio after
   mf_free_rcu_pages() checked page_count(p), causing __get_hwpoison_page()
   to return 0 and get_any_page() to retry:

mm/memory-failure.c:get_any_page() {
			} else if (!PageHuge(p) && !is_free_buddy_page(p)) {
				/* We raced with put_page, retry. */
				if (pass++ < GET_PAGE_MAX_RETRY_NUM)
					goto try_again;
				ret = -EIO;
			}
}

When the folio is freed during any of those paths, __free_pages_prepare()
checks:

mm/page_alloc.c:__free_pages_prepare() {
	if (unlikely(folio_test_rcu_free(folio)) &&
	    !(PageHWPoison(page) && !order)) {
		VM_WARN_ON_ONCE(fpi_flags & FPI_NOLOCK);
		__folio_clear_rcu_free(folio);
		count_vm_events(PGFREE_RCU, 1 << order);\n		rcu_free_defer(folio);
		return false;
	}
}

Because neither get_any_page() nor shake_page() drains RCU-freed pages on
retry, a large folio in memory_failure() (or any PG_rcu_free folio in
soft_offline_page()) is deferred via rcu_free_defer(), causing all retries
in get_any_page() to see page_count(p) == 0 and !is_free_buddy_page(p) and
fail with -EIO. In memory_failure(), PG_hwpoison is already set on p, so
when the RCU grace period later expires, the large folio is freed into the
buddy allocator with the poisoned subpage still inside it. And for an
order-0 folio in memory_failure() freed after TestSetPageHWPoison(p),
__free_pages_prepare() skips rcu_free_defer() and drops the page without
putting it in the buddy allocator, so get_any_page() also fails with -EIO
(MF_IGNORED).

>  		res = -EHWPOISON;
>  		if (flags & MF_ACTION_REQUIRED)
> @@ -2612,6 +2628,13 @@ int memory_failure(unsigned long pfn, int flags)
>  
>  identify_page_state:
>  	res = identify_page_state(pfn, p, page_flags);
> +	/*
> +	 * The caller may unmap the page once this returns 0.  Lockless
> +	 * readers that started before the page got PG_hwpoison, or before a
> +	 * split left it in a folio of its own, may still be copying it.
> +	 */
> +	if (!res && folio_test_rcu_free(folio))
> +		synchronize_rcu();
>  	mutex_unlock(&mf_mutex);
>  	return res;

[Severity: High]
Can memory_failure() return 0 without calling synchronize_rcu() when a
second memory_failure() call hits the TestSetPageHWPoison(p) path?

Consider a lockless RCU reader in filemap_read_rcu_batch() that checks
folio_contain_hwpoisoned_page(folio) before PG_hwpoison is set and begins
copying from the page via the direct map in filemap_read_rcu_copy().

1. A first memory_failure() call (for instance from a background scrubber
   or UCNA) sets PG_hwpoison via TestSetPageHWPoison(p). In
   hwpoison_user_mappings() -> unmap_poisoned_folio(), because the
   page-cache folio is clean, TTU_HWPOISON is cleared and user PTEs are
   unmapped to pte_none without installing hwpoison swap entries. If
   me_pagecache_clean() then returns MF_FAILED (e.g. due to a transient
   extra refcount in has_extra_refcount() or truncate_error_folio()
   failing), identify_page_state() returns -EBUSY, so !res is false and
   memory_failure() unlocks mf_mutex without calling synchronize_rcu().
2. A racing Action Required MCE on the same PFN calls memory_failure(pfn,
   MF_ACTION_REQUIRED). TestSetPageHWPoison(p) returns true and calls:

mm/memory-failure.c:memory_failure() {
	if (TestSetPageHWPoison(p)) {
		res = -EHWPOISON;
		if (flags & MF_ACTION_REQUIRED)
			res = kill_accessing_process(current, pfn, flags);
		if (flags & MF_COUNT_INCREASED)
			put_page(p);
		action_result(pfn, MF_MSG_ALREADY_POISONED, MF_FAILED);
		goto unlock_mutex;
	}
}

Because the clean user PTEs were already unmapped without hwpoison swap
entries, walk_page_range() in kill_accessing_process() finds no matching
PTE in current->mm and returns 0. memory_failure() then jumps to
unlock_mutex and returns 0 without waiting for an RCU grace period,
allowing the caller (such as kill_me_maybe() -> set_mce_nospec(pfn)) to
unmap the page from the kernel direct map while the lockless RCU reader is
still copying from it.

[ ... ]

>
> -- 
> 2.51.2
>

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261019154913.1765974-1-kas@kernel.org?part=8


  reply	other threads:[~2026-10-20 15:53 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-19 15:48 [RFC PATCH 00/18] mm: read the page cache without folio references Kiryl Shutsemau (Meta)
2026-10-19 15:48 ` [RFC PATCH 01/18] mm: free PG_rcu_free folios only after an RCU grace period Kiryl Shutsemau (Meta)
2026-10-19 15:48 ` [RFC PATCH 02/18] splice: don't steal page cache folios Kiryl Shutsemau (Meta)
2026-10-19 15:48 ` [RFC PATCH 03/18] fuse: don't move a folio with PG_rcu_free into a fuse file Kiryl Shutsemau (Meta)
2026-10-19 15:48 ` [RFC PATCH 04/18] mm: count the pages waiting for a grace period Kiryl Shutsemau (Meta)
2026-10-19 15:48 ` [RFC PATCH 05/18] mm: expedite the grace period when a CPU has many deferred frees Kiryl Shutsemau (Meta)
2026-10-19 15:48 ` [RFC PATCH 06/18] mm: free the pages waiting for a grace period before OOM Kiryl Shutsemau (Meta)
2026-10-20 15:51   ` sashiko-bot
2026-10-19 15:49 ` [RFC PATCH 07/18] mm: free the pages waiting for a grace period before isolation fails Kiryl Shutsemau (Meta)
2026-10-20 15:52   ` sashiko-bot
2026-10-19 15:49 ` [RFC PATCH 08/18] mm/memory-failure: handle folios with PG_rcu_free Kiryl Shutsemau (Meta)
2026-10-20 15:52   ` sashiko-bot [this message]
2026-10-19 15:49 ` [RFC PATCH 09/18] mm: have folio_mark_accessed() return whether the folio is hot Kiryl Shutsemau (Meta)
2026-10-19 15:49 ` [RFC PATCH 10/18] mm/filemap: mark a folio accessed in one place in filemap_read() Kiryl Shutsemau (Meta)
2026-10-19 15:49 ` [RFC PATCH 11/18] mm/filemap: set PG_rcu_free on the folios that reads keep coming back to Kiryl Shutsemau (Meta)
2026-10-19 15:49 ` [RFC PATCH 12/18] mm/filemap: don't defer the free of folios of an inode being evicted Kiryl Shutsemau (Meta)
2026-10-20 15:53   ` sashiko-bot
2026-10-19 15:49 ` [RFC PATCH 13/18] mm/filemap: store the end of a read in f_ra only when it changes Kiryl Shutsemau (Meta)
2026-10-19 15:49 ` [RFC PATCH 14/18] mm/filemap: read from the page cache without folio references Kiryl Shutsemau (Meta)
2026-10-20 15:53   ` sashiko-bot
2026-10-19 15:49 ` [RFC PATCH 15/18] mm/filemap: read large folios " Kiryl Shutsemau (Meta)
2026-10-20 15:54   ` sashiko-bot
2026-10-19 15:49 ` [RFC PATCH 16/18] mm/shmem: count accesses to a folio the way filemap_read() does Kiryl Shutsemau (Meta)
2026-10-19 15:49 ` [RFC PATCH 17/18] mm/shmem: read without folio references Kiryl Shutsemau (Meta)
2026-10-19 15:49 ` [RFC PATCH 18/18] mm/filemap: add a debugfs switch for lockless page cache reads Kiryl Shutsemau (Meta)
2026-10-19 21:20 ` [RFC PATCH 00/18] mm: read the page cache without folio references Harry Yoo

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=sashiko-outbox-179306@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=akpm@linux-foundation.org \
    --cc=axelrasmussen@google.com \
    --cc=baohua@kernel.org \
    --cc=baolin.wang@linux.alibaba.com \
    --cc=baoquan.he@linux.dev \
    --cc=brauner@kernel.org \
    --cc=brendan.jackman@linux.dev \
    --cc=david@kernel.org \
    --cc=dev.jain@arm.com \
    --cc=hannes@cmpxchg.org \
    --cc=harry@kernel.org \
    --cc=hughd@google.com \
    --cc=ilya.gladyshev@linux.dev \
    --cc=jack@suse.cz \
    --cc=jaegeuk@kernel.org \
    --cc=kas@kernel.org \
    --cc=kasong@tencent.com \
    --cc=kernel-team@meta.com \
    --cc=lance.yang@linux.dev \
    --cc=liam@infradead.org \
    --cc=linmiaohe@huawei.com \
    --cc=linux-mm@kvack.org \
    --cc=ljs@kernel.org \
    --cc=mcgrof@kernel.org \
    --cc=mhocko@suse.com \
    --cc=miklos@szeredi.hu \
    --cc=nao.horiguchi@gmail.com \
    --cc=nico.pache@linux.dev \
    --cc=osalvador@suse.de \
    --cc=paulmck@kernel.org \
    --cc=pfalcato@suse.de \
    --cc=qi.zheng@linux.dev \
    --cc=rppt@kernel.org \
    --cc=ryan.roberts@arm.com \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=shakeel.butt@linux.dev \
    --cc=surenb@google.com \
    --cc=torvalds@linux-foundation.org \
    --cc=usama.arif@linux.dev \
    --cc=vbabka@kernel.org \
    --cc=viro@zeniv.linux.org.uk \
    --cc=weixugc@google.com \
    --cc=willy@infradead.org \
    --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