implement io_uring notification (ubuf_info) stacking
Subsystems (5)
cover implement io_uring notification (ubuf_info) stacking
io_uring allocates a ubuf_info per zerocopy send request, it's convenient for the userspace but with how things are it means that every time the TCP stack has to allocate a new skb instead of amending into a previous one. Unless sends are big enough, there will be lots of small skbs straining the stack and dipping performance.
The patchset implements notification, i.e. an io_uring's ubuf_info extension, stacking. It tries to link ubuf_info's into a list, and the entire link will be put down together once all references are gone.
Testing with liburing/examples/send-zerocopy and another custom made tool, with 4K bytes per send it improves performance ~6 times and levels it with MSG_ZEROCOPY. Without the patchset it requires much larger sends to utilise all potential.
bytes | before | after (Kqps) 100 | 283 | 936 1200 | 195 | 1023 4000 | 193 | 1386 8000 | 154 | 1058
Pavel Begunkov (6):
net: extend ubuf_info callback to ops structure
net: add callback for setting a ubuf_info to skb io_uring/notif: refactor io_tx_ubuf_complete() io_uring/notif: remove ctx var from io_notif_tw_complete io_uring/notif: simplify io_notif_flush() io_uring/notif: implement notification stacking
drivers/net/tap.c | 2 +- drivers/net/tun.c | 2 +- drivers/vhost/net.c | 8 +++- include/linux/skbuff.h | 21 ++++++---- io_uring/notif.c | 91 +++++++++++++++++++++++++++++++++++------- io_uring/notif.h | 13 +++--- net/core/skbuff.c | 37 +++++++++++------ 7 files changed, 129 insertions(+), 45 deletions(-)
Signature · 1 line
2.44.0
RFC 1/6 net: extend ubuf_info callback to ops structure
We'll need to associate additional callbacks with ubuf_info, introduce a structure holding ubuf_info callbacks. Apart from a more smarter io_uring notification management introduced in next patches, it can be used to generalise msg_zerocopy_put_abort() and also store ->sg_from_iter, which is currently passed in struct msghdr.
--- drivers/net/tap.c | 2 +- drivers/net/tun.c | 2 +- drivers/vhost/net.c | 8 ++++++-- include/linux/skbuff.h | 19 +++++++++++-------- io_uring/notif.c | 8 ++++++-- net/core/skbuff.c | 17 +++++++++++------ 6 files changed, 36 insertions(+), 20 deletions(-)
drivers/net/tap.c +1 -1
@@ -754,7 +754,7 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control,skb_zcopy_init (skb, msg_control); }else if (msg_control) {struct ubuf_info *uarg = msg_control; - uarg->callback (NULL , uarg, false); + uarg->ops->complete (NULL , uarg, false); }dev_queue_xmit (skb);
drivers/net/tun.c +1 -1
@@ -1906,7 +1906,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,skb_zcopy_init (skb, msg_control); }else if (msg_control) {struct ubuf_info *uarg = msg_control; - uarg->callback (NULL , uarg, false); + uarg->ops->complete (NULL , uarg, false); }skb_reset_network_header (skb);
drivers/vhost/net.c +6 -2
@@ -380,7 +380,7 @@ static void vhost_zerocopy_signal_used(struct vhost_net *net, } } -static void vhost_zerocopy_callback (struct sk_buff *skb, +static void vhost_zerocopy_complete (struct sk_buff *skb,struct ubuf_info *ubuf_base,bool success) {struct ubuf_info_msgzc *ubuf =uarg_to_msgzc (ubuf_base);
@@ -408,6 +408,10 @@ static void vhost_zerocopy_callback(struct sk_buff *skb,rcu_read_unlock_bh (); } +static const struct ubuf_info_ops vhost_ubuf_ops = { + .complete = vhost_zerocopy_complete, +}; +static inline unsigned long busy_clock (void ) {return local_clock () >>10 ;
@@ -879,7 +883,7 @@ static void handle_tx_zerocopy(struct vhost_net *net, struct socket *sock) vq->heads[nvq->upend_idx].len =VHOST_DMA_IN_PROGRESS ; ubuf->ctx = nvq->ubufs; ubuf->desc = nvq->upend_idx; - ubuf->ubuf.callback = vhost_zerocopy_callback; + ubuf->ubuf.ops = &vhost_ubuf_ops; ubuf->ubuf.flags =SKBFL_ZEROCOPY_FRAG ;refcount_set (&ubuf->ubuf.refcnt,1 ); msg.msg_control = &ctl;
include/linux/skbuff.h +11 -8
@@ -527,6 +527,11 @@ enum {#define SKBFL_ALL_ZEROCOPY (SKBFL_ZEROCOPY_FRAG | SKBFL_PURE_ZEROCOPY | \ SKBFL_DONT_ORPHAN | SKBFL_MANAGED_FRAG_REFS) +struct ubuf_info_ops { +void (*complete)(struct sk_buff *,struct ubuf_info *, +bool zerocopy_success); +}; +/* * The callback notifies userspace to release buffers when skb DMA is done in * lower device, the skb last reference should be 0 when calling this.
@@ -536,8 +541,7 @@ enum {* The desc field is used to track userspace buffer index. */ struct ubuf_info { -void (*callback)(struct sk_buff *,struct ubuf_info *, -bool zerocopy_success); +const struct ubuf_info_ops *ops;refcount_t refcnt;u8 flags; };
@@ -1662,14 +1666,13 @@ static inline void skb_set_end_offset(struct sk_buff *skb, unsigned int offset) }#endif +extern const struct ubuf_info_ops msg_zerocopy_ubuf_ops; +struct ubuf_info *msg_zerocopy_realloc (struct sock *sk,size_t size,struct ubuf_info *uarg);void msg_zerocopy_put_abort (struct ubuf_info *uarg,bool have_uref); -void msg_zerocopy_callback (struct sk_buff *skb,struct ubuf_info *uarg, -bool success); -int __zerocopy_sg_from_iter (struct msghdr *msg,struct sock *sk,struct sk_buff *skb,struct iov_iter *from,size_t length);
@@ -1757,13 +1760,13 @@ static inline void *skb_zcopy_get_nouarg(struct sk_buff *skb)static inline void net_zcopy_put (struct ubuf_info *uarg) {if (uarg) - uarg->callback (NULL , uarg, true); + uarg->ops->complete (NULL , uarg, true); }static inline void net_zcopy_put_abort (struct ubuf_info *uarg,bool have_uref) {if (uarg) { -if (uarg->callback == msg_zerocopy_callback) +if (uarg->ops == &msg_zerocopy_ubuf_ops)msg_zerocopy_put_abort (uarg, have_uref);else if (have_uref)net_zcopy_put (uarg);
@@ -1777,7 +1780,7 @@ static inline void skb_zcopy_clear(struct sk_buff *skb, bool zerocopy_success)if (uarg) {if (!skb_zcopy_is_nouarg (skb)) - uarg->callback (skb, uarg, zerocopy_success); + uarg->ops->complete (skb, uarg, zerocopy_success);skb_shinfo (skb)->flags &= ~SKBFL_ALL_ZEROCOPY ; }
io_uring/notif.c +6 -2
@@ -24,7 +24,7 @@ void io_notif_tw_complete(struct io_kiocb *notif, struct io_tw_state *ts)io_req_task_complete (notif, ts); } -static void io_tx_ubuf_callback (struct sk_buff *skb,struct ubuf_info *uarg, +static void io_tx_ubuf_complete (struct sk_buff *skb,struct ubuf_info *uarg,bool success) { struct io_notif_data *nd = container_of(uarg, struct io_notif_data, uarg);
@@ -43,6 +43,10 @@ static void io_tx_ubuf_callback(struct sk_buff *skb, struct ubuf_info *uarg, } } +static const struct ubuf_info_ops io_ubuf_ops = { + .complete = io_tx_ubuf_complete, +}; +struct io_kiocb *io_alloc_notif (struct io_ring_ctx *ctx) __must_hold(&ctx->uring_lock) {
@@ -62,7 +66,7 @@ struct io_kiocb *io_alloc_notif(struct io_ring_ctx *ctx) nd->zc_report = false; nd->account_pages =0 ; nd->uarg.flags =IO_NOTIF_UBUF_FLAGS ; - nd->uarg.callback = io_tx_ubuf_callback; + nd->uarg.ops = &io_ubuf_ops;refcount_set (&nd->uarg.refcnt,1 );return notif; }
net/core/skbuff.c +11 -6
@@ -1708,7 +1708,7 @@ static struct ubuf_info *msg_zerocopy_alloc(struct sock *sk, size_t size)return NULL ; } - uarg->ubuf.callback = msg_zerocopy_callback; + uarg->ubuf.ops = &msg_zerocopy_ubuf_ops; uarg->id = ((u32 )atomic_inc_return (&sk->sk_zckey)) -1 ; uarg->len =1 ; uarg->bytelen = size;
@@ -1734,7 +1734,7 @@ struct ubuf_info *msg_zerocopy_realloc(struct sock *sk, size_t size,u32 bytelen, next;/* there might be non MSG_ZEROCOPY users */ -if (uarg->callback != msg_zerocopy_callback) +if (uarg->ops != &msg_zerocopy_ubuf_ops)return NULL ;/* realloc only when socket is locked (TCP, UDP cork),
@@ -1845,8 +1845,8 @@ static void __msg_zerocopy_callback(struct ubuf_info_msgzc *uarg)sock_put (sk); } -void msg_zerocopy_callback (struct sk_buff *skb,struct ubuf_info *uarg, -bool success) +static void msg_zerocopy_complete (struct sk_buff *skb,struct ubuf_info *uarg, +bool success) {struct ubuf_info_msgzc *uarg_zc =uarg_to_msgzc (uarg);
@@ -1855,7 +1855,7 @@ void msg_zerocopy_callback(struct sk_buff *skb, struct ubuf_info *uarg,if (refcount_dec_and_test (&uarg->refcnt))__msg_zerocopy_callback (uarg_zc); } -EXPORT_SYMBOL_GPL (msg_zerocopy_callback); +void msg_zerocopy_put_abort (struct ubuf_info *uarg,bool have_uref) {
@@ -1865,10 +1865,15 @@ void msg_zerocopy_put_abort(struct ubuf_info *uarg, bool have_uref)uarg_to_msgzc (uarg)->len--;if (have_uref) -msg_zerocopy_callback (NULL , uarg, true); +msg_zerocopy_complete (NULL , uarg, true); }EXPORT_SYMBOL_GPL (msg_zerocopy_put_abort); +const struct ubuf_info_ops msg_zerocopy_ubuf_ops = { + .complete = msg_zerocopy_complete, +}; +EXPORT_SYMBOL_GPL (msg_zerocopy_ubuf_ops); +int skb_zerocopy_iter_stream (struct sock *sk,struct sk_buff *skb,struct msghdr *msg,int len,struct ubuf_info *uarg)
Signature · 1 line
2.44.0
Re: [RFC 1/6] net: extend ubuf_info callback to ops structure
Quoted 7 lines · Pavel Begunkov
We'll need to associate additional callbacks with
ubuf_info, introduce a structure holdingubuf_infocallbacks. Apart from a more smarterio_uringnotification management introduced in next patches, it can be used to generalisemsg_zerocopy_put_abort()and also store ->sg_from_iter, which is currently passed in struct msghdr.Signed-off-by Pavel Begunkov <[email protected]>
Quoted 9 lines
--- drivers/net/tap.c | 2 +- drivers/net/tun.c | 2 +- drivers/vhost/net.c | 8 ++++++-- include/linux/skbuff.h | 19 +++++++++++-------- io_uring/notif.c | 8 ++++++-- net/core/skbuff.c | 17 +++++++++++------ 6 files changed, 36 insertions(+), 20 deletions(-)
Willem de Bruijn
maintainerRe: [RFC 1/6] net: extend ubuf_info callback to ops structure
In reply to Pavel BegunkovQuoted 5 lines · Pavel Begunkov
We'll need to associate additional callbacks with
ubuf_info, introduce a structure holdingubuf_infocallbacks. Apart from a more smarterio_uringnotification management introduced in next patches, it can be used to generalisemsg_zerocopy_put_abort()and also store ->sg_from_iter, which is currently passed in struct msghdr.
This adds an extra indirection for all other ubuf implementations. Can that be avoided?
Re: [RFC 1/6] net: extend ubuf_info callback to ops structure
Quoted 9 lines · Willem de Bruijn
Quoted 5 lines · Pavel Begunkov
We'll need to associate additional callbacks with
ubuf_info, introduce a structure holdingubuf_infocallbacks. Apart from a more smarterio_uringnotification management introduced in next patches, it can be used to generalisemsg_zerocopy_put_abort()and also store ->sg_from_iter, which is currently passed in struct msghdr.This adds an extra indirection for all other ubuf implementations. Can that be avoided?
It could be fitted directly into ubuf_info, but that doesn't feel right. It should be hot, so does it even matter? On the bright side, with the patch I'll also ->sg_from_iter from msghdr into it, so it doesn't have to be in the generic path.
I think it's the right approach, but if you have a strong opinion I can fit it as a new field in ubuf_info.
Signature · 1 line
Pavel Begunkov
Willem de Bruijn
maintainerRe: [RFC 1/6] net: extend ubuf_info callback to ops structure
Quoted 8 lines · Pavel Begunkov
Quoted 7 lines · Willem de Bruijn
Quoted 5 lines · Pavel Begunkov
We'll need to associate additional callbacks with
ubuf_info, introduce a structure holdingubuf_infocallbacks. Apart from a more smarterio_uringnotification management introduced in next patches, it can be used to generalisemsg_zerocopy_put_abort()and also store ->sg_from_iter, which is currently passed in struct msghdr.
Quoted 5 lines
Quoted 2 lines
This adds an extra indirection for all other ubuf implementations. Can that be avoided?
It could be fitted directly into
ubuf_info, but that doesn't feel right. It should be hot, so does it even matter?
That depends on the workload (working set size)?
Quoted 3 lines
On the bright side,
with the patch I'll also ->sg_from_iter from msghdr into it, so it doesn't have to be in the generic path.
I don't follow this: is this suggested future work?
Quoted 3 lines
I think it's the right approach, but if you have a strong opinion I can fit it as a new field in
ubuf_info.
If there is a significant cost, I suppose we could use INDIRECT_CALL or go one step further and demultiplex based on the new ops
if (uarg->ops == &msg_zerocopy_ubuf_ops)
msg_zerocopy_callback(..);Re: [RFC 1/6] net: extend ubuf_info callback to ops structure
Quoted 15 lines · Willem de Bruijn
Quoted 8 lines · Pavel Begunkov
Quoted 7 lines · Willem de Bruijn
Quoted 5 lines · Pavel Begunkov
We'll need to associate additional callbacks with
ubuf_info, introduce a structure holdingubuf_infocallbacks. Apart from a more smarterio_uringnotification management introduced in next patches, it can be used to generalisemsg_zerocopy_put_abort()and also store ->sg_from_iter, which is currently passed in struct msghdr.Quoted 5 lines
Quoted 2 lines
This adds an extra indirection for all other ubuf implementations. Can that be avoided?
It could be fitted directly into
ubuf_info, but that doesn't feel right. It should be hot, so does it even matter?
Quoted 6 lines
That depends on the workload (working set size)?
Quoted 3 lines
Quoted 1 line
On the bright side,
with the patch I'll also ->sg_from_iter from msghdr into it, so it doesn't have to be in the generic path.
I don't follow this: is this suggested future work?
Right, a small change I will add later. Without ops though having 3 callback fields in uargs would be out of hands.
Quoted 9 lines
Quoted 2 lines
I think it's the right approach, but if you have a strong opinion I can fit it as a new field in
ubuf_info.If there is a significant cost, I suppose we could use
INDIRECT_CALLor go one step further and demultiplex based on the new opsif (uarg->ops == &msg_zerocopy_ubuf_ops) msg_zerocopy_callback(..);
Let me note that the patch doesn't change the number of indirect calls but only adds one extra deref to get the callback, i.e. uarg->ops->callback() instead of uarg->callback(). Your snippet goes an extra mile and removes the indirect call.
Can I take it as that you're fine with the direction of the patch? Or do you want me to change anything?
Signature · 1 line
Pavel Begunkov
Willem de Bruijn
maintainerRe: [RFC 1/6] net: extend ubuf_info callback to ops structure
Quoted 33 lines · Pavel Begunkov
Quoted 15 lines · Willem de Bruijn
Quoted 8 lines · Pavel Begunkov
Quoted 7 lines · Willem de Bruijn
Quoted 5 lines · Pavel Begunkov
We'll need to associate additional callbacks with
ubuf_info, introduce a structure holdingubuf_infocallbacks. Apart from a more smarterio_uringnotification management introduced in next patches, it can be used to generalisemsg_zerocopy_put_abort()and also store ->sg_from_iter, which is currently passed in struct msghdr.Quoted 5 lines
Quoted 2 lines
This adds an extra indirection for all other ubuf implementations. Can that be avoided?
It could be fitted directly into
ubuf_info, but that doesn't feel right. It should be hot, so does it even matter?Quoted 6 lines
That depends on the workload (working set size)?
Quoted 3 lines
Quoted 1 line
On the bright side,
with the patch I'll also ->sg_from_iter from msghdr into it, so it doesn't have to be in the generic path.
I don't follow this: is this suggested future work?
Right, a small change I will add later. Without ops though having 3 callback fields in uargs would be out of hands.
Quoted 7 lines
Quoted 2 lines
I think it's the right approach, but if you have a strong opinion I can fit it as a new field in
ubuf_info.If there is a significant cost, I suppose we could use
INDIRECT_CALLor go one step further and demultiplex based on the new ops
Quoted 6 lines
Quoted 2 lines
if (uarg->ops == &msg_zerocopy_ubuf_ops) msg_zerocopy_callback(..);Let me note that the patch doesn't change the number of indirect calls but only adds one extra deref to get the callback, i.e.
uarg->ops->callback()instead ofuarg->callback().
Of course. Didn't mean to imply otherwise.
Quoted 5 lines
Your snippet
goes an extra mile and removes the indirect call.Can I take it as that you're fine with the direction of the patch? Or do you want me to change anything?
It's fine. I want to avoid new paths slowing down existing code where possible. But if this extra deref would prove significant we have a workaround.
Re: [RFC 1/6] net: extend ubuf_info callback to ops structure
In reply to Pavel BegunkovQuoted 3 lines · Pavel Begunkov
On the bright side,
with the patch I'll also ->sg_from_iter from msghdr into it, so it doesn't have to be in the generic path.
So, what's old is new again? That's where it started:
https://lore.kernel.org/netdev/20220628225204.GA27554@u2004-local/
Re: [RFC 1/6] net: extend ubuf_info callback to ops structure
Quoted 8 lines · David Ahern
Quoted 3 lines · Pavel Begunkov
On the bright side,
with the patch I'll also ->sg_from_iter from msghdr into it, so it doesn't have to be in the generic path.So, what's old is new again? That's where it started:
https://lore.kernel.org/netdev/20220628225204.GA27554@u2004-local/
Hah, indeed, your patch had it in uarg. I wonder why I didn't put them all in a table back then, if the argument was to keep struct ubuf_info leaner.
Signature · 1 line
Pavel Begunkov
RFC 2/6 net: add callback for setting a ubuf_info to skb
In reply to Pavel BegunkovAt the moment an skb can only have one ubuf_info associated with it, which might be a performance problem for zerocopy sends in cases like TCP via io_uring. Add a callback for assigning ubuf_info to skb, this way we will implement smarter assignment later like linking ubuf_info together.
Note, it's an optional callback, which should be compatible with skb_zcopy_set(), that's because the net stack might potentially decide to clone an skb and take another reference to ubuf_info whenever it wishes. Also, a correct implementation should always be able to bind to an skb without prior ubuf_info, otherwise we could end up in a situation when the send would not be able to progress.
--- include/linux/skbuff.h | 2 ++ net/core/skbuff.c | 20 ++++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-)
include/linux/skbuff.h +2 -0
@@ -530,6 +530,8 @@ enum {struct ubuf_info_ops {void (*complete)(struct sk_buff *,struct ubuf_info *,bool zerocopy_success); +/* has to be compatible with skb_zcopy_set() */ +int (*link_skb)(struct sk_buff *skb,struct ubuf_info *uarg); };/*
net/core/skbuff.c +14 -6
@@ -1881,11 +1881,18 @@ int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb,struct ubuf_info *orig_uarg =skb_zcopy (skb);int err, orig_len = skb->len; -/* 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. -*/ -if (orig_uarg && uarg != orig_uarg) -return -EEXIST ; +if (uarg->ops->link_skb) { + err = uarg->ops->link_skb (skb, uarg); +if (err) +return err; + }else { +/* 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. +*/ +if (orig_uarg && uarg != orig_uarg) +return -EEXIST ; + } err =__zerocopy_sg_from_iter (msg, sk, skb, &msg->msg_iter, len);if (err == -EFAULT || (err == -EMSGSIZE && skb->len == orig_len)) {
@@ -1899,7 +1906,8 @@ int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb,return err; } -skb_zcopy_set (skb, uarg,NULL ); +if (!uarg->ops->link_skb) +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: [RFC 2/6] net: add callback for setting a ubuf_info to skb
Quoted 13 lines · Pavel Begunkov
At the moment an skb can only have one
ubuf_infoassociated with it, which might be a performance problem for zerocopy sends in cases like TCP viaio_uring. Add a callback for assigningubuf_infoto skb, this way we will implement smarter assignment later like linkingubuf_infotogether.Note, it's an optional callback, which should be compatible with
skb_zcopy_set(), that's because the net stack might potentially decide to clone an skb and take another reference toubuf_infowhenever it wishes. Also, a correct implementation should always be able to bind to an skb without priorubuf_info, otherwise we could end up in a situation when the send would not be able to progress.
Quoted 6 lines
Signed-off-by Pavel Begunkov <[email protected]>--- include/linux/skbuff.h | 2 ++ net/core/skbuff.c | 20 ++++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-)
RFC 3/6 io_uring/notif: refactor io_tx_ubuf_complete()
In reply to Pavel BegunkovFlip the dec_and_test if, so when we add more stuff later there is less churn.
--- io_uring/notif.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
io_uring/notif.c +5 -4
@@ -37,10 +37,11 @@ static void io_tx_ubuf_complete(struct sk_buff *skb, struct ubuf_info *uarg,WRITE_ONCE (nd->zc_copied, true); } -if (refcount_dec_and_test (&uarg->refcnt)) { - notif->io_task_work.func = io_notif_tw_complete; -__io_req_task_work_add (notif,IOU_F_TWQ_LAZY_WAKE ); - } +if (!refcount_dec_and_test (&uarg->refcnt)) +return ; + + notif->io_task_work.func = io_notif_tw_complete; +__io_req_task_work_add (notif,IOU_F_TWQ_LAZY_WAKE ); }static const struct ubuf_info_ops io_ubuf_ops = {
Signature · 1 line
2.44.0
RFC 4/6 io_uring/notif: remove ctx var from io_notif_tw_complete
In reply to Pavel BegunkovWe don't need ctx in the hottest path, i.e. registered buffers, let's get it only when we need it.
--- io_uring/notif.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
io_uring/notif.c +2 -3
@@ -12,13 +12,12 @@void io_notif_tw_complete (struct io_kiocb *notif,struct io_tw_state *ts) {struct io_notif_data *nd =io_notif_to_data (notif); -struct io_ring_ctx *ctx = notif->ctx;if (unlikely (nd->zc_report) && (nd->zc_copied || !nd->zc_used)) notif->cqe.res |=IORING_NOTIF_USAGE_ZC_COPIED ; -if (nd->account_pages && ctx->user) { -__io_unaccount_mem (ctx->user, nd->account_pages); +if (nd->account_pages && notif->ctx->user) { +__io_unaccount_mem (notif->ctx->user, nd->account_pages); nd->account_pages =0 ; }io_req_task_complete (notif, ts);
Signature · 1 line
2.44.0
RFC 5/6 io_uring/notif: simplify io_notif_flush()
In reply to Pavel Begunkovio_notif_flush() is partially duplicating io_tx_ubuf_complete(), so instead of duplicating it, make the flush call io_tx_ubuf_complete.
--- io_uring/notif.c | 6 +++--- io_uring/notif.h | 9 +++------ 2 files changed, 6 insertions(+), 9 deletions(-)
io_uring/notif.c +3 -3
@@ -9,7 +9,7 @@#include "notif.h" #include "rsrc.h" -void io_notif_tw_complete (struct io_kiocb *notif,struct io_tw_state *ts) +static void io_notif_tw_complete (struct io_kiocb *notif,struct io_tw_state *ts) {struct io_notif_data *nd =io_notif_to_data (notif);
@@ -23,8 +23,8 @@ void io_notif_tw_complete(struct io_kiocb *notif, struct io_tw_state *ts)io_req_task_complete (notif, ts); } -static void io_tx_ubuf_complete (struct sk_buff *skb,struct ubuf_info *uarg, -bool success) +void io_tx_ubuf_complete (struct sk_buff *skb,struct ubuf_info *uarg, +bool success) { struct io_notif_data *nd = container_of(uarg, struct io_notif_data, uarg);struct io_kiocb *notif =cmd_to_io_kiocb (nd);
io_uring/notif.h +3 -6
@@ -20,7 +20,8 @@ struct io_notif_data { };struct io_kiocb *io_alloc_notif (struct io_ring_ctx *ctx); -void io_notif_tw_complete (struct io_kiocb *notif,struct io_tw_state *ts); +void io_tx_ubuf_complete (struct sk_buff *skb,struct ubuf_info *uarg, +bool success);static inline struct io_notif_data *io_notif_to_data (struct io_kiocb *notif) {
@@ -32,11 +33,7 @@ static inline void io_notif_flush(struct io_kiocb *notif) {struct io_notif_data *nd =io_notif_to_data (notif); -/* drop slot's master ref */ -if (refcount_dec_and_test (&nd->uarg.refcnt)) { - notif->io_task_work.func = io_notif_tw_complete; -__io_req_task_work_add (notif,IOU_F_TWQ_LAZY_WAKE ); - } +io_tx_ubuf_complete (NULL , &nd->uarg, true); }static inline int io_notif_account_mem (struct io_kiocb *notif,unsigned len)
Signature · 1 line
2.44.0
RFC 6/6 io_uring/notif: implement notification stacking
In reply to Pavel BegunkovThe network stack allows only one ubuf_info per skb, and unlike MSG_ZEROCOPY, each io_uring zerocopy send will carry a separate ubuf_info. That means that send requests can't reuse a previosly allocated skb and need to get one more or more of new ones. That's fine for large sends, but otherwise it would spam the stack with lots of skbs carrying just a little data each.
To help with that implement linking notification (i.e. an io_uring wrapper around ubuf_info) into a list. Each is refcounted by skbs and the stack as usual. additionally all non head entries keep a reference to the head, which they put down when their refcount hits 0. When the head have no more users, it'll efficiently put all notifications in a batch.
As mentioned previously about ->io_link_skb, the callback implementation always allows to bind to an skb without a ubuf_info.
--- io_uring/notif.c | 71 +++++++++++++++++++++++++++++++++++++++++++----- io_uring/notif.h | 4 +++ 2 files changed, 68 insertions(+), 7 deletions(-)
io_uring/notif.c +64 -7
@@ -9,18 +9,28 @@#include "notif.h" #include "rsrc.h" +static const struct ubuf_info_ops io_ubuf_ops; +static void io_notif_tw_complete (struct io_kiocb *notif,struct io_tw_state *ts) {struct io_notif_data *nd =io_notif_to_data (notif); -if (unlikely (nd->zc_report) && (nd->zc_copied || !nd->zc_used)) - notif->cqe.res |=IORING_NOTIF_USAGE_ZC_COPIED ; +do { + notif =cmd_to_io_kiocb (nd); -if (nd->account_pages && notif->ctx->user) { -__io_unaccount_mem (notif->ctx->user, nd->account_pages); - nd->account_pages =0 ; - } -io_req_task_complete (notif, ts); +lockdep_assert (refcount_read (&nd->uarg.refcnt) ==0 ); + +if (unlikely (nd->zc_report) && (nd->zc_copied || !nd->zc_used)) + notif->cqe.res |=IORING_NOTIF_USAGE_ZC_COPIED ; + +if (nd->account_pages && notif->ctx->user) { +__io_unaccount_mem (notif->ctx->user, nd->account_pages); + nd->account_pages =0 ; + } + + nd = nd->next; +io_req_task_complete (notif, ts); + }while (nd); } void io_tx_ubuf_complete(struct sk_buff *skb, struct ubuf_info *uarg,
@@ -39,12 +49,56 @@ void io_tx_ubuf_complete(struct sk_buff *skb, struct ubuf_info *uarg,if (!refcount_dec_and_test (&uarg->refcnt))return ; +if (nd->head != nd) { +io_tx_ubuf_complete (skb, &nd->head->uarg, success); +return ; + } notif->io_task_work.func = io_notif_tw_complete;__io_req_task_work_add (notif,IOU_F_TWQ_LAZY_WAKE ); } +static int io_link_skb (struct sk_buff *skb,struct ubuf_info *uarg) +{ +struct io_notif_data *nd, *prev_nd; +struct io_kiocb *prev_notif, *notif; +struct ubuf_info *prev_uarg =skb_zcopy (skb); + + nd = container_of(uarg, struct io_notif_data, uarg); + notif =cmd_to_io_kiocb (nd); + +if (!prev_uarg) { +net_zcopy_get (&nd->uarg); +skb_zcopy_init (skb, &nd->uarg); +return 0 ; + } +/* handle it separately as we can't link a notif to itself */ +if (unlikely (prev_uarg == &nd->uarg)) +return 0 ; +/* we can't join two links together, just request a fresh skb */ +if (unlikely (nd->head != nd || nd->next)) +return -EEXIST ; +/* don't mix zc providers */ +if (unlikely (prev_uarg->ops != &io_ubuf_ops)) +return -EEXIST ; + + prev_nd = container_of(prev_uarg, struct io_notif_data, uarg); + prev_notif =cmd_to_io_kiocb (nd); + +/* make sure all noifications can be finished in the same task_work */ +if (unlikely (notif->ctx != prev_notif->ctx || + notif->task != prev_notif->task)) +return -EEXIST ; + + nd->head = prev_nd->head; + nd->next = prev_nd->next; + prev_nd->next = nd; +net_zcopy_get (&nd->head->uarg); +return 0 ; +} +static const struct ubuf_info_ops io_ubuf_ops = { .complete = io_tx_ubuf_complete, + .link_skb = io_link_skb, };struct io_kiocb *io_alloc_notif (struct io_ring_ctx *ctx)
@@ -65,6 +119,9 @@ struct io_kiocb *io_alloc_notif(struct io_ring_ctx *ctx) nd =io_notif_to_data (notif); nd->zc_report = false; nd->account_pages =0 ; + nd->next =NULL ; + nd->head = nd; + nd->uarg.flags =IO_NOTIF_UBUF_FLAGS ; nd->uarg.ops = &io_ubuf_ops;refcount_set (&nd->uarg.refcnt,1 );
io_uring/notif.h +4 -0
@@ -14,6 +14,10 @@ struct io_notif_data {struct file *file;struct ubuf_info uarg;unsigned long account_pages; + +struct io_notif_data *next; +struct io_notif_data *head; +bool zc_report;bool zc_used;bool zc_copied;
Signature · 1 line
2.44.0
Willem de Bruijn
maintainerRe: [RFC 6/6] io_uring/notif: implement notification stacking
Quoted 6 lines · Pavel Begunkov
The network stack allows only one
ubuf_infoper skb, and unlikeMSG_ZEROCOPY, eachio_uringzerocopy send will carry a separateubuf_info. That means that send requests can't reuse a previosly allocated skb and need to get one more or more of new ones. That's fine for large sends, but otherwise it would spam the stack with lots of skbs carrying just a little data each.
Can you give a little context why each send request has to be a separate ubuf_info?
This patch series aims to make that model more efficient. Would it be possible to just change the model instead? I assume you tried that and it proved unworkable, but is it easy to explain what the fundamental blocker is?
MSG_ZEROCOPY uses uarg->len to identify multiple consecutive send operations that can be notified at once.
Re: [RFC 6/6] io_uring/notif: implement notification stacking
Quoted 8 lines · Willem de Bruijn
Quoted 6 lines · Pavel Begunkov
The network stack allows only one
ubuf_infoper skb, and unlikeMSG_ZEROCOPY, eachio_uringzerocopy send will carry a separateubuf_info. That means that send requests can't reuse a previosly allocated skb and need to get one more or more of new ones. That's fine for large sends, but otherwise it would spam the stack with lots of skbs carrying just a little data each.
Quoted 7 lines
Can you give a little context why each send request has to be a separate
ubuf_info?This patch series aims to make that model more efficient. Would it be possible to just change the model instead? I assume you tried that and it proved unworkable, but is it easy to explain what the fundamental blocker is?
The uapi is so that you get a buffer completion (analogous to what you get with recv(MSG_ERRQUEUE)) for each send request. With that, for skb to serve multiple send requests it'd need to store a list of completions in some way. One could try to track sockets, have one "active" ubuf_info per socket which all sends would use, and then eventually flush the active ubuf so it can post completions and create a new one. but io_uring wouldn't know when it needs to "flush", whenever in the net stack it happens naturally when it pushes skbs from the queue. Not to mention that socket tracking has its own complications.
As for uapi, in early versions of io_uring's SEND_ZC, ubuf_info and requests weren't entangled, roughly speaking, the user could choose that this request should use this ubuf_info (I can elaborate if interesting). It wasn't too complex, but all feedback was pointing that it's much easier to use hot it is now, and honestly it does buy with simplicity.
I'm not sure what a different model would give. We wouldn't win in efficiency comparing to this patch, I can go into details how there are no extra atomics/locks/kmalloc/etc., the only bit is waking up waiting tasks, but that still would need to happen. I can even optimise / ammortise ubuf refcounting if that would matter.
Quoted 2 lines
MSG_ZEROCOPYusesuarg->lento identify multiple consecutive send operations that can be notified at once.
Signature · 1 line
Pavel Begunkov
Willem de Bruijn
maintainerRe: [RFC 6/6] io_uring/notif: implement notification stacking
Quoted 17 lines · Pavel Begunkov
Quoted 8 lines · Willem de Bruijn
Quoted 6 lines · Pavel Begunkov
The network stack allows only one
ubuf_infoper skb, and unlikeMSG_ZEROCOPY, eachio_uringzerocopy send will carry a separateubuf_info. That means that send requests can't reuse a previosly allocated skb and need to get one more or more of new ones. That's fine for large sends, but otherwise it would spam the stack with lots of skbs carrying just a little data each.Quoted 7 lines
Can you give a little context why each send request has to be a separate
ubuf_info?This patch series aims to make that model more efficient. Would it be possible to just change the model instead? I assume you tried that and it proved unworkable, but is it easy to explain what the fundamental blocker is?
Quoted 4 lines
The uapi is so that you get a buffer completion (analogous to what you get with
recv(MSG_ERRQUEUE)) for each send request. With that, for skb to serve multiple send requests it'd need to store a list of completions in some way.
I probably don't know the io_uring implementation well enough yet, so take this with a huge grain of salt.
MSG_ZEROCOPY can generate completions for multiple send calls from a single uarg, by virtue of completions being incrementing IDs.
Is there a fundamental reason why io_uring needs a 1:1 mapping between request slots in the API and uarg in the datapath? Or differently, is there no trivial way to associate a range of completions with a single uarg?
Quoted 3 lines
One could try to track sockets, have one "active"
ubuf_infoper socket which all sends would use, and then eventually flush the active ubuf so it can post completions and create a new one.
This is basically what MSG_ZEROCOPY does for TCP. It signals POLLERR as soon as one completion arrives. Then when a process gets around to calling MSG_ERRQUEUE, it returns the range of completions that have arrived in the meantime. A process can thus decide to postpone completion handling to increase batching.
Quoted 11 lines
but
io_uring
wouldn't know when it needs to "flush", whenever in the net stack it happens naturally when it pushes skbs from the queue. Not to mention that socket tracking has its own complications.As for uapi, in early versions of io_uring's
SEND_ZC,ubuf_infoand requests weren't entangled, roughly speaking, the user could choose that this request should use thisubuf_info(I can elaborate if interesting). It wasn't too complex, but all feedback was pointing that it's much easier to use hot it is now, and honestly it does buy with simplicity.
I see. I suppose that answers the 1:1 mapping the ABI question I asked above. I should reread that patch.
Quoted 6 lines
I'm not sure what a different model would give. We wouldn't win in efficiency comparing to this patch, I can go into details how there are no extra atomics/locks/kmalloc/etc., the only bit is waking up waiting tasks, but that still would need to happen. I can even optimise / ammortise ubuf refcounting if that would matter.
Slight aside: we know that MSG_ZEROCOPY is quite inefficient for small sends. Very rough rule of thumb is you need around 16KB or larger sends for it to outperform regular copy. Part of that is the memory pinning. The other part is the notification handling. MSG_ERRQUEUE is expensive. I hope that io_uring cannot just match, but improve on MSG_ZEROCOPY, especially for smaller packets.
Quoted 6 lines
Quoted 3 lines
MSG_ZEROCOPYusesuarg->lento identify multiple consecutive send operations that can be notified at once.Signature · 1 line
Pavel Begunkov
Re: [RFC 6/6] io_uring/notif: implement notification stacking
Quoted 23 lines · Willem de Bruijn
Quoted 17 lines · Pavel Begunkov
Quoted 8 lines · Willem de Bruijn
Quoted 6 lines · Pavel Begunkov
The network stack allows only one
ubuf_infoper skb, and unlikeMSG_ZEROCOPY, eachio_uringzerocopy send will carry a separateubuf_info. That means that send requests can't reuse a previosly allocated skb and need to get one more or more of new ones. That's fine for large sends, but otherwise it would spam the stack with lots of skbs carrying just a little data each.Quoted 7 lines
Can you give a little context why each send request has to be a separate
ubuf_info?This patch series aims to make that model more efficient. Would it be possible to just change the model instead? I assume you tried that and it proved unworkable, but is it easy to explain what the fundamental blocker is?
Quoted 4 lines
The uapi is so that you get a buffer completion (analogous to what you get with
recv(MSG_ERRQUEUE)) for each send request. With that, for skb to serve multiple send requests it'd need to store a list of completions in some way.
Quoted 8 lines
I probably don't know the
io_uringimplementation well enough yet, so take this with a huge grain of salt.
MSG_ZEROCOPYcan generate completions for multiple send calls from a single uarg, by virtue of completions being incrementing IDs.Is there a fundamental reason why
io_uringneeds a 1:1 mapping between request slots in the API and uarg in the datapath?
That's an ABI difference. Where MSG_ZEROCOPY returns a range of bytes for the user to look up which buffers now can be reused, io_uring posts one completion per send request, and by request I mean an io_uring way of doing sendmsg(2). Hence the 1:1 mapping of uargs (which post that completion) to send zc requests.
IOW, and if MSG_ZEROCOPY's uarg tracks byte range it covers, then io_uring needs to know all requests associated with it, which is currently just one request because of the 1:1 mapping.
Quoted 3 lines
Or differently, is
there no trivial way to associate a range of completions with a single uarg?
Quite non trivial without changing ABI, I'd say. And a ABI change wouldn't be small and without pitfalls.
Quoted 9 lines
Quoted 3 lines
One could try to track sockets, have one "active"
ubuf_infoper socket which all sends would use, and then eventually flush the active ubuf so it can post completions and create a new one.This is basically what
MSG_ZEROCOPYdoes for TCP. It signals POLLERR as soon as one completion arrives. Then when a process gets around to callingMSG_ERRQUEUE, it returns the range of completions that have arrived in the meantime. A process can thus decide to postpone completion handling to increase batching.
Yes, there is that on the completion side, but in the submission you need to also decide when to let the current uarg go and allocate a new one. Not an issue if uarg is owned by the TCP stack, you don't have to additionally reference it, you know when you empty the queue and all that. Not that great if io_uring needs to talk to socket to understand when uarg is better to be dropped.
Quoted 22 lines
Quoted 11 lines
but
io_uring
wouldn't know when it needs to "flush", whenever in the net stack it happens naturally when it pushes skbs from the queue. Not to mention that socket tracking has its own complications.As for uapi, in early versions of io_uring's
SEND_ZC,ubuf_infoand requests weren't entangled, roughly speaking, the user could choose that this request should use thisubuf_info(I can elaborate if interesting). It wasn't too complex, but all feedback was pointing that it's much easier to use hot it is now, and honestly it does buy with simplicity.I see. I suppose that answers the 1:1 mapping the ABI question I asked above. I should reread that patch.
Quoted 6 lines
I'm not sure what a different model would give. We wouldn't win in efficiency comparing to this patch, I can go into details how there are no extra atomics/locks/kmalloc/etc., the only bit is waking up waiting tasks, but that still would need to happen. I can even optimise / ammortise ubuf refcounting if that would matter.
Quoted 6 lines
Slight aside: we know that
MSG_ZEROCOPYis quite inefficient for small sends. Very rough rule of thumb is you need around 16KB or larger sends for it to outperform regular copy. Part of that is the memory pinning. The other part is the notification handling.MSG_ERRQUEUEis expensive. I hope thatio_uringcannot just match, but improve onMSG_ZEROCOPY, especially for smaller packets.
I has some numbers left from this patchset benchmarking. Not too well suited to answer your question, but still gives an idea. Just a benchmark, single buffer, 100g broadcom NIC IIRC. All is io_uring based, -z<bool> switches copy vs zerocopy. Zero copy uses registered buffers, so no page pinning and page table traversal at runtime. 10s per run is not ideal, but was matching longer runs.
# 1200 bytes
./send-zerocopy -4 tcp -D <ip> -t 10 -n 1 -l0 -b1 -d -s1200 -z0 packets=15004160 (MB=17170), rps=1470996 (MB/s=1683) ./send-zerocopy -4 tcp -D <ip> -t 10 -n 1 -l0 -b1 -d -s1200 -z1 packets=10440224 (MB=11947), rps=1023551 (MB/s=1171)
# 4000 bytes
./send-zerocopy -4 tcp -D <ip> -t 10 -n 1 -l0 -b1 -d -s4000 -z0 packets=11742688 (MB=44794), rps=1151243 (MB/s=4391) ./send-zerocopy -4 tcp -D <ip> -t 10 -n 1 -l0 -b1 -d -s4000 -z1 packets=14144048 (MB=53955), rps=1386671 (MB/s=5289)
# 8000 bytes
./send-zerocopy -4 tcp -D <ip> -t 10 -n 1 -l0 -b1 -d -s8000 -z0 packets=6868976 (MB=52406), rps=673429 (MB/s=5137) ./send-zerocopy -4 tcp -D <ip> -t 10 -n 1 -l0 -b1 -d -s8000 -z1 packets=10800784 (MB=82403), rps=1058900 (MB/s=8078)
Signature · 1 line
Pavel Begunkov
Willem de Bruijn
maintainerRe: [RFC 6/6] io_uring/notif: implement notification stacking
Quoted 28 lines
Quoted 7 lines
Slight aside: we know that
MSG_ZEROCOPYis quite inefficient for small sends. Very rough rule of thumb is you need around 16KB or larger sends for it to outperform regular copy. Part of that is the memory pinning. The other part is the notification handling.MSG_ERRQUEUEis expensive. I hope thatio_uringcannot just match, but improve onMSG_ZEROCOPY, especially for smaller packets.I has some numbers left from this patchset benchmarking. Not too well suited to answer your question, but still gives an idea. Just a benchmark, single buffer, 100g broadcom NIC IIRC. All is
io_uringbased, -z<bool> switches copy vs zerocopy. Zero copy uses registered buffers, so no page pinning and page table traversal at runtime. 10s per run is not ideal, but was matching longer runs.# 1200 bytes
./send-zerocopy -4 tcp -D <ip> -t 10 -n 1 -l0 -b1 -d -s1200 -z0 packets=15004160 (MB=17170), rps=1470996 (MB/s=1683) ./send-zerocopy -4 tcp -D <ip> -t 10 -n 1 -l0 -b1 -d -s1200 -z1 packets=10440224 (MB=11947), rps=1023551 (MB/s=1171)# 4000 bytes
./send-zerocopy -4 tcp -D <ip> -t 10 -n 1 -l0 -b1 -d -s4000 -z0 packets=11742688 (MB=44794), rps=1151243 (MB/s=4391) ./send-zerocopy -4 tcp -D <ip> -t 10 -n 1 -l0 -b1 -d -s4000 -z1 packets=14144048 (MB=53955), rps=1386671 (MB/s=5289)
Quoted 5 lines
# 8000 bytes
./send-zerocopy -4 tcp -D <ip> -t 10 -n 1 -l0 -b1 -d -s8000 -z0 packets=6868976 (MB=52406), rps=673429 (MB/s=5137) ./send-zerocopy -4 tcp -D <ip> -t 10 -n 1 -l0 -b1 -d -s8000 -z1 packets=10800784 (MB=82403), rps=1058900 (MB/s=8078)
Parity around 4K. That is very encouraging :)
Jens Axboe
maintainerQuoted 10 lines · Pavel Begunkov
io_uringallocates aubuf_infoper zerocopy send request, it's convenient for the userspace but with how things are it means that every time the TCP stack has to allocate a new skb instead of amending into a previous one. Unless sends are big enough, there will be lots of small skbs straining the stack and dipping performance.The patchset implements notification, i.e. an io_uring's
ubuf_infoextension, stacking. It tries to link ubuf_info's into a list, and the entire link will be put down together once all references are gone.
Excellent! I'll take a closer look, but I ran a quick test with my test tool just to see the difference. This is on a 100G link.
Packet size Before (Mbit) After (Mbit) Diff
====================================================
100 290 1250 4.3x
200 560 2460 4.4x
400 1190 4900 4.1x
800 2300 9700 4.2x
1600 4500 19100 4.2x
3200 8900 35000 3.9x
which are just rough numbers and the tool isn't that great, but definitely encouraging. And it does have parity with sync MSG_ZEROPCY, which is what I was really bugged about before.
Signature · 1 line
Jens Axboe
Reviewed the patch set, and I think this is nice and clean and the right fix. For the series:
If the net people agree, we'll have to coordinate staging of the first two patches.
Signature · 1 line
Jens Axboe
Quoted 5 lines · Pavel Begunkov
io_uringallocates aubuf_infoper zerocopy send request, it's convenient for the userspace but with how things are it means that every time the TCP stack has to allocate a new skb instead of amending into a previous one. Unless sends are big enough, there will be lots of small skbs straining the stack and dipping performance.
The ubuf_info forces TCP segmentation at less than MTU boundaries which kills performance with small message sizes as TCP is forced to send small packets. This is an interesting solution to allow the byte stream to flow yet maintain the segmentation boundaries for callbacks.
Quoted 34 lines
The patchset implements notification, i.e. an io_uring's
ubuf_infoextension, stacking. It tries to link ubuf_info's into a list, and the entire link will be put down together once all references are gone.Testing with liburing/examples/send-zerocopy and another custom made tool, with 4K bytes per send it improves performance ~6 times and levels it with
MSG_ZEROCOPY. Without the patchset it requires much larger sends to utilise all potential.bytes | before | after (Kqps) 100 | 283 | 936 1200 | 195 | 1023 4000 | 193 | 1386 8000 | 154 | 1058Pavel Begunkov (6):
net: extendubuf_infocallback to ops structure
net: add callback for setting aubuf_infoto skbio_uring/notif: refactorio_tx_ubuf_complete()io_uring/notif: remove ctx var fromio_notif_tw_completeio_uring/notif: simplifyio_notif_flush()io_uring/notif: implement notification stackingdrivers/net/tap.c | 2 +- drivers/net/tun.c | 2 +- drivers/vhost/net.c | 8 +++- include/linux/skbuff.h | 21 ++++++---- io_uring/notif.c | 91 +++++++++++++++++++++++++++++++++++------- io_uring/notif.h | 13 +++--- net/core/skbuff.c | 37 +++++++++++------ 7 files changed, 129 insertions(+), 45 deletions(-)
Quoted 11 lines · David Ahern
Quoted 5 lines · Pavel Begunkov
io_uringallocates aubuf_infoper zerocopy send request, it's convenient for the userspace but with how things are it means that every time the TCP stack has to allocate a new skb instead of amending into a previous one. Unless sends are big enough, there will be lots of small skbs straining the stack and dipping performance.The
ubuf_infoforces TCP segmentation at less than MTU boundaries which kills performance with small message sizes as TCP is forced to send small packets. This is an interesting solution to allow the byte stream to flow yet maintain the segmentation boundaries for callbacks.
Thanks, I'll add your reviews if the patches survive in the current form!
Quoted 34 lines
Quoted 33 lines
The patchset implements notification, i.e. an io_uring's
ubuf_infoextension, stacking. It tries to link ubuf_info's into a list, and the entire link will be put down together once all references are gone.Testing with liburing/examples/send-zerocopy and another custom made tool, with 4K bytes per send it improves performance ~6 times and levels it with
MSG_ZEROCOPY. Without the patchset it requires much larger sends to utilise all potential.bytes | before | after (Kqps) 100 | 283 | 936 1200 | 195 | 1023 4000 | 193 | 1386 8000 | 154 | 1058Pavel Begunkov (6):
net: extendubuf_infocallback to ops structure
net: add callback for setting aubuf_infoto skbio_uring/notif: refactorio_tx_ubuf_complete()io_uring/notif: remove ctx var fromio_notif_tw_completeio_uring/notif: simplifyio_notif_flush()io_uring/notif: implement notification stackingdrivers/net/tap.c | 2 +- drivers/net/tun.c | 2 +- drivers/vhost/net.c | 8 +++- include/linux/skbuff.h | 21 ++++++---- io_uring/notif.c | 91 +++++++++++++++++++++++++++++++++++------- io_uring/notif.h | 13 +++--- net/core/skbuff.c | 37 +++++++++++------ 7 files changed, 129 insertions(+), 45 deletions(-)
Signature · 1 line
Pavel Begunkov