Skip to content

Commit 2e910b9

Browse files
dhowellskuba-moo
authored andcommitted
net: Add a function to splice pages into an skbuff for MSG_SPLICE_PAGES
Add a function to handle MSG_SPLICE_PAGES being passed internally to sendmsg(). Pages are spliced into the given socket buffer if possible and copied in if not (e.g. they're slab pages or have a zero refcount). Signed-off-by: David Howells <[email protected]> cc: David Ahern <[email protected]> cc: Al Viro <[email protected]> cc: Jens Axboe <[email protected]> cc: Matthew Wilcox <[email protected]> Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 96449f9 commit 2e910b9

File tree

2 files changed

+91
-0
lines changed

2 files changed

+91
-0
lines changed

include/linux/skbuff.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5097,5 +5097,8 @@ static inline void skb_mark_for_recycle(struct sk_buff *skb)
50975097
#endif
50985098
}
50995099

5100+
ssize_t skb_splice_from_iter(struct sk_buff *skb, struct iov_iter *iter,
5101+
ssize_t maxsize, gfp_t gfp);
5102+
51005103
#endif /* __KERNEL__ */
51015104
#endif /* _LINUX_SKBUFF_H */

net/core/skbuff.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6892,3 +6892,91 @@ nodefer: __kfree_skb(skb);
68926892
if (unlikely(kick) && !cmpxchg(&sd->defer_ipi_scheduled, 0, 1))
68936893
smp_call_function_single_async(cpu, &sd->defer_csd);
68946894
}
6895+
6896+
static void skb_splice_csum_page(struct sk_buff *skb, struct page *page,
6897+
size_t offset, size_t len)
6898+
{
6899+
const char *kaddr;
6900+
__wsum csum;
6901+
6902+
kaddr = kmap_local_page(page);
6903+
csum = csum_partial(kaddr + offset, len, 0);
6904+
kunmap_local(kaddr);
6905+
skb->csum = csum_block_add(skb->csum, csum, skb->len);
6906+
}
6907+
6908+
/**
6909+
* skb_splice_from_iter - Splice (or copy) pages to skbuff
6910+
* @skb: The buffer to add pages to
6911+
* @iter: Iterator representing the pages to be added
6912+
* @maxsize: Maximum amount of pages to be added
6913+
* @gfp: Allocation flags
6914+
*
6915+
* This is a common helper function for supporting MSG_SPLICE_PAGES. It
6916+
* extracts pages from an iterator and adds them to the socket buffer if
6917+
* possible, copying them to fragments if not possible (such as if they're slab
6918+
* pages).
6919+
*
6920+
* Returns the amount of data spliced/copied or -EMSGSIZE if there's
6921+
* insufficient space in the buffer to transfer anything.
6922+
*/
6923+
ssize_t skb_splice_from_iter(struct sk_buff *skb, struct iov_iter *iter,
6924+
ssize_t maxsize, gfp_t gfp)
6925+
{
6926+
size_t frag_limit = READ_ONCE(sysctl_max_skb_frags);
6927+
struct page *pages[8], **ppages = pages;
6928+
ssize_t spliced = 0, ret = 0;
6929+
unsigned int i;
6930+
6931+
while (iter->count > 0) {
6932+
ssize_t space, nr;
6933+
size_t off, len;
6934+
6935+
ret = -EMSGSIZE;
6936+
space = frag_limit - skb_shinfo(skb)->nr_frags;
6937+
if (space < 0)
6938+
break;
6939+
6940+
/* We might be able to coalesce without increasing nr_frags */
6941+
nr = clamp_t(size_t, space, 1, ARRAY_SIZE(pages));
6942+
6943+
len = iov_iter_extract_pages(iter, &ppages, maxsize, nr, 0, &off);
6944+
if (len <= 0) {
6945+
ret = len ?: -EIO;
6946+
break;
6947+
}
6948+
6949+
i = 0;
6950+
do {
6951+
struct page *page = pages[i++];
6952+
size_t part = min_t(size_t, PAGE_SIZE - off, len);
6953+
6954+
ret = -EIO;
6955+
if (WARN_ON_ONCE(!sendpage_ok(page)))
6956+
goto out;
6957+
6958+
ret = skb_append_pagefrags(skb, page, off, part,
6959+
frag_limit);
6960+
if (ret < 0) {
6961+
iov_iter_revert(iter, len);
6962+
goto out;
6963+
}
6964+
6965+
if (skb->ip_summed == CHECKSUM_NONE)
6966+
skb_splice_csum_page(skb, page, off, part);
6967+
6968+
off = 0;
6969+
spliced += part;
6970+
maxsize -= part;
6971+
len -= part;
6972+
} while (len > 0);
6973+
6974+
if (maxsize <= 0)
6975+
break;
6976+
}
6977+
6978+
out:
6979+
skb_len_add(skb, spliced);
6980+
return spliced ?: ret;
6981+
}
6982+
EXPORT_SYMBOL(skb_splice_from_iter);

0 commit comments

Comments
 (0)