From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
To: 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, linux-kernel@vger.kernel.org,
linux-fsdevel@vger.kernel.org, fuse-devel@lists.linux.dev,
kernel-team@meta.com, "Kiryl Shutsemau (Meta)" <kas@kernel.org>
Subject: [RFC PATCH 05/18] mm: expedite the grace period when a CPU has many deferred frees
Date: Mon, 19 Oct 2026 16:48:58 +0100 [thread overview]
Message-ID: <20261019154913.1765974-6-kas@kernel.org> (raw)
In-Reply-To: <20261019154913.1765974-1-kas@kernel.org>
From: Linus Torvalds <torvalds@linux-foundation.org>
A deferred folio waits one to two grace periods on its CPU's list, where
the page allocator cannot use it. A CPU that frees many flagged folios
at once, for example when a file is truncated, can hold a lot of memory
there.
When more than 4 MiB is pending on a CPU, a work takes both of the CPU's
lists and frees them after an expedited grace period. That grace period
starts after the take, so it covers every reader that found one of the
folios. The task that frees them does not wait.
An expedited grace period interrupts every CPU, so keep it for when the
memory matters. That is when the zone is below its high watermark, or
when the pages waiting in it are more than an eighth of its free pages.
The watermark alone comes too late for a bulk free. A truncate or
fadvise(POSIX_FADV_DONTNEED) of a hot range defers all of it while the
zone still has memory. When the readers that refill the range then run
short, nothing is freed that would check again.
With rcu_normal, as on PREEMPT_RT, the expedited grace period is a
normal one.
pgfree_rcu_expedite in /proc/vmstat counts the expedite runs that freed
something.
Assisted-by: LLM
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
[kas: take both lists, expedite only when the memory matters, rewrote
the changelog]
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
---
include/linux/vm_event_item.h | 2 +-
mm/page_alloc.c | 61 ++++++++++++++++++++++++++++++++++-
mm/vmstat.c | 1 +
3 files changed, 62 insertions(+), 2 deletions(-)
diff --git a/include/linux/vm_event_item.h b/include/linux/vm_event_item.h
index cbe849701653..45527fa7d0c1 100644
--- a/include/linux/vm_event_item.h
+++ b/include/linux/vm_event_item.h
@@ -36,7 +36,7 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT,
FOR_ALL_ZONES(ALLOCSTALL)
FOR_ALL_ZONES(PGSCAN_SKIP)
PGFREE, PGACTIVATE, PGDEACTIVATE, PGLAZYFREE,
- PGFREE_RCU,
+ PGFREE_RCU, PGFREE_RCU_EXPEDITE,
PGFAULT, PGMAJFAULT,
PGLAZYFREED,
PGREUSE,
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 15bb66e36304..a9058d414f92 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1313,6 +1313,8 @@ static inline void pgalloc_tag_sub_pages(struct alloc_tag *tag, unsigned int nr)
#endif /* CONFIG_MEM_ALLOC_PROFILING */
+#define RCU_FREE_EXPEDITE (SZ_4M >> PAGE_SHIFT)
+
/*
* Each CPU has at most one grace period in flight (@queued), covering
* @batch. Folios freed meanwhile wait on @pending for the next one.
@@ -1322,7 +1324,9 @@ struct rcu_free_pcpu {
bool queued;
struct list_head pending;
struct list_head batch;
+ unsigned long nr_pending;
struct rcu_work rwork;
+ struct work_struct expedite;
};
static DEFINE_PER_CPU(struct rcu_free_pcpu, rcu_free_pcpu);
@@ -1337,17 +1341,29 @@ static void rcu_free_start(struct rcu_free_pcpu *r)
return;
list_splice_init(&r->pending, &r->batch);
+ r->nr_pending = 0;
r->queued = true;
WARN_ON_ONCE(!queue_rcu_work(rcu_free_wq, &r->rwork));
}
-static void rcu_free_pages(struct list_head *list)
+static void rcu_free_take(struct rcu_free_pcpu *r, struct list_head *list)
+{
+ lockdep_assert_held(&r->lock);
+
+ list_splice_tail_init(&r->batch, list);
+ list_splice_tail_init(&r->pending, list);
+ r->nr_pending = 0;
+}
+
+static unsigned long rcu_free_pages(struct list_head *list)
{
struct folio_batch fbatch;
struct folio *folio, *next;
+ unsigned long nr = 0;
folio_batch_init(&fbatch);
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)) {
@@ -1357,6 +1373,17 @@ static void rcu_free_pages(struct list_head *list)
}
if (folio_batch_count(&fbatch))
free_unref_folios(&fbatch);
+ return nr;
+}
+
+/* Free what rcu_free_take() took: the grace period starts after that */
+static unsigned long rcu_free_flush(struct list_head *list)
+{
+ if (list_empty(list))
+ return 0;
+
+ synchronize_rcu_expedited();
+ return rcu_free_pages(list);
}
static void rcu_free_workfn(struct work_struct *work)
@@ -1374,17 +1401,48 @@ static void rcu_free_workfn(struct work_struct *work)
rcu_free_pages(&done);
}
+static void rcu_free_expedite_workfn(struct work_struct *work)
+{
+ struct rcu_free_pcpu *r = container_of(work, struct rcu_free_pcpu,
+ expedite);
+ LIST_HEAD(list);
+
+ spin_lock_irq(&r->lock);
+ rcu_free_take(r, &list);
+ spin_unlock_irq(&r->lock);
+
+ if (rcu_free_flush(&list))
+ count_vm_event(PGFREE_RCU_EXPEDITE);
+}
+
static void rcu_free_defer(struct folio *folio)
{
struct rcu_free_pcpu *r = raw_cpu_ptr(&rcu_free_pcpu);
struct zone *zone = folio_zone(folio);
+ unsigned long free, waiting;
unsigned long flags;
spin_lock_irqsave(&r->lock, flags);
__mod_zone_page_state(zone, NR_FREE_RCU_PAGES, folio_nr_pages(folio));
list_add_tail(&folio->lru, &r->pending);
+ r->nr_pending += folio_nr_pages(folio);
rcu_free_start(r);
spin_unlock_irqrestore(&r->lock, flags);
+
+ /* Unlocked: a stale count delays the expedite by one free */
+ if (data_race(r->nr_pending) <= RCU_FREE_EXPEDITE ||
+ work_pending(&r->expedite))
+ return;
+
+ /*
+ * An expedited grace period interrupts every CPU: only while the
+ * zone is short of memory, or while more than an eighth of what it
+ * has free waits here.
+ */
+ free = zone_page_state(zone, NR_FREE_PAGES);
+ waiting = zone_page_state(zone, NR_FREE_RCU_PAGES);
+ if (free < high_wmark_pages(zone) || waiting > free / 8)
+ queue_work(rcu_free_wq, &r->expedite);
}
static void __init rcu_free_init(void)
@@ -1404,6 +1462,7 @@ static void __init rcu_free_init(void)
INIT_LIST_HEAD(&r->pending);
INIT_LIST_HEAD(&r->batch);
INIT_RCU_WORK(&r->rwork, rcu_free_workfn);
+ INIT_WORK(&r->expedite, rcu_free_expedite_workfn);
}
}
diff --git a/mm/vmstat.c b/mm/vmstat.c
index 64068d73d6d2..aa7280b5e69c 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -1345,6 +1345,7 @@ const char * const vmstat_text[] = {
[I(PGDEACTIVATE)] = "pgdeactivate",
[I(PGLAZYFREE)] = "pglazyfree",
[I(PGFREE_RCU)] = "pgfree_rcu",
+ [I(PGFREE_RCU_EXPEDITE)] = "pgfree_rcu_expedite",
[I(PGFAULT)] = "pgfault",
[I(PGMAJFAULT)] = "pgmajfault",
--
2.54.0
next prev parent reply other threads:[~2026-10-19 15:50 UTC|newest]
Thread overview: 20+ 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 ` Kiryl Shutsemau (Meta) [this message]
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-19 15:49 ` [RFC PATCH 07/18] mm: free the pages waiting for a grace period before isolation fails Kiryl Shutsemau (Meta)
2026-10-19 15:49 ` [RFC PATCH 08/18] mm/memory-failure: handle folios with PG_rcu_free Kiryl Shutsemau (Meta)
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-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-19 15:49 ` [RFC PATCH 15/18] mm/filemap: read large folios " Kiryl Shutsemau (Meta)
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=20261019154913.1765974-6-kas@kernel.org \
--to=kas@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=fuse-devel@lists.linux.dev \
--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=kasong@tencent.com \
--cc=kernel-team@meta.com \
--cc=lance.yang@linux.dev \
--cc=liam@infradead.org \
--cc=linmiaohe@huawei.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--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=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