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>,
"Harry Yoo" <harry@kernel.org>
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>,
"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 v2 07/18] mm: free the pages waiting for a grace period before isolation fails
Date: Thu, 05 Nov 2026 13:03:37 +0000 [thread overview]
Message-ID: <sashiko-outbox-199100@kernel.org> (raw)
In-Reply-To: <20261104130118.2507513-8-kas@kernel.org>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] mm: premature NR_FREE_RCU_PAGES decrement in rcu_free_pages() skips sync
- [Medium] mm: stale outer_start in alloc_contig_frozen_range_noprof() after RCU free merge
- [Medium] mm: page_is_unmovable() treats freed compound PG_rcu_free folios as unmovable
--
commit 01209427ff9390747e1e7bf6dbe80b5fa2e38b2b
Author: Kiryl Shutsemau (Meta) <kas@kernel.org>
mm: free the pages waiting for a grace period before isolation fails
This commit adds rcu_free_sync() and zone_rcu_free_waiting() so that
alloc_contig_frozen_range_noprof() and offline_pages() can drain and flush
folios waiting for an RCU grace period when test_pages_isolated() fails.
> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
> index 226ab9cb078ad..c23fab6529571 100644
> --- a/mm/memory_hotplug.c
> +++ b/mm/memory_hotplug.c
> @@ -1967,6 +1967,7 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages,
> .nid = NUMA_NO_NODE,
> };
> unsigned long flags;
> + bool migrated;
> char *reason;
> int ret;
> unsigned long normal_pages = 0;
> @@ -2050,6 +2051,7 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages,
>
> do {
> pfn = start_pfn;
> + migrated = false;
> do {
> /*
> * Historically we always checked for any signal and
> @@ -2071,6 +2073,7 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages,
> * out
> */
> do_migrate_range(pfn, end_pfn);
> + migrated = true;
> }
> } while (!ret);
>
> @@ -2092,6 +2095,9 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages,
>
> ret = test_pages_isolated(start_pfn, end_pfn,
> PB_ISOLATE_MODE_MEM_OFFLINE);
> + /* What this pass migrated can wait for a grace period */
> + if (ret && migrated && zone_rcu_free_waiting(zone))
> + rcu_free_sync();
>
> } while (ret);
>
[Severity: Medium]
What happens if a large compound folio with PG_rcu_free was already freed
before offline_pages() or alloc_contig_frozen_range_noprof() calls
start_isolate_page_range()?
When __free_pages_prepare() intercepts a folio with PG_rcu_free and defers
it via rcu_free_defer(), the folio has a reference count of zero and
PageLRU cleared, while PageCompound remains set until rcu_free_pages()
later completes the free:
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);
rcu_free_defer(folio);
return false;
}
...
}
Before reaching test_pages_isolated() and rcu_free_sync(), both
offline_pages() and alloc_contig_frozen_range_noprof() (as well as
alloc_contig_frozen_pages() via pfn_range_valid_contig()) check for
unmovable pages through:
start_isolate_page_range()
-> set_migratetype_isolate()
-> has_unmovable_pages()
-> page_is_unmovable()
In page_is_unmovable() (outside ZONE_MOVABLE), order-0 pages with a zero
reference count fall through to the !page_ref_count(page) check and are
treated as movable, but compound pages are checked first and rejected when
!PageLRU(page) without checking !folio_ref_count(folio):
mm/page_isolation.c:page_is_unmovable() {
...
if (PageCompound(page)) {
struct folio *folio = page_folio(page);
...
if (folio_test_hugetlb(folio)) {
...
} else if (!PageLRU(page)) {
return true;
}
...
}
Won't start_isolate_page_range() or pfn_range_valid_contig() treat a freed
compound folio waiting for an RCU grace period as unmovable and fail with
-EBUSY before rcu_free_sync() is ever reached?
> diff --git a/mm/page_alloc.c b/mm/page_alloc.c
> index 9caaa57bf51d7..eea287ab2a011 100644
> --- a/mm/page_alloc.c
> +++ b/mm/page_alloc.c
> @@ -1479,6 +1479,19 @@ static void rcu_free_drain(void)
> mutex_unlock(&lock);
> }
>
> +/* Free every waiting folio, including those a work took off the lists */
> +void rcu_free_sync(void)
> +{
> + int cpu;
> +
> + if (!IS_ENABLED(CONFIG_PAGE_RCU_FREE))
> + return;
> +
> + rcu_free_drain();
> + for_each_possible_cpu(cpu)
> + flush_work(&per_cpu_ptr(&rcu_free_pcpu, cpu)->rwork.work);
> +}
> +
> static void __init rcu_free_init(void)
> {
> int cpu;
> @@ -7649,7 +7662,16 @@ int alloc_contig_frozen_range_noprof(unsigned long start, unsigned long end,
> outer_start = find_large_buddy(start);
>
> /* Make sure the range is really isolated. */
> - if (test_pages_isolated(outer_start, end, mode)) {
> + ret = test_pages_isolated(outer_start, end, mode);
> + if (ret && zone_rcu_free_waiting(cc.zone)) {
> + rcu_free_sync();
> +
> + /* A freed folio can merge with a free buddy below start */
> + outer_start = find_large_buddy(start);
> + ret = test_pages_isolated(outer_start, end, mode);
> + }
> +
> + if (ret) {
> ret = -EBUSY;
> goto done;
> }
[Severity: Medium]
Can outer_start in alloc_contig_frozen_range_noprof() still become stale if
a background RCU free completes and merges across start or outer_start?
First, suppose a migrated folio at start had PG_rcu_free and a free buddy
exists immediately below start in the same isolated pageblock. When the
initial find_large_buddy(start) runs, the folio at start is not yet freed,
so find_large_buddy(start) returns start:
mm/page_alloc.c:find_large_buddy() {
...
if (pfn + (1 << buddy_order(page)) > start_pfn)
return pfn;
/* Nothing found */
return start_pfn;
}
If rcu_free_workfn() or rcu_free_expedite_workfn() on another CPU finishes
freeing that folio before zone_rcu_free_waiting(cc.zone) is checked,
__free_one_page() merges the folio with the preceding buddy below start,
clearing PageBuddy on start. Then test_pages_isolated(outer_start, end,
mode) fails because outer_start (still equal to start) is now a tail of the
merged buddy, and zone_rcu_free_waiting(cc.zone) returns false because
NR_FREE_RCU_PAGES was already decremented to 0, so outer_start is never
recomputed and alloc_contig_frozen_range_noprof() returns -EBUSY.
Second, suppose all pages in [start, end) are already free in the buddy
allocator when the initial find_large_buddy(start) and
test_pages_isolated(outer_start, end, mode) run, so test_pages_isolated()
returns 0 and rcu_free_sync() is not called. If a folio below outer_start
within the same isolated pageblock was waiting for an RCU grace period and
is freed by rcu_free_workfn() before isolate_freepages_range(&cc,
outer_start, end) runs:
mm/page_alloc.c:alloc_contig_frozen_range_noprof() {
...
/* Grab isolated pages from freelists. */
outer_end = isolate_freepages_range(&cc, outer_start, end);
if (!outer_end) {
ret = -EBUSY;
goto done;
}
...
}
__free_one_page() can merge that newly freed folio with the buddy at
outer_start, moving the buddy head to a lower PFN and causing
isolate_freepages_range() to fail with -EBUSY.
> diff --git a/mm/page_alloc.h b/mm/page_alloc.h
> index b9259deddb59d..764e3f43b3b98 100644
> --- a/mm/page_alloc.h
> +++ b/mm/page_alloc.h
> @@ -235,6 +235,14 @@ struct page *__alloc_frozen_pages_noprof(gfp_t gfp, unsigned int order, int nid,
> alloc_hooks(__alloc_frozen_pages_noprof(__VA_ARGS__))
> void free_frozen_pages(struct page *page, unsigned int order);
> void free_unref_folios(struct folio_batch *fbatch);
> +void rcu_free_sync(void);
> +
> +/* Whether @zone has pages waiting for a grace period */
> +static inline bool zone_rcu_free_waiting(struct zone *zone)
> +{
> + return IS_ENABLED(CONFIG_PAGE_RCU_FREE) &&
> + zone_page_state_snapshot(zone, NR_FREE_RCU_PAGES);
> +}
[Severity: Medium]
As reported by Sashiko on RFC [1], doesn't gating rcu_free_sync() on
zone_rcu_free_waiting() miss folios that are actively being freed inside
rcu_free_pages() after NR_FREE_RCU_PAGES has already been decremented?
In rcu_free_pages(), NR_FREE_RCU_PAGES is decremented for each folio before
__free_pages_ok() or free_unref_folios(&fbatch) prepares the pages and
returns them to the buddy freelists under zone->lock:
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 (direct) {
__free_pages_ok(&folio->page, folio_order(folio),
FPI_NONE);
cond_resched();
} else if (!folio_batch_add(&fbatch, folio)) {
free_unref_folios(&fbatch);
cond_resched_tasks_rcu_qs();
}
}
if (folio_batch_count(&fbatch))
free_unref_folios(&fbatch);
...
}
When rcu_free_workfn(), rcu_free_expedite_workfn(), or a concurrent
rcu_free_drain() decrements NR_FREE_RCU_PAGES to 0 for the last waiting
folios in the zone, there is a window while __free_pages_prepare() (page
zeroing, KASAN poisoning, memcg uncharging, etc.) and free_one_page() are
still running where the pages are not yet PageBuddy in the buddy allocator,
but zone_rcu_free_waiting(zone) already reads 0 and returns false.
Although rcu_free_sync() flushes rwork.work and expedite works to wait for
folios taken off the per-CPU lists, both alloc_contig_frozen_range_noprof()
and offline_pages() skip calling rcu_free_sync() when
zone_rcu_free_waiting() returns false, causing
alloc_contig_frozen_range_noprof() to return -EBUSY and offline_pages() to
skip synchronizing on the pass that migrated folios.
[1] https://lore.kernel.org/r/20261019154913.1765974-8-kas@kernel.org
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261104130118.2507513-1-kas@kernel.org?part=7
next prev parent reply other threads:[~2026-11-05 13:03 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-04 13:00 [RFC PATCH v2 00/18] mm: read the page cache without folio references Kiryl Shutsemau (Meta)
2026-11-04 13:00 ` [RFC PATCH v2 01/18] mm: free PG_rcu_free folios only after an RCU grace period Kiryl Shutsemau (Meta)
2026-11-05 20:37 ` David Hildenbrand (Arm)
2026-11-06 10:23 ` Kiryl Shutsemau
2026-11-04 13:00 ` [RFC PATCH v2 02/18] splice: don't steal page cache folios Kiryl Shutsemau (Meta)
2026-11-04 16:05 ` David Hildenbrand (Arm)
2026-11-05 14:55 ` Kiryl Shutsemau
2026-11-04 13:00 ` [RFC PATCH v2 03/18] fuse: don't move a folio with PG_rcu_free into a fuse file Kiryl Shutsemau (Meta)
2026-11-04 13:01 ` [RFC PATCH v2 04/18] mm: count the pages waiting for a grace period Kiryl Shutsemau (Meta)
2026-11-04 13:01 ` [RFC PATCH v2 05/18] mm: expedite the grace period when a CPU has many deferred frees Kiryl Shutsemau (Meta)
2026-11-04 13:01 ` [RFC PATCH v2 06/18] mm: free the pages waiting for a grace period before OOM Kiryl Shutsemau (Meta)
2026-11-05 13:03 ` sashiko-bot
2026-11-04 13:01 ` [RFC PATCH v2 07/18] mm: free the pages waiting for a grace period before isolation fails Kiryl Shutsemau (Meta)
2026-11-05 13:03 ` sashiko-bot [this message]
2026-11-04 13:01 ` [RFC PATCH v2 08/18] mm/compaction: free the migrated folios that wait for a grace period Kiryl Shutsemau (Meta)
2026-11-05 13:03 ` sashiko-bot
2026-11-04 13:01 ` [RFC PATCH v2 09/18] mm/memory-failure: handle folios with PG_rcu_free Kiryl Shutsemau (Meta)
2026-11-05 13:03 ` sashiko-bot
2026-11-04 13:01 ` [RFC PATCH v2 10/18] mm: have folio_mark_accessed() return whether the folio is hot Kiryl Shutsemau (Meta)
2026-11-04 13:01 ` [RFC PATCH v2 11/18] mm/filemap: mark a folio accessed in one place in filemap_read() Kiryl Shutsemau (Meta)
2026-11-04 13:01 ` [RFC PATCH v2 12/18] mm/filemap: set PG_rcu_free on the folios that reads keep coming back to Kiryl Shutsemau (Meta)
2026-11-04 13:01 ` [RFC PATCH v2 13/18] mm/filemap: read from the page cache without folio references Kiryl Shutsemau (Meta)
2026-11-04 13:01 ` [RFC PATCH v2 14/18] mm/filemap: read large folios " Kiryl Shutsemau (Meta)
2026-11-05 13:03 ` sashiko-bot
2026-11-04 13:01 ` [RFC PATCH v2 15/18] mm/filemap: don't store f_ra.prev_pos for FMODE_RANDOM files Kiryl Shutsemau (Meta)
2026-11-04 13:01 ` [RFC PATCH v2 16/18] mm/shmem: count accesses to a folio the way filemap_read() does Kiryl Shutsemau (Meta)
2026-11-04 13:01 ` [RFC PATCH v2 17/18] mm/shmem: read without folio references Kiryl Shutsemau (Meta)
2026-11-05 13:03 ` sashiko-bot
2026-11-04 13:01 ` [RFC PATCH v2 18/18] mm/filemap: add a debugfs switch for lockless page cache reads Kiryl Shutsemau (Meta)
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-199100@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 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.