zerocopy tx cleanups
Subsystems (4)
cover zerocopy tx cleanups
Assorted zerocopy send path cleanups, the main part of which is moving some net stack specific accounting out of io_uring back to net/ in Patch 4.
Pavel Begunkov (5):
net: always try to set ubuf in skb_zerocopy_iter_stream
net: split __zerocopy_sg_from_iter()
net: batch zerocopy_fill_skb_from_iter accounting io_uring/net: move charging socket out of zc io_uring
net: limit scope of a skb_zerocopy_iter_stream var
include/linux/skbuff.h | 3 +++ include/linux/socket.h | 2 +- io_uring/net.c | 16 ++++---------- net/core/datagram.c | 47 +++++++++++++++++++++++++----------------- net/core/skbuff.c | 6 +++--- 5 files changed, 39 insertions(+), 35 deletions(-)
Signature · 1 line
2.44.0
PATCH 1/5 net: always try to set ubuf in skb_zerocopy_iter_stream
skb_zcopy_set() does nothing if there is already a ubuf_info associated with an skb, and since ->link_skb should have set it several lines above the check here essentially does nothing and can be removed. It's also safer this way, because even if the callback is faulty we'll have it set.
--- net/core/skbuff.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
net/core/skbuff.c +1 -2
@@ -1884,8 +1884,7 @@ int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb,return err; } -if (!uarg->ops->link_skb) -skb_zcopy_set (skb, uarg,NULL ); +skb_zcopy_set (skb, uarg,NULL );return skb->len - orig_len; }EXPORT_SYMBOL_GPL (skb_zerocopy_iter_stream);
Signature · 1 line
2.44.0
Re: [PATCH net-next 1/5] net: always try to set ubuf in skb_zerocopy_iter_stream
Quoted 7 lines · Pavel Begunkov
skb_zcopy_set()does nothing if there is already aubuf_infoassociated with an skb, and since ->link_skb should have set it several lines above the check here essentially does nothing and can be removed. It's also safer this way, because even if the callback is faulty we'll have it set.Signed-off-by Pavel Begunkov <[email protected]>
PATCH 2/5 net: split __zerocopy_sg_from_iter()
In reply to Pavel BegunkovSplit a function out of __zerocopy_sg_from_iter() that only cares about the traditional path with refcounted pages and doesn't need to know about ->sg_from_iter. A preparation patch, we'll improve on the function later.
--- net/core/datagram.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-)
net/core/datagram.c +13 -9
@@ -610,16 +610,10 @@ int skb_copy_datagram_from_iter(struct sk_buff *skb, int offset, }EXPORT_SYMBOL (skb_copy_datagram_from_iter); -int __zerocopy_sg_from_iter (struct msghdr *msg,struct sock *sk, -struct sk_buff *skb,struct iov_iter *from, -size_t length) +static int zerocopy_fill_skb_from_iter (struct sock *sk,struct sk_buff *skb, +struct iov_iter *from,size_t length) { -int frag; - -if (msg && msg->msg_ubuf && msg->sg_from_iter) -return msg->sg_from_iter (sk, skb, from, length); - - frag =skb_shinfo (skb)->nr_frags; +int frag =skb_shinfo (skb)->nr_frags;while (length &&iov_iter_count (from)) {struct page *head, *last_head =NULL ;
@@ -692,6 +686,16 @@ int __zerocopy_sg_from_iter(struct msghdr *msg, struct sock *sk, }return 0 ; } + +int __zerocopy_sg_from_iter (struct msghdr *msg,struct sock *sk, +struct sk_buff *skb,struct iov_iter *from, +size_t length) +{ +if (msg && msg->msg_ubuf && msg->sg_from_iter) +return msg->sg_from_iter (sk, skb, from, length); +else +return zerocopy_fill_skb_from_iter (sk, skb, from, length); +}EXPORT_SYMBOL (__zerocopy_sg_from_iter);/**
Signature · 1 line
2.44.0
Re: [PATCH net-next 2/5] net: split __zerocopy_sg_from_iter()
Quoted 6 lines · Pavel Begunkov
Split a function out of
__zerocopy_sg_from_iter()that only cares about the traditional path with refcounted pages and doesn't need to know about ->sg_from_iter. A preparation patch, we'll improve on the function later.Signed-off-by Pavel Begunkov <[email protected]>
PATCH 3/5 net: batch zerocopy_fill_skb_from_iter accounting
In reply to Pavel BegunkovInstead of accounting every page range against the socket separately, do it in batch based on the change in skb->truesize. It's also moved into __zerocopy_sg_from_iter(), so that zerocopy_fill_skb_from_iter() is simpler and responsible for setting frags but not the accounting.
--- net/core/datagram.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-)
net/core/datagram.c +18 -13
@@ -610,7 +610,7 @@ int skb_copy_datagram_from_iter(struct sk_buff *skb, int offset, }EXPORT_SYMBOL (skb_copy_datagram_from_iter); -static int zerocopy_fill_skb_from_iter (struct sock *sk,struct sk_buff *skb, +static int zerocopy_fill_skb_from_iter (struct sk_buff *skb,struct iov_iter *from,size_t length) {int frag =skb_shinfo (skb)->nr_frags;
@@ -621,7 +621,6 @@ static int zerocopy_fill_skb_from_iter(struct sock *sk, struct sk_buff *skb,int refs, order, n =0 ;size_t start;ssize_t copied; -unsigned long truesize;if (frag ==MAX_SKB_FRAGS )return -EMSGSIZE ;
@@ -633,17 +632,9 @@ static int zerocopy_fill_skb_from_iter(struct sock *sk, struct sk_buff *skb, length -= copied; - truesize =PAGE_ALIGN (copied + start); skb->data_len += copied; skb->len += copied; - skb->truesize += truesize; -if (sk && sk->sk_type ==SOCK_STREAM ) { -sk_wmem_queued_add (sk, truesize); -if (!skb_zcopy_pure (skb)) -sk_mem_charge (sk, truesize); - }else { -refcount_add (truesize, &skb->sk->sk_wmem_alloc); - } + skb->truesize +=PAGE_ALIGN (copied + start); head =compound_head (pages[n]); order =compound_order (head);
@@ -691,10 +682,24 @@ int __zerocopy_sg_from_iter(struct msghdr *msg, struct sock *sk, struct sk_buff *skb, struct iov_iter *from, size_t length) { + unsigned long orig_size = skb->truesize; +unsigned long truesize; +int ret; +if (msg && msg->msg_ubuf && msg->sg_from_iter)return msg->sg_from_iter (sk, skb, from, length); - else - return zerocopy_fill_skb_from_iter(sk, skb, from, length); + + ret =zerocopy_fill_skb_from_iter (skb, from, length); + truesize = skb->truesize - orig_size; + +if (sk && sk->sk_type ==SOCK_STREAM ) { +sk_wmem_queued_add (sk, truesize); +if (!skb_zcopy_pure (skb)) +sk_mem_charge (sk, truesize); + }else { +refcount_add (truesize, &skb->sk->sk_wmem_alloc); + } +return ret; }EXPORT_SYMBOL (__zerocopy_sg_from_iter);
Signature · 1 line
2.44.0
Re: [PATCH net-next 3/5] net: batch zerocopy_fill_skb_from_iter accounting
Quoted 6 lines · Pavel Begunkov
Instead of accounting every page range against the socket separately, do it in batch based on the change in
skb->truesize. It's also moved into__zerocopy_sg_from_iter(), so thatzerocopy_fill_skb_from_iter()is simpler and responsible for setting frags but not the accounting.Signed-off-by Pavel Begunkov <[email protected]>
Quoted 18 lines
--- net/core/datagram.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-)net/core/datagram.c +1
-1@@ -610,7 +610,7 @@ int skb_copy_datagram_from_iter(struct sk_buff *skb, int offset, }EXPORT_SYMBOL (skb_copy_datagram_from_iter); -static int zerocopy_fill_skb_from_iter (struct sock *sk,struct sk_buff *skb, +static int zerocopy_fill_skb_from_iter (struct sk_buff *skb,struct iov_iter *from,size_t length) {int frag =skb_shinfo (skb)->nr_frags;
net/core/datagram.c static int zerocopy_fill_skb_from_iter(struct sock *sk, struct sk_buff *skb, · line 626
Quoted 7 lines
net/core/datagram.c +0
-1@@ -621,7 +621,6 @@ static int zerocopy_fill_skb_from_iter(struct sock *sk, struct sk_buff *skb,int refs, order, n =0 ;size_t start;ssize_t copied; -unsigned long truesize;if (frag ==MAX_SKB_FRAGS )return -EMSGSIZE ;
Does the existing code then incorrectly not unwind sk_wmem_queued_add and sk_mem_charge if returning with error from the second or later loop..
Quoted 49 lines
net/core/datagram.c +17
-11@@ -633,17 +632,9 @@ static int zerocopy_fill_skb_from_iter(struct sock *sk, struct sk_buff *skb, length -= copied; - truesize =PAGE_ALIGN (copied + start); skb->data_len += copied; skb->len += copied; - skb->truesize += truesize; -if (sk && sk->sk_type ==SOCK_STREAM ) { -sk_wmem_queued_add (sk, truesize); -if (!skb_zcopy_pure (skb)) -sk_mem_charge (sk, truesize); - }else { -refcount_add (truesize, &skb->sk->sk_wmem_alloc); - } + skb->truesize +=PAGE_ALIGN (copied + start); head =compound_head (pages[n]); order =compound_order (head);@@ -691,10 +682,24 @@ int __zerocopy_sg_from_iter(struct msghdr *msg, struct sock *sk, struct sk_buff *skb, struct iov_iter *from, size_t length) { + unsigned long orig_size = skb->truesize; +unsigned long truesize; +int ret; +if (msg && msg->msg_ubuf && msg->sg_from_iter)return msg->sg_from_iter (sk, skb, from, length); - else - return zerocopy_fill_skb_from_iter(sk, skb, from, length); + + ret =zerocopy_fill_skb_from_iter (skb, from, length); + truesize = skb->truesize - orig_size; + +if (sk && sk->sk_type ==SOCK_STREAM ) { +sk_wmem_queued_add (sk, truesize); +if (!skb_zcopy_pure (skb)) +sk_mem_charge (sk, truesize); + }else { +refcount_add (truesize, &skb->sk->sk_wmem_alloc); + } +return ret; }EXPORT_SYMBOL (__zerocopy_sg_from_iter);Signature · 1 line
2.44.0
Re: [PATCH net-next 3/5] net: batch zerocopy_fill_skb_from_iter accounting
Quoted 9 lines · Willem de Bruijn
Quoted 6 lines · Pavel Begunkov
Instead of accounting every page range against the socket separately, do it in batch based on the change in
skb->truesize. It's also moved into__zerocopy_sg_from_iter(), so thatzerocopy_fill_skb_from_iter()is simpler and responsible for setting frags but not the accounting.Signed-off-by Pavel Begunkov <[email protected]>Reviewed-by Willem de Bruijn <[email protected]>
Thanks for reviews!
Quoted 23 lines
Quoted 23 lines
--- net/core/datagram.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-)net/core/datagram.c +1
-2@@ -610,7 +610,7 @@ int skb_copy_datagram_from_iter(struct sk_buff *skb, int offset, }EXPORT_SYMBOL (skb_copy_datagram_from_iter); -static int zerocopy_fill_skb_from_iter (struct sock *sk,struct sk_buff *skb, +static int zerocopy_fill_skb_from_iter (struct sk_buff *skb,struct iov_iter *from,size_t length) {int frag =skb_shinfo (skb)->nr_frags;@@ -621,7 +621,6 @@ static int zerocopy_fill_skb_from_iter(struct sock *sk, struct sk_buff *skb,int refs, order, n =0 ;size_t start;ssize_t copied; -unsigned long truesize;
Quoted 6 lines
net/core/datagram.c static int zerocopy_fill_skb_from_iter(struct sock *sk, struct sk_buff *skb, · line 626
Quoted 2 lines
net/core/datagram.c +0
-0if (frag ==MAX_SKB_FRAGS )return -EMSGSIZE ;Does the existing code then incorrectly not unwind
sk_wmem_queued_addandsk_mem_chargeif returning with error from the second or later loop..
As long as ->truesize matches what's accounted to the socket, kfree_skb() -> sock_wfree()/->destructor() should take care of it. With sk_mem_charge() I assume __zerocopy_sg_from_iter -> ___pskb_trim() should do it, need to look it up, but if not, it sounds like a temporary over estimation until the skb is put down. I don't see anything concerning. Is that the scenario you're worried about?
Signature · 1 line
Pavel Begunkov
Re: [PATCH net-next 3/5] net: batch zerocopy_fill_skb_from_iter accounting
Quoted 43 lines · Pavel Begunkov
Quoted 9 lines · Willem de Bruijn
Quoted 6 lines · Pavel Begunkov
Instead of accounting every page range against the socket separately, do it in batch based on the change in
skb->truesize. It's also moved into__zerocopy_sg_from_iter(), so thatzerocopy_fill_skb_from_iter()is simpler and responsible for setting frags but not the accounting.Signed-off-by Pavel Begunkov <[email protected]>Reviewed-by Willem de Bruijn <[email protected]>Thanks for reviews!
Quoted 23 lines
Quoted 23 lines
--- net/core/datagram.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-)net/core/datagram.c +1
-2@@ -610,7 +610,7 @@ int skb_copy_datagram_from_iter(struct sk_buff *skb, int offset, }EXPORT_SYMBOL (skb_copy_datagram_from_iter); -static int zerocopy_fill_skb_from_iter (struct sock *sk,struct sk_buff *skb, +static int zerocopy_fill_skb_from_iter (struct sk_buff *skb,struct iov_iter *from,size_t length) {int frag =skb_shinfo (skb)->nr_frags;@@ -621,7 +621,6 @@ static int zerocopy_fill_skb_from_iter(struct sock *sk, struct sk_buff *skb,int refs, order, n =0 ;size_t start;ssize_t copied; -unsigned long truesize;Quoted 6 lines
net/core/datagram.c static int zerocopy_fill_skb_from_iter(struct sock *sk, struct sk_buff *skb, · line 626
Quoted 2 lines
net/core/datagram.c +0
-0if (frag ==MAX_SKB_FRAGS )return -EMSGSIZE ;Does the existing code then incorrectly not unwind
sk_wmem_queued_addandsk_mem_chargeif returning with error from the second or later loop..
Quoted 6 lines
As long as ->truesize matches what's accounted to the socket,
kfree_skb()-> sock_wfree()/->destructor() should take care of it. Withsk_mem_charge()I assume__zerocopy_sg_from_iter->___pskb_trim()should do it, need to look it up, but if not, it sounds like a temporary over estimation until the skb is put down. I don't see anything concerning. Is that the scenario you're worried about?
Oh indeed. Thanks. I don't see ___pskb_trim adjusting except for the cases where it calls skb_condese, but neither does it adjust truesize. So indeed a temporary over estimation until e.g., tcp_wmem_free_skb. Sounds fine.
PATCH 4/5 io_uring/net: move charging socket out of zc io_uring
In reply to Pavel BegunkovCurrently, io_uring's io_sg_from_iter() duplicates the part of __zerocopy_sg_from_iter() charging pages to the socket. It'd be too easy to miss while changing it in net/, the chunk is not the most straightforward for outside users and full of internal implementation details. io_uring is not a good place to keep it, deduplicate it by moving out of the callback into __zerocopy_sg_from_iter().
--- include/linux/skbuff.h | 3 +++ include/linux/socket.h | 2 +- io_uring/net.c | 16 ++++------------ net/core/datagram.c | 10 +++++----- 4 files changed, 13 insertions(+), 18 deletions(-)
include/linux/skbuff.h +3 -0
@@ -1703,6 +1703,9 @@ int __zerocopy_sg_from_iter(struct msghdr *msg, struct sock *sk, struct sk_buff *skb, struct iov_iter *from, size_t length); +int zerocopy_fill_skb_from_iter (struct sk_buff *skb, +struct iov_iter *from,size_t length); +static inline int skb_zerocopy_iter_dgram (struct sk_buff *skb,struct msghdr *msg,int len) {
include/linux/socket.h +1 -1
@@ -76,7 +76,7 @@ struct msghdr {__kernel_size_t msg_controllen;/* ancillary data buffer length */ struct kiocb *msg_iocb;/* ptr to iocb for async requests */ struct ubuf_info *msg_ubuf; -int (*sg_from_iter)(struct sock *sk,struct sk_buff *skb, +int (*sg_from_iter)(struct sk_buff *skb,struct iov_iter *from,size_t length); };
io_uring/net.c +4 -12
@@ -1265,14 +1265,14 @@ int io_send_zc_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)return io_sendmsg_prep_setup (req, req->opcode ==IORING_OP_SENDMSG_ZC ); } -static int io_sg_from_iter_iovec (struct sock *sk,struct sk_buff *skb, +static int io_sg_from_iter_iovec (struct sk_buff *skb,struct iov_iter *from,size_t length) {skb_zcopy_downgrade_managed (skb); -return __zerocopy_sg_from_iter (NULL , sk, skb, from, length); +return zerocopy_fill_skb_from_iter (skb, from, length); } -static int io_sg_from_iter (struct sock *sk,struct sk_buff *skb, +static int io_sg_from_iter (struct sk_buff *skb,struct iov_iter *from,size_t length) {struct skb_shared_info *shinfo =skb_shinfo (skb);
@@ -1285,7 +1285,7 @@ static int io_sg_from_iter(struct sock *sk, struct sk_buff *skb,if (!frag) shinfo->flags |=SKBFL_MANAGED_FRAG_REFS ;else if (unlikely (!skb_zcopy_managed (skb))) -return __zerocopy_sg_from_iter (NULL , sk, skb, from, length); +return zerocopy_fill_skb_from_iter (skb, from, length); bi.bi_size =min (from->count, length); bi.bi_bvec_done = from->iov_offset;
@@ -1312,14 +1312,6 @@ static int io_sg_from_iter(struct sock *sk, struct sk_buff *skb, skb->data_len += copied; skb->len += copied; skb->truesize += truesize; - -if (sk && sk->sk_type ==SOCK_STREAM ) { -sk_wmem_queued_add (sk, truesize); -if (!skb_zcopy_pure (skb)) -sk_mem_charge (sk, truesize); - }else { -refcount_add (truesize, &skb->sk->sk_wmem_alloc); - }return ret; }
net/core/datagram.c +5 -5
@@ -610,8 +610,8 @@ int skb_copy_datagram_from_iter(struct sk_buff *skb, int offset, }EXPORT_SYMBOL (skb_copy_datagram_from_iter); -static int zerocopy_fill_skb_from_iter (struct sk_buff *skb, -struct iov_iter *from,size_t length) +int zerocopy_fill_skb_from_iter (struct sk_buff *skb, +struct iov_iter *from,size_t length) {int frag =skb_shinfo (skb)->nr_frags;
@@ -687,11 +687,11 @@ int __zerocopy_sg_from_iter(struct msghdr *msg, struct sock *sk,int ret;if (msg && msg->msg_ubuf && msg->sg_from_iter) -return msg->sg_from_iter (sk, skb, from, length); + ret = msg->sg_from_iter (skb, from, length); +else + ret =zerocopy_fill_skb_from_iter (skb, from, length); - ret =zerocopy_fill_skb_from_iter (skb, from, length); truesize = skb->truesize - orig_size; -if (sk && sk->sk_type ==SOCK_STREAM ) {sk_wmem_queued_add (sk, truesize);if (!skb_zcopy_pure (skb))
Signature · 1 line
2.44.0
Re: [PATCH net-next 4/5] io_uring/net: move charging socket out of zc io_uring
Quoted 8 lines · Pavel Begunkov
Currently, io_uring's
io_sg_from_iter()duplicates the part of__zerocopy_sg_from_iter()charging pages to the socket. It'd be too easy to miss while changing it innet/, the chunk is not the most straightforward for outside users and full of internal implementation details.io_uringis not a good place to keep it, deduplicate it by moving out of the callback into__zerocopy_sg_from_iter().Signed-off-by Pavel Begunkov <[email protected]>
PATCH 5/5 net: limit scope of a skb_zerocopy_iter_stream var
In reply to Pavel Begunkovskb_zerocopy_iter_stream() only uses @orig_uarg in the !link_skb path, and we can move the local variable in the appropriate block.
--- net/core/skbuff.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/core/skbuff.c +2 -1
@@ -1856,7 +1856,6 @@ int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb, struct msghdr *msg, int len, struct ubuf_info *uarg) { - struct ubuf_info *orig_uarg = skb_zcopy(skb); int err, orig_len = skb->len;if (uarg->ops->link_skb) {
@@ -1864,6 +1863,8 @@ int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb,if (err)return err; }else { +struct ubuf_info *orig_uarg =skb_zcopy (skb); +/* An skb can only point to one uarg. This edge case happens * when TCP appends to an skb, but zerocopy_realloc triggered * a new alloc.
Signature · 1 line
2.44.0
Re: [PATCH net-next 5/5] net: limit scope of a skb_zerocopy_iter_stream var
Quoted 4 lines · Pavel Begunkov
skb_zerocopy_iter_stream()only uses @orig_uarg in the !link_skb path, and we can move the local variable in the appropriate block.Signed-off-by Pavel Begunkov <[email protected]>
Quoted 3 lines · Pavel Begunkov
Assorted zerocopy send path cleanups, the main part of which is moving some net stack specific accounting out of
io_uringback tonet/in Patch 4.
Looks good to me:
Signature · 1 line
Jens Axboe
This series was applied to netdev/net-next.git (main) by Paolo Abeni <[email protected]>:
Hello:
This series was applied to netdev/net-next.git (main) by Paolo Abeni <[email protected]>:
Quoted 4 lines · On Thu, 27 Jun 2024 13:59:40 +0100 you wrote:
Assorted zerocopy send path cleanups, the main part of which is moving some net stack specific accounting out of
io_uringback tonet/in Patch 4.
Quoted 8 lines
Pavel Begunkov (5):
net: always try to set ubuf inskb_zerocopy_iter_stream
net: split__zerocopy_sg_from_iter()
net: batchzerocopy_fill_skb_from_iteraccountingio_uring/net: move charging socket out of zcio_uring
net: limit scope of askb_zerocopy_iter_streamvar[...]
Here is the summary with links:
- [net-next,1/5] net: always try to set ubuf in skb_zerocopy_iter_stream
https://git.kernel.org/netdev/net-next/c/9e2db9d3993e - [net-next,2/5] net: split __zerocopy_sg_from_iter() https://git.kernel.org/netdev/net-next/c/7fb05423fed4 - [net-next,3/5] net: batch zerocopy_fill_skb_from_iter accounting https://git.kernel.org/netdev/net-next/c/aeb320fc05c7 - [net-next,4/5] io_uring/net: move charging socket out of zc io_uring https://git.kernel.org/netdev/net-next/c/060f4ba6e403 - [net-next,5/5] net: limit scope of a skb_zerocopy_iter_stream var https://git.kernel.org/netdev/net-next/c/2ca58ed21cef
You are awesome, thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html