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: "Kiryl Shutsemau (Meta)" <kas@kernel.org>,
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
Subject: [RFC PATCH 15/18] mm/filemap: read large folios without folio references
Date: Mon, 19 Oct 2026 16:49:08 +0100 [thread overview]
Message-ID: <20261019154913.1765974-16-kas@kernel.org> (raw)
In-Reply-To: <20261019154913.1765974-1-kas@kernel.org>
A large folio is in all the slots of its range or in none. Once the
slot of the last page to copy holds it too, the pages in between are
this folio's: copy them after one lookup. The folio's size is read
without a reference and only picks the page to check; a split after the
check copies PG_rcu_free to the new folios.
Copy page by page with _copy_to_iter(): the hardened usercopy check of
copy_to_iter() reads the folio's order, which a split can change.
Assisted-by: LLM
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
---
mm/filemap.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 57 insertions(+), 7 deletions(-)
diff --git a/mm/filemap.c b/mm/filemap.c
index 55fc471c0489..ace6b1fd6b9e 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -2843,9 +2843,6 @@ static bool filemap_read_rcu_can_copy(struct folio *folio, pgoff_t index,
/* memory_failure() waits only for readers that got past this */
if (folio_contain_hwpoisoned_page(folio))
return false;
- /* One reference covers all of a large folio's pages */
- if (folio_test_large(folio))
- return false;
/*
* The slow path does not call folio_mark_accessed() on the folio that
@@ -2899,6 +2896,60 @@ static struct folio *filemap_read_rcu_lookup(struct xa_state *xas,
return folio;
}
+/*
+ * How much of @bytes, starting @offset into page @index, can be copied from
+ * @folio. A folio is in all the slots of its range or in none: when the slot
+ * of the last page holds it too, so do the slots in between, and the pages
+ * are this folio's.
+ */
+static size_t filemap_read_rcu_size(struct xa_state *xas, struct folio *folio,
+ pgoff_t index, size_t offset, size_t bytes)
+{
+ pgoff_t end = index + DIV_ROUND_UP(offset + bytes, PAGE_SIZE);
+ pgoff_t next;
+
+ if (end - index > 1 && folio_test_large(folio)) {
+ /* Read without a reference: it only picks the page to check */
+ next = folio_next_index(folio);
+ if (next > index && next < end)
+ end = next;
+ xas_set(xas, end - 1);
+ /* A split after this copies PG_rcu_free to the new folios */
+ if (xas_load(xas) == folio)
+ return min(bytes, (end - index) * PAGE_SIZE - offset);
+ }
+ return min(bytes, PAGE_SIZE - offset);
+}
+
+/*
+ * Copy @bytes from @folio, starting @offset into its page at @index. Page
+ * by page, as copy_page_to_iter() does, without its checks for each call.
+ * Not copy_to_iter(): its usercopy check reads the folio's order, which a
+ * split can change.
+ */
+static size_t filemap_read_rcu_copy(struct folio *folio, pgoff_t index,
+ size_t offset, size_t bytes, struct iov_iter *iter)
+{
+ struct page *page = folio_page(folio, index - folio->index);
+ size_t n = 0;
+
+ while (n < bytes) {
+ size_t len = min(bytes - n, PAGE_SIZE - offset);
+ size_t done;
+ void *kaddr;
+
+ kaddr = kmap_local_page(page);
+ done = _copy_to_iter(kaddr + offset, len, iter);
+ kunmap_local(kaddr);
+ n += done;
+ if (done < len)
+ break;
+ offset = 0;
+ page++;
+ }
+ return n;
+}
+
/* Bytes one RCU read section copies at most: it holds up grace periods */
#define FILEMAP_RCU_READ_MAX SZ_512K
@@ -2944,10 +2995,9 @@ static bool filemap_read_rcu_batch(struct kiocb *iocb, struct iov_iter *iter,
break;
bytes = min_t(loff_t, isize - pos, iov_iter_count(iter));
- bytes = min3(bytes, budget, PAGE_SIZE - offset);
- /* Not _nofault(): its usercopy check reads the folio's order */
- n = copy_page_to_iter(folio_page(folio, index - folio->index),
- offset, bytes, iter);
+ bytes = filemap_read_rcu_size(&xas, folio, index, offset,
+ min(bytes, budget));
+ n = filemap_read_rcu_copy(folio, index, offset, bytes, iter);
*copied += n;
iocb->ki_pos += n;
budget -= n;
--
2.54.0
next prev parent reply other threads:[~2026-10-19 15:51 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 ` [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-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 ` Kiryl Shutsemau (Meta) [this message]
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-16-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