From: Alex Markuze <amarkuze@redhat.com>
To: "David S . Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@kernel.org>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>
Cc: Simon Horman <horms@kernel.org>,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [RFC PATCH net-next 3/4] net: skb_splice_from_iter(): walk ITER_BVEC segments directly
Date: Tue, 10 Nov 2026 11:23:24 +0000 [thread overview]
Message-ID: <20261110112325.3263078-4-amarkuze@redhat.com> (raw)
In-Reply-To: <20261110112325.3263078-1-amarkuze@redhat.com>
ITER_BVEC already describes source pages and byte ranges. Walk its
segments directly instead of extracting a temporary page array and
reconstructing those ranges.
Split at folio and representation boundaries, retaining page-bounded
fragments on HIGHMEM. Reuse the fragment append and checksum helpers.
Advance only by appended bytes, leaving a short request's remainder
available for another skb. Compare a positive limit with the unsigned
iterator count before assigning the bounded result to ssize_t.
Other iterator types keep the page-extraction path.
Signed-off-by: Alex Markuze <amarkuze@redhat.com>
---
net/core/skbuff.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 3a66a50513a3..189fe25293ac 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -7550,6 +7550,75 @@ static void skb_splice_csum_range(struct sk_buff *skb, struct page *page,
}
}
+/*
+ * Splice from an ITER_BVEC by walking its segments: there is no page array
+ * to fill. Direct-mapped folio runs are split only at representation
+ * boundaries; CONFIG_HIGHMEM builds retain page-bounded fragments.
+ */
+static ssize_t skb_splice_from_bvec(struct sk_buff *skb, struct iov_iter *iter,
+ ssize_t maxsize, size_t frag_limit)
+{
+ const struct bio_vec *bv = iter->bvec;
+ size_t skip = iter->iov_offset;
+ ssize_t spliced = 0, ret = 0;
+ unsigned long seg;
+
+ if (maxsize <= 0)
+ return 0;
+ maxsize = min_t(size_t, maxsize, iter->count);
+ for (seg = 0; seg < iter->nr_segs && maxsize > 0; seg++, skip = 0) {
+ size_t boff = bv[seg].bv_offset + skip;
+ size_t blen = min_t(size_t, bv[seg].bv_len - skip, maxsize);
+
+ while (blen) {
+ struct page *page, *fpage;
+ struct folio *folio;
+ size_t foff, part;
+
+ page = bv[seg].bv_page + (boff >> PAGE_SHIFT);
+ folio = page_folio(page);
+ if (IS_ENABLED(CONFIG_HIGHMEM)) {
+ fpage = page;
+ foff = offset_in_page(boff);
+ part = min_t(size_t, blen, PAGE_SIZE - foff);
+ } else {
+ struct skb_splice_frag range;
+ u64 pos = ((u64)folio_page_idx(folio, page) << PAGE_SHIFT) +
+ offset_in_page(boff);
+
+ part = min_t(u64, blen, folio_size(folio) - pos);
+ range = skb_splice_frag_normalize(pos, part);
+ fpage = folio_page(folio, range.anchor >> PAGE_SHIFT);
+ foff = range.offset;
+ part = range.len;
+ }
+
+ ret = -EIO;
+ if (WARN_ON_ONCE(!sendpage_ok(fpage)))
+ goto out;
+
+ ret = skb_append_pagefrags(skb, fpage, foff, part, frag_limit);
+ if (ret < 0)
+ goto out;
+
+ if (skb->ip_summed == CHECKSUM_NONE)
+ skb_splice_csum_range(skb, page,
+ offset_in_page(boff),
+ part, skb->len + spliced);
+
+ boff += part;
+ blen -= part;
+ spliced += part;
+ maxsize -= part;
+ }
+ }
+
+out:
+ iov_iter_advance(iter, spliced);
+ skb_len_add(skb, spliced);
+ return spliced ?: ret;
+}
+
/**
* skb_splice_from_iter - Splice (or copy) pages to skbuff
* @skb: The buffer to add pages to
@@ -7564,6 +7633,7 @@ static void skb_splice_csum_range(struct sk_buff *skb, struct page *page,
* or a tail page of that folio and holds one reference on the folio either
* way. On CONFIG_HIGHMEM builds a fragment stays within one actual page
* of the folio, because kmap_local_page() maps only one page.
+ * A bvec iterator is walked directly, without extracting a page array.
*
* Returns the amount of data spliced/copied or -EMSGSIZE if there's
* insufficient space in the buffer to transfer anything.
@@ -7579,6 +7649,9 @@ ssize_t skb_splice_from_iter(struct sk_buff *skb, struct iov_iter *iter,
if (!maxsize)
return 0;
+ if (iov_iter_is_bvec(iter))
+ return skb_splice_from_bvec(skb, iter, maxsize, frag_limit);
+
while (iter->count > 0) {
ssize_t space, nr, len;
size_t off;
--
2.34.1
next prev parent reply other threads:[~2026-11-10 11:23 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 11:23 [RFC PATCH net-next 0/4] net: streamline skb_splice_from_iter() Alex Markuze
2026-11-10 11:23 ` [RFC PATCH net-next 1/4] net: preserve splice iterator progress on rejection Alex Markuze
2026-11-10 11:23 ` [RFC PATCH net-next 2/4] net: skb_splice_from_iter(): group consecutive pages of a folio Alex Markuze
2026-11-10 11:23 ` Alex Markuze [this message]
2026-11-10 11:23 ` [RFC PATCH net-next 4/4] net: add basic splice KUnit tests Alex Markuze
2026-11-10 11:26 ` [RFC PATCH net-next 0/4] net: streamline skb_splice_from_iter() netdev-bot+sinfo
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=20261110112325.3263078-4-amarkuze@redhat.com \
--to=amarkuze@redhat.com \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.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®