From: "David Hildenbrand (Arm)" <david@kernel.org>
To: Pedro Falcato <pfalcato@suse.de>
Cc: 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>,
"Matthew Wilcox (Oracle)" <willy@infradead.org>,
Jan Kara <jack@suse.cz>,
Andrew Morton <akpm@linux-foundation.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>,
Kiryl Shutsemau <kas@kernel.org>,
Matthew Brost <matthew.brost@intel.com>,
Joshua Hahn <joshua.hahnjy@gmail.com>,
Byungchul Park <byungchul@sk.com>,
Gregory Price <gourry@gourry.net>,
Ying Huang <ying.huang@linux.alibaba.com>,
Alistair Popple <apopple@nvidia.com>,
Peter Xu <peterx@redhat.com>,
Johannes Weiner <hannes@cmpxchg.org>,
Song Liu <songliubraving@fb.com>, Rik van Riel <riel@surriel.com>,
Miklos Szeredi <miklos@szeredi.hu>,
Alexander Viro <viro@zeniv.linux.org.uk>,
Christian Brauner <brauner@kernel.org>,
Hugh Dickins <hughd@google.com>, Will Deacon <will@kernel.org>,
"Aneesh Kumar K.V" <aneesh.kumar@kernel.org>,
Nick Piggin <npiggin@gmail.com>,
Peter Zijlstra <peterz@infradead.org>,
Arnd Bergmann <arnd@arndb.de>, Jason Gunthorpe <jgg@ziepe.ca>,
Kevin Tian <kevin.tian@intel.com>,
"Joerg Roedel (AMD)" <joro@8bytes.org>,
Robin Murphy <robin.murphy@arm.com>,
Steve Sistare <steven.sistare@oracle.com>,
John Hubbard <jhubbard@nvidia.com>,
"Jose A. Perez de Azpillaga" <azpijr@gmail.com>,
Josef Bacik <josef@toxicpanda.com>,
"David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@kernel.org>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Simon Horman <horms@kernel.org>,
Neal Cardwell <ncardwell@google.com>,
Kuniyuki Iwashima <kuniyu@google.com>,
Linus Torvalds <torvalds@linux-foundation.org>,
linux-mm@kvack.org, linux-kernel@vger.kernel.org,
linux-fsdevel@vger.kernel.org, fuse-devel@lists.linux.dev,
linux-arch@vger.kernel.org, iommu@lists.linux.dev,
netdev@vger.kernel.org, stable@vger.kernel.org
Subject: Re: [PATCH v3 2/9] mm: limit pagecache folio refcount when splicing into pipes
Date: Thu, 12 Nov 2026 14:24:09 +0100 [thread overview]
Message-ID: <4ae22b7a-c160-4003-a029-8a20381d196f@kernel.org> (raw)
In-Reply-To: <avW3v-oLxcHXXNQD@pedro-suse.tail5790ac.ts.net>
On 11/12/26 14:09, Pedro Falcato wrote:
> On Tue, Nov 10, 2026 at 09:35:42PM +0100, David Hildenbrand (Arm) wrote:
>> Similar to mapping pagecache folios into page tables, let's reject
>> additional references when splicing folios into a pipe if the refcount
>> is at its limit, similar to how we handle it during GUP.
>>
>> This will prevent more cases of possible folio refcount overflows with
>> large pagecache folios.
>>
>> Introduce folio_get_checked() for checked reference acquisition when
>> the caller already holds a reference. It warns and fails on a non-positive
>> refcount and rejects additional references at the limit.
>>
>> Use folio_get_checked() in splice_folio_into_pipe() and
>> fuse_ref_folio(), returning -EFAULT on failure, like we already do for
>> pipe-buffer duplication. Similarly, make try_get_page() use it instead.
>>
>> Likely try_get_page() should be renamed (or removed) later, as it doesn't
>> match the folio_try_get() semantics and is confusing.
>>
>> If we hit the limit after splicing some data, return the number of bytes
>> already spliced. Teach the callers to handle errors, and stop
>> filemap_splice_read() if we only spliced part of a folio, so we don't
>> skip ahead to the next folio.
>>
>> The check in try_get_page() also covers pipe-buffer duplication by tee()
>> and partial pipe-to-pipe splicing.
>>
>> Note that we deliberately will not fall back to the slow path of copying
>> the folio, particularly as the folio might have an invalid refcount. If
>> it is ever really required, we can revisit.
>>
>> Fixes: 800d8c63b2e9 ("shmem: add huge pages support")
>> Fixes: 99cb0dbd47a1 ("mm,thp: add read-only THP support for (non-shmem) FS")
>> Fixes: 793917d997df ("mm/readahead: Add large folio readahead")
>> Cc: stable@vger.kernel.org
>> Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
>> ---
[...]
>> +static inline __must_check bool folio_get_checked(struct folio *folio)
>> {
>> - page = compound_head(page);
>> - if (WARN_ON_ONCE(page_ref_count(page) <= 0))
>> + if (WARN_ON_ONCE(folio_ref_count(folio) <= 0))
>> return false;
>> - page_ref_inc(page);
>> + if (unlikely(folio_ref_count_over_limit(folio)))
>> + return false;
>
> Ah nice, the helper I wanted :)
>
> Nit: Having these two functions called back-to-back will result in two
> separate loads on the refcount (folio_ref_count() distills into a READ_ONCE).
Ack, something to optimize. I'd likely leave that for future work, as we'd need
yet another helper.
Unless I can convince myself that it is easy to do.
>
>> + folio_ref_inc(folio);
>> return true;
>> }
>>
>> +static inline __must_check bool try_get_page(struct page *page)
>> +{
>> + return folio_get_checked(page_folio(page));
>> +
>
> God I hate this. I never realized try_get_page() isn't atomic, but
> folio_try_get() is.
Yeah, but that's for another purpose and more expensive.
>
>> +
>> /**
>> * folio_put - Decrement the reference count on a folio.
>> * @folio: The folio.
>> diff --git a/mm/filemap.c b/mm/filemap.c
>> index 0f6d57432a7e5..e6e73e37f3ea4 100644
>> --- a/mm/filemap.c
>> +++ b/mm/filemap.c
>> @@ -3047,10 +3047,11 @@ generic_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
>> EXPORT_SYMBOL(generic_file_read_iter);
>>
>> /*
>> - * Splice subpages from a folio into a pipe.
>> + * Splice subpages from a folio into a pipe. Return the number of bytes spliced,
>> + * or -EFAULT if we cannot splice from the folio.
>
> -EFAULT is a weird error code to return from splice, et al.
We might be able to use ETOOMANYREFS (have to check if that could confuse
callers), but IMHO EFAULT is good enough.
What would be your suggestion?
>
>> */
>> -size_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
>> - struct folio *folio, loff_t fpos, size_t size)
>> +ssize_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
>> + struct folio *folio, loff_t fpos, size_t size)
>> {
>> struct page *page;
>> size_t spliced = 0, offset = offset_in_folio(folio, fpos);
>> @@ -3063,13 +3064,15 @@ size_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
>> struct pipe_buffer *buf = pipe_head_buf(pipe);
>> size_t part = min_t(size_t, PAGE_SIZE - offset, size - spliced);
>>
>> + if (!folio_get_checked(folio))
>> + return spliced ? spliced : -EFAULT;
> return spliced ?: -EFAULT;
>
> I don't know if there's much kernel code out there doing this, but I like
> the trick :)
There are a couple of thousands, but yes, one doesn't encounter it that often.
>
>> +
>> *buf = (struct pipe_buffer) {
>> .ops = &page_cache_pipe_buf_ops,
>> .page = page,
>> .offset = offset,
>> .len = part,
>> };
>> - folio_get(folio);
>> pipe->head++;
>> page++;
>> spliced += part;
>> @@ -3094,9 +3097,10 @@ size_t splice_folio_into_pipe(struct pipe_inode_info *pipe,
>> * Return: On success, the number of bytes read will be returned and *@ppos
>> * will be updated if appropriate; 0 will be returned if there is no more data
>> * to be read; -EAGAIN will be returned if the pipe had no space, and some
>> - * other negative error code will be returned on error. A short read may occur
>> - * if the pipe has insufficient space, we reach the end of the data or we hit a
>> - * hole.
>> + * other negative error code will be returned on error; -EFAULT will be returned
>> + * if acquiring a folio reference fails and no data has been spliced.
>> + * A short read may occur if the pipe has insufficient space, we reach the
>> + * end of the data, hit a hole or hit a folio refcount limit.
>> */
>> ssize_t filemap_splice_read(struct file *in, loff_t *ppos,
>> struct pipe_inode_info *pipe,
>> @@ -3154,7 +3158,7 @@ ssize_t filemap_splice_read(struct file *in, loff_t *ppos,
>>
>> for (i = 0; i < folio_batch_count(&fbatch); i++) {
>> struct folio *folio = fbatch.folios[i];
>> - size_t n;
>> + ssize_t n;
>>
>> if (folio_pos(folio) >= end_offset)
>> goto out;
>> @@ -3170,13 +3174,15 @@ ssize_t filemap_splice_read(struct file *in, loff_t *ppos,
>>
>> n = min_t(loff_t, len, isize - *ppos);
>> n = splice_folio_into_pipe(pipe, folio, *ppos, n);
>> - if (!n)
>> + if (n <= 0) {
>> + error = n;
>> goto out;
>> + }
>> len -= n;
>> total_spliced += n;
>> *ppos += n;
>> in->f_ra.prev_pos = *ppos;
>> - if (pipe_is_full(pipe))
>> + if (pipe_is_full(pipe) || *ppos < folio_next_pos(folio))
>
> I'm confused. Why does this need to be done here?
>
I have above:
"stop filemap_splice_read() if we only spliced part of a folio, so we don't
skip ahead to the next folio"
Thanks a lot for the review!
--
Cheers,
David
next prev parent reply other threads:[~2026-11-12 13:24 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 20:35 [PATCH v3 0/9] mm: limit pagecache folio refcount David Hildenbrand (Arm)
2026-11-10 20:35 ` [PATCH v3 1/9] mm: limit pagecache folio refcount when mapping pages David Hildenbrand (Arm)
2026-11-12 10:42 ` David Hildenbrand (Arm)
2026-11-12 12:55 ` Pedro Falcato
2026-11-12 13:16 ` David Hildenbrand (Arm)
2026-11-12 14:06 ` Pedro Falcato
2026-11-12 14:16 ` David Hildenbrand (Arm)
2026-11-12 14:26 ` Pedro Falcato
2026-11-12 14:38 ` David Hildenbrand (Arm)
2026-11-10 20:35 ` [PATCH v3 2/9] mm: limit pagecache folio refcount when splicing into pipes David Hildenbrand (Arm)
2026-11-10 21:34 ` Jose A. Perez de Azpillaga
2026-11-12 13:09 ` Pedro Falcato
2026-11-12 13:24 ` David Hildenbrand (Arm) [this message]
2026-11-12 14:00 ` Pedro Falcato
2026-11-12 14:31 ` David Hildenbrand (Arm)
2026-11-10 20:35 ` [PATCH v3 3/9] mm/mmu_gather: flush pending references early before the folio limit David Hildenbrand (Arm)
2026-11-12 13:10 ` Pedro Falcato
2026-11-10 20:35 ` [PATCH v3 4/9] iommufd: fix cleanup when adding memfd folio pins fails David Hildenbrand (Arm)
2026-11-10 20:35 ` [PATCH v3 5/9] mm/gup: limit pagecache folio refcount when grabbing references David Hildenbrand (Arm)
2026-11-12 13:12 ` Pedro Falcato
2026-11-12 14:33 ` David Hildenbrand (Arm)
2026-11-10 20:35 ` [PATCH v3 6/9] RFC: net: limit folio refcount when copying skb frag references David Hildenbrand (Arm)
2026-11-12 16:05 ` Pedro Falcato
2026-11-10 20:35 ` [PATCH v3 7/9] RFC: net: limit folio refcount when rearranging skb frags David Hildenbrand (Arm)
2026-11-12 16:08 ` Pedro Falcato
2026-11-10 20:35 ` [PATCH v3 8/9] RFC: net: limit folio refcount when splicing pages into skbs David Hildenbrand (Arm)
2026-11-12 16:09 ` Pedro Falcato
2026-11-10 20:35 ` [PATCH v3 9/9] RFC: net: limit folio refcount when splicing from sockets David Hildenbrand (Arm)
2026-11-12 16:10 ` Pedro Falcato
2026-11-11 21:37 ` [PATCH v3 0/9] mm: limit pagecache folio refcount David Hildenbrand (Arm)
2026-11-12 9:39 ` (subset) " David Hildenbrand
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=4ae22b7a-c160-4003-a029-8a20381d196f@kernel.org \
--to=david@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=aneesh.kumar@kernel.org \
--cc=apopple@nvidia.com \
--cc=arnd@arndb.de \
--cc=azpijr@gmail.com \
--cc=baohua@kernel.org \
--cc=baolin.wang@linux.alibaba.com \
--cc=brauner@kernel.org \
--cc=byungchul@sk.com \
--cc=davem@davemloft.net \
--cc=dev.jain@arm.com \
--cc=edumazet@kernel.org \
--cc=fuse-devel@lists.linux.dev \
--cc=gourry@gourry.net \
--cc=hannes@cmpxchg.org \
--cc=horms@kernel.org \
--cc=hughd@google.com \
--cc=iommu@lists.linux.dev \
--cc=jack@suse.cz \
--cc=jgg@ziepe.ca \
--cc=jhubbard@nvidia.com \
--cc=joro@8bytes.org \
--cc=josef@toxicpanda.com \
--cc=joshua.hahnjy@gmail.com \
--cc=kas@kernel.org \
--cc=kevin.tian@intel.com \
--cc=kuba@kernel.org \
--cc=kuniyu@google.com \
--cc=lance.yang@linux.dev \
--cc=liam@infradead.org \
--cc=linux-arch@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--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=miklos@szeredi.hu \
--cc=ncardwell@google.com \
--cc=netdev@vger.kernel.org \
--cc=nico.pache@linux.dev \
--cc=npiggin@gmail.com \
--cc=pabeni@redhat.com \
--cc=peterx@redhat.com \
--cc=peterz@infradead.org \
--cc=pfalcato@suse.de \
--cc=riel@surriel.com \
--cc=robin.murphy@arm.com \
--cc=rppt@kernel.org \
--cc=ryan.roberts@arm.com \
--cc=songliubraving@fb.com \
--cc=stable@vger.kernel.org \
--cc=steven.sistare@oracle.com \
--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=will@kernel.org \
--cc=willy@infradead.org \
--cc=ying.huang@linux.alibaba.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