From: "David Hildenbrand (Arm)" <david@kernel.org>
To: 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>
Cc: "David Hildenbrand (Arm)" <david@kernel.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: [PATCH v3 9/9] RFC: net: limit folio refcount when splicing from sockets
Date: Tue, 10 Nov 2026 21:35:49 +0100 [thread overview]
Message-ID: <20261110-refcount_overflow_stable-v3-9-318625d63a98@kernel.org> (raw)
In-Reply-To: <20261110-refcount_overflow_stable-v3-0-318625d63a98@kernel.org>
Various networking features support splicing data from sockets into
pipes (TCP, AF_UNIX, KCM, kTLS), whereby spd_fill_page() takes an
additional page reference for each fragment, while the skb keeps its own
reference until it was fully consumed. This allows for exceeding folio
refcount limits.
This is not specific to large pagecache folios, but with them it is a
lot easier to reach the refcount limit, as page table mappings can cheaply
create many references.
Let's use try_get_page() in spd_fill_page() and stop splicing with
-EFAULT if that fails. To propagate the error, let spd_fill_page(),
__splice_segment() and __skb_splice_bits() return an int instead of a
bool. If we could not splice anything, return the error from
skb_splice_bits(): returning 0 would make the callers (TCP, AF_UNIX, KCM,
kTLS) report EOF, although data is still queued. The callers already
handle errors.
Use -EFAULT, consistent with splice_folio_into_pipe().
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>
---
net/core/skbuff.c | 90 +++++++++++++++++++++++++++++++------------------------
1 file changed, 51 insertions(+), 39 deletions(-)
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 5e3f910eeea0b..a5965f8fa1b00 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -3150,11 +3150,13 @@ static bool spd_can_coalesce(const struct splice_pipe_desc *spd,
}
/*
- * Fill page/offset/length into spd, if it can hold more pages.
+ * Fill page/offset/length into spd, if it can hold more pages and we can
+ * take a page reference. Returns non-zero to stop, or a negative error
+ * if we cannot take a page reference.
*/
-static bool spd_fill_page(struct splice_pipe_desc *spd, struct page *page,
- unsigned int *len, unsigned int offset, bool linear,
- struct sock *sk)
+static int spd_fill_page(struct splice_pipe_desc *spd, struct page *page,
+ unsigned int *len, unsigned int offset, bool linear,
+ struct sock *sk)
{
if (unlikely(spd->nr_pages == MAX_SKB_FRAGS))
return true;
@@ -3168,7 +3170,8 @@ static bool spd_fill_page(struct splice_pipe_desc *spd, struct page *page,
spd->partial[spd->nr_pages - 1].len += *len;
return false;
}
- get_page(page);
+ if (!try_get_page(page))
+ return -EFAULT;
spd->pages[spd->nr_pages] = page;
spd->partial[spd->nr_pages].len = *len;
spd->partial[spd->nr_pages].offset = offset;
@@ -3177,12 +3180,14 @@ static bool spd_fill_page(struct splice_pipe_desc *spd, struct page *page,
return false;
}
-static bool __splice_segment(struct page *page, unsigned int poff,
- unsigned int plen, unsigned int *off,
- unsigned int *len,
- struct splice_pipe_desc *spd, bool linear,
- struct sock *sk)
+static int __splice_segment(struct page *page, unsigned int poff,
+ unsigned int plen, unsigned int *off,
+ unsigned int *len,
+ struct splice_pipe_desc *spd, bool linear,
+ struct sock *sk)
{
+ int ret;
+
if (!*len)
return true;
@@ -3200,8 +3205,9 @@ static bool __splice_segment(struct page *page, unsigned int poff,
do {
unsigned int flen = min(*len, plen);
- if (spd_fill_page(spd, page, &flen, poff, linear, sk))
- return true;
+ ret = spd_fill_page(spd, page, &flen, poff, linear, sk);
+ if (ret)
+ return ret;
poff += flen;
plen -= flen;
*len -= flen;
@@ -3213,28 +3219,30 @@ static bool __splice_segment(struct page *page, unsigned int poff,
}
/*
- * Map linear and fragment data from the skb to spd. It reports true if the
- * pipe is full or if we already spliced the requested length.
+ * Map linear and fragment data from the skb to spd. It returns non-zero to
+ * stop: true if the pipe is full or if we already spliced the requested
+ * length, or a negative error if we cannot take a page reference.
*/
-static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe,
- unsigned int *offset, unsigned int *len,
- struct splice_pipe_desc *spd, struct sock *sk)
+static int __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe,
+ unsigned int *offset, unsigned int *len,
+ struct splice_pipe_desc *spd, struct sock *sk)
{
struct sk_buff *iter;
- int seg;
+ int seg, ret;
/* map the linear part :
* If skb->head_frag is set, this 'linear' part is backed by a
* fragment, and if the head is not shared with any clones then
* we can avoid a copy since we own the head portion of this page.
*/
- if (__splice_segment(virt_to_page(skb->data),
- (unsigned long) skb->data & (PAGE_SIZE - 1),
- skb_headlen(skb),
- offset, len, spd,
- skb_head_is_locked(skb),
- sk))
- return true;
+ ret = __splice_segment(virt_to_page(skb->data),
+ (unsigned long)skb->data & (PAGE_SIZE - 1),
+ skb_headlen(skb),
+ offset, len, spd,
+ skb_head_is_locked(skb),
+ sk);
+ if (ret)
+ return ret;
/*
* then map the fragments
@@ -3248,10 +3256,11 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe,
if (WARN_ON_ONCE(!skb_frag_page(f)))
return false;
- if (__splice_segment(skb_frag_page(f),
- skb_frag_off(f), skb_frag_size(f),
- offset, len, spd, false, sk))
- return true;
+ ret = __splice_segment(skb_frag_page(f),
+ skb_frag_off(f), skb_frag_size(f),
+ offset, len, spd, false, sk);
+ if (ret)
+ return ret;
}
skb_walk_frags(skb, iter) {
@@ -3260,11 +3269,12 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe,
continue;
}
/* __skb_splice_bits() only fails if the output has no room
- * left, so no point in going over the frag_list for the error
- * case.
+ * left or on error, so no point in going over the frag_list
+ * for the error case.
*/
- if (__skb_splice_bits(iter, pipe, offset, len, spd, sk))
- return true;
+ ret = __skb_splice_bits(iter, pipe, offset, len, spd, sk);
+ if (ret)
+ return ret;
}
return false;
@@ -3272,7 +3282,9 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe,
/*
* Map data from the skb to a pipe. Should handle both the linear part,
- * the fragments, and the frag list.
+ * the fragments, and the frag list. Returns the number of bytes spliced,
+ * or an error if we could not splice anything because we could not take
+ * a page reference.
*/
int skb_splice_bits(struct sk_buff *skb, struct sock *sk, unsigned int offset,
struct pipe_inode_info *pipe, unsigned int tlen,
@@ -3287,14 +3299,14 @@ int skb_splice_bits(struct sk_buff *skb, struct sock *sk, unsigned int offset,
.ops = &nosteal_pipe_buf_ops,
.spd_release = sock_spd_release,
};
- int ret = 0;
+ int ret;
- __skb_splice_bits(skb, pipe, &offset, &tlen, &spd, sk);
+ ret = __skb_splice_bits(skb, pipe, &offset, &tlen, &spd, sk);
if (spd.nr_pages)
- ret = splice_to_pipe(pipe, &spd);
-
- return ret;
+ return splice_to_pipe(pipe, &spd);
+ /* Returning 0 would make callers report EOF. */
+ return ret < 0 ? ret : 0;
}
EXPORT_SYMBOL_GPL(skb_splice_bits);
--
2.56.0
prev parent reply other threads:[~2026-11-10 20:39 UTC|newest]
Thread overview: 10+ 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-10 20:35 ` [PATCH v3 2/9] mm: limit pagecache folio refcount when splicing into pipes 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-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-10 20:35 ` [PATCH v3 6/9] RFC: net: limit folio refcount when copying skb frag references David Hildenbrand (Arm)
2026-11-10 20:35 ` [PATCH v3 7/9] RFC: net: limit folio refcount when rearranging skb frags David Hildenbrand (Arm)
2026-11-10 20:35 ` [PATCH v3 8/9] RFC: net: limit folio refcount when splicing pages into skbs David Hildenbrand (Arm)
2026-11-10 20:35 ` David Hildenbrand (Arm) [this message]
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=20261110-refcount_overflow_stable-v3-9-318625d63a98@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=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
all inboxes | Powered by JetHome®