Fix MSG_WAITALL for IORING_OP_RECV/RECVMSG
Subsystems (3)
Jens Axboe
maintainerv2 cover Fix MSG_WAITALL for IORING_OP_RECV/RECVMSG
Hi,
If we get a partial receive, we don't retry even if MSG_WAITALL is set. Ensure that we retry for the remainder in that case.
The ordering of patches may look a bit odd here, but it's done this way to make it easier to handle for the stable backport.
v2:
- Only do it for SOCK_STREAM/SOCK_SEQPACKET
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH v2 1/2 io_uring: ensure recv and recvmsg handle MSG_WAITALL correctly
We currently don't attempt to get the full asked for length even if MSG_WAITALL is set, if we get a partial receive. If we do see a partial receive, then just note how many bytes we did and return -EAGAIN to get it retried.
The iov is advanced appropriately for the vector based case, and we manually bump the buffer and remainder for the non-vector case.
--- fs/io_uring.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)
fs/io_uring.c +28 -0
@@ -612,6 +612,7 @@ struct io_sr_msg {int msg_flags;int bgid;size_t len; +size_t done_io; };struct io_open {
@@ -5417,12 +5418,21 @@ static int io_recvmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)if (req->ctx->compat) sr->msg_flags |=MSG_CMSG_COMPAT ;#endif + sr->done_io =0 ;return 0 ; } +static bool io_net_retry (struct socket *sock,int flags) +{ +if (!(flags &MSG_WAITALL )) +return false; +return sock->type ==SOCK_STREAM || sock->type ==SOCK_SEQPACKET ; +} +static int io_recvmsg (struct io_kiocb *req,unsigned int issue_flags) {struct io_async_msghdr iomsg, *kmsg; +struct io_sr_msg *sr = &req->sr_msg;struct socket *sock;struct io_buffer *kbuf;unsigned flags;
@@ -5465,6 +5475,10 @@ static int io_recvmsg(struct io_kiocb *req, unsigned int issue_flags)return io_setup_async_msg (req, kmsg);if (ret == -ERESTARTSYS ) ret = -EINTR ; +if (ret >0 &&io_net_retry (sock, flags)) { + sr->done_io += ret; +return io_setup_async_msg (req, kmsg); + }req_set_fail (req); }else if ((flags &MSG_WAITALL ) && (kmsg->msg.msg_flags & (MSG_TRUNC |MSG_CTRUNC ))) {req_set_fail (req);
@@ -5474,6 +5488,10 @@ static int io_recvmsg(struct io_kiocb *req, unsigned int issue_flags)if (kmsg->free_iov)kfree (kmsg->free_iov); req->flags &= ~REQ_F_NEED_CLEANUP ; +if (ret >=0 ) + ret += sr->done_io; +else if (sr->done_io) + ret = sr->done_io;__io_req_complete (req, issue_flags, ret,io_put_kbuf (req, issue_flags));return 0 ; }
@@ -5524,12 +5542,22 @@ static int io_recv(struct io_kiocb *req, unsigned int issue_flags)return -EAGAIN ;if (ret == -ERESTARTSYS ) ret = -EINTR ; +if (ret >0 &&io_net_retry (sock, flags)) { + sr->len -= ret; + sr->buf += ret; + sr->done_io += ret; +return -EAGAIN ; + }req_set_fail (req); }else if ((flags &MSG_WAITALL ) && (msg.msg_flags & (MSG_TRUNC |MSG_CTRUNC ))) { out_free:req_set_fail (req); } +if (ret >=0 ) + ret += sr->done_io; +else if (sr->done_io) + ret = sr->done_io;__io_req_complete (req, issue_flags, ret,io_put_kbuf (req, issue_flags));return 0 ; }
Signature · 1 line
2.35.1
Re: [PATCH 1/2] io_uring: ensure recv and recvmsg handle MSG_WAITALL correctly
Hi Jens,
Quoted 4 lines
fs/io_uring.c +0
-0@@ -5524,12 +5542,22 @@ static int io_recv(struct io_kiocb *req, unsigned int issue_flags)return -EAGAIN ;if (ret == -ERESTARTSYS ) ret = -EINTR ;
fs/io_uring.c static int io_recv(struct io_kiocb *req, unsigned int issue_flags) · line 5554
Quoted 10 lines
fs/io_uring.c +6
-0+if (ret >0 &&io_net_retry (sock, flags)) { + sr->len -= ret; + sr->buf += ret; + sr->done_io += ret; +return -EAGAIN ; + }req_set_fail (req); }else if ((flags &MSG_WAITALL ) && (msg.msg_flags & (MSG_TRUNC |MSG_CTRUNC ))) { out_free:req_set_fail (req);
The change only affects retry based socket io in the main thread, correct?
The truncated mesages still trigger req_set_fail if MSG_WAITALL was set?
metze
Jens Axboe
maintainerRe: [PATCH 1/2] io_uring: ensure recv and recvmsg handle MSG_WAITALL correctly
Quoted 18 lines · Stefan Metzmacher
Hi Jens,
Quoted 4 lines
fs/io_uring.c +0
-0@@ -5524,12 +5542,22 @@ static int io_recv(struct io_kiocb *req, unsigned int issue_flags)return -EAGAIN ;if (ret == -ERESTARTSYS ) ret = -EINTR ;fs/io_uring.c static int io_recv(struct io_kiocb *req, unsigned int issue_flags) · line 5554
Quoted 10 lines
fs/io_uring.c +6
-0+if (ret >0 &&io_net_retry (sock, flags)) { + sr->len -= ret; + sr->buf += ret; + sr->done_io += ret; +return -EAGAIN ; + }req_set_fail (req); }else if ((flags &MSG_WAITALL ) && (msg.msg_flags & (MSG_TRUNC |MSG_CTRUNC ))) { out_free:req_set_fail (req);The change only affects retry based socket io in the main thread, correct?
Not sure I follow - it affects retries for streams based sockets, where previously they could be broken into two pieces when they should not be.
Quoted 1 line
The truncated mesages still trigger
req_set_failifMSG_WAITALLwas set?
If we don't retry, regardless of flags, then we'll set failure on the request to break links. If it ends up transferring the whole amount, regardless of whether or not it happens in one go or not, it will not fail links.
Signature · 1 line
Jens Axboe
Jens Axboe
maintainerPATCH v2 2/2 io_uring: add flag for disabling provided buffer recycling
In reply to Jens AxboeIf we need to continue doing this IO, then we don't want a potentially selected buffer recycled. Add a flag for that.
Set this for recv/recvmsg if they do partial IO.
--- fs/io_uring.c | 8 ++++++++ 1 file changed, 8 insertions(+)
fs/io_uring.c +8 -0
@@ -783,6 +783,7 @@ enum {REQ_F_SKIP_LINK_CQES_BIT ,REQ_F_SINGLE_POLL_BIT ,REQ_F_DOUBLE_POLL_BIT , +REQ_F_PARTIAL_IO_BIT ,/* keep async read/write and isreg together and in order */ REQ_F_SUPPORT_NOWAIT_BIT , REQ_F_ISREG_BIT,
@@ -845,6 +846,8 @@ enum {REQ_F_SINGLE_POLL =BIT (REQ_F_SINGLE_POLL_BIT ),/* double poll may active */ REQ_F_DOUBLE_POLL =BIT (REQ_F_DOUBLE_POLL_BIT ), +/* request has already done partial IO */ + REQ_F_PARTIAL_IO = BIT(REQ_F_PARTIAL_IO_BIT), }; struct async_poll {
@@ -1392,6 +1395,9 @@ static void io_kbuf_recycle(struct io_kiocb *req, unsigned issue_flags)if (likely (!(req->flags &REQ_F_BUFFER_SELECTED )))return ; +/* don't recycle if we already did IO to this buffer */ +if (req->flags &REQ_F_PARTIAL_IO ) +return ;if (issue_flags &IO_URING_F_UNLOCKED )mutex_lock (&ctx->uring_lock);
@@ -5477,6 +5483,7 @@ static int io_recvmsg(struct io_kiocb *req, unsigned int issue_flags) ret = -EINTR ;if (ret >0 &&io_net_retry (sock, flags)) { sr->done_io += ret; + req->flags |=REQ_F_PARTIAL_IO ;return io_setup_async_msg (req, kmsg); }req_set_fail (req);
@@ -5546,6 +5553,7 @@ static int io_recv(struct io_kiocb *req, unsigned int issue_flags) sr->len -= ret; sr->buf += ret; sr->done_io += ret; + req->flags |=REQ_F_PARTIAL_IO ;return -EAGAIN ; }req_set_fail (req);
Signature · 1 line
2.35.1
Quoted 6 lines · Jens Axboe
Hi,
If we get a partial receive, we don't retry even if
MSG_WAITALLis set. Ensure that we retry for the remainder in that case.
Quoted 10 lines
The ordering of patches may look a bit odd here, but it's done this way to make it easier to handle for the stable backport.
v2:
- Only do it for SOCK_STREAM/SOCK_SEQPACKET--
Jens Axboe
Jens:
I was able to test the patch, after making some minor changes to apply to Fedora kernel. I confirm that the patch works.
Signature · 7 lines
---------------------------------------- Constantine Gavrilov Storage Architect Master Inventor Tel-Aviv IBM Storage Lab 1 Azrieli Center, Tel-Aviv ----------------------------------------
Jens Axboe
maintainerQuoted 18 lines · Constantine Gavrilov
Quoted 6 lines · Jens Axboe
Hi,
If we get a partial receive, we don't retry even if
MSG_WAITALLis set. Ensure that we retry for the remainder in that case.Quoted 10 lines
The ordering of patches may look a bit odd here, but it's done this way to make it easier to handle for the stable backport.
v2:
- Only do it for SOCK_STREAM/SOCK_SEQPACKET--
Jens Axboe
Quoted 4 lines
Jens:
I was able to test the patch, after making some minor changes to apply to Fedora kernel. I confirm that the patch works.
Great, thanks for testing! It'll go into the current kernel tree next week and make its way back to stable after that.
Signature · 1 line
Jens Axboe