Commit 717ca0b8 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'io_uring-6.8-2024-02-01' of git://git.kernel.dk/linux

Pull io_uring fixes from Jens Axboe:

 - Fix for missing retry for read multishot.

   If we trigger the execution of it and there's more than one buffer to
   be read, then we don't always read more than the first one. As it's
   edge triggered, this can lead to stalls.

 - Limit inline receive multishot retries for fairness reasons.

   If we have a very bursty socket receiving data, we still need to
   ensure we process other requests as well. This is really two minor
   cleanups, then adding a way for poll reissue to trigger a requeue,
   and then finally having multishot receive utilize that.

 - Fix for a weird corner case for non-multishot receive with
   MSG_WAITALL, using provided buffers, and setting the length to
   zero (to let the buffer dictate the receive size).

* tag 'io_uring-6.8-2024-02-01' of git://git.kernel.dk/linux:
  io_uring/net: fix sr->len for IORING_OP_RECV with MSG_WAITALL and buffers
  io_uring/net: limit inline multishot retries
  io_uring/poll: add requeue return code from poll multishot handling
  io_uring/net: un-indent mshot retry path in io_recv_finish()
  io_uring/poll: move poll execution helpers higher up
  io_uring/rw: ensure poll based multishot read retries appropriately
parents ec86369c 72bd8025
...@@ -15,11 +15,17 @@ ...@@ -15,11 +15,17 @@
#include <trace/events/io_uring.h> #include <trace/events/io_uring.h>
#endif #endif
enum { enum {
IOU_OK = 0, IOU_OK = 0,
IOU_ISSUE_SKIP_COMPLETE = -EIOCBQUEUED, IOU_ISSUE_SKIP_COMPLETE = -EIOCBQUEUED,
/*
* Requeue the task_work to restart operations on this request. The
* actual value isn't important, should just be not an otherwise
* valid error code, yet less than -MAX_ERRNO and valid internally.
*/
IOU_REQUEUE = -3072,
/* /*
* Intended only when both IO_URING_F_MULTISHOT is passed * Intended only when both IO_URING_F_MULTISHOT is passed
* to indicate to the poll runner that multishot should be * to indicate to the poll runner that multishot should be
......
...@@ -60,6 +60,7 @@ struct io_sr_msg { ...@@ -60,6 +60,7 @@ struct io_sr_msg {
unsigned len; unsigned len;
unsigned done_io; unsigned done_io;
unsigned msg_flags; unsigned msg_flags;
unsigned nr_multishot_loops;
u16 flags; u16 flags;
/* initialised and used only by !msg send variants */ /* initialised and used only by !msg send variants */
u16 addr_len; u16 addr_len;
...@@ -70,6 +71,13 @@ struct io_sr_msg { ...@@ -70,6 +71,13 @@ struct io_sr_msg {
struct io_kiocb *notif; struct io_kiocb *notif;
}; };
/*
* Number of times we'll try and do receives if there's more data. If we
* exceed this limit, then add us to the back of the queue and retry from
* there. This helps fairness between flooding clients.
*/
#define MULTISHOT_MAX_RETRY 32
static inline bool io_check_multishot(struct io_kiocb *req, static inline bool io_check_multishot(struct io_kiocb *req,
unsigned int issue_flags) unsigned int issue_flags)
{ {
...@@ -611,6 +619,7 @@ int io_recvmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) ...@@ -611,6 +619,7 @@ int io_recvmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
sr->msg_flags |= MSG_CMSG_COMPAT; sr->msg_flags |= MSG_CMSG_COMPAT;
#endif #endif
sr->done_io = 0; sr->done_io = 0;
sr->nr_multishot_loops = 0;
return 0; return 0;
} }
...@@ -645,23 +654,35 @@ static inline bool io_recv_finish(struct io_kiocb *req, int *ret, ...@@ -645,23 +654,35 @@ static inline bool io_recv_finish(struct io_kiocb *req, int *ret,
return true; return true;
} }
if (!mshot_finished) { if (mshot_finished)
if (io_fill_cqe_req_aux(req, issue_flags & IO_URING_F_COMPLETE_DEFER, goto finish;
*ret, cflags | IORING_CQE_F_MORE)) {
io_recv_prep_retry(req); /*
/* Known not-empty or unknown state, retry */ * Fill CQE for this receive and see if we should keep trying to
if (cflags & IORING_CQE_F_SOCK_NONEMPTY || * receive from this socket.
msg->msg_inq == -1) */
if (io_fill_cqe_req_aux(req, issue_flags & IO_URING_F_COMPLETE_DEFER,
*ret, cflags | IORING_CQE_F_MORE)) {
struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg);
int mshot_retry_ret = IOU_ISSUE_SKIP_COMPLETE;
io_recv_prep_retry(req);
/* Known not-empty or unknown state, retry */
if (cflags & IORING_CQE_F_SOCK_NONEMPTY || msg->msg_inq == -1) {
if (sr->nr_multishot_loops++ < MULTISHOT_MAX_RETRY)
return false; return false;
if (issue_flags & IO_URING_F_MULTISHOT) /* mshot retries exceeded, force a requeue */
*ret = IOU_ISSUE_SKIP_COMPLETE; sr->nr_multishot_loops = 0;
else mshot_retry_ret = IOU_REQUEUE;
*ret = -EAGAIN;
return true;
} }
/* Otherwise stop multishot but use the current result. */ if (issue_flags & IO_URING_F_MULTISHOT)
*ret = mshot_retry_ret;
else
*ret = -EAGAIN;
return true;
} }
/* Otherwise stop multishot but use the current result. */
finish:
io_req_set_res(req, *ret, cflags); io_req_set_res(req, *ret, cflags);
if (issue_flags & IO_URING_F_MULTISHOT) if (issue_flags & IO_URING_F_MULTISHOT)
...@@ -902,6 +923,7 @@ int io_recv(struct io_kiocb *req, unsigned int issue_flags) ...@@ -902,6 +923,7 @@ int io_recv(struct io_kiocb *req, unsigned int issue_flags)
if (!buf) if (!buf)
return -ENOBUFS; return -ENOBUFS;
sr->buf = buf; sr->buf = buf;
sr->len = len;
} }
ret = import_ubuf(ITER_DEST, sr->buf, len, &msg.msg_iter); ret = import_ubuf(ITER_DEST, sr->buf, len, &msg.msg_iter);
......
...@@ -226,8 +226,29 @@ enum { ...@@ -226,8 +226,29 @@ enum {
IOU_POLL_NO_ACTION = 1, IOU_POLL_NO_ACTION = 1,
IOU_POLL_REMOVE_POLL_USE_RES = 2, IOU_POLL_REMOVE_POLL_USE_RES = 2,
IOU_POLL_REISSUE = 3, IOU_POLL_REISSUE = 3,
IOU_POLL_REQUEUE = 4,
}; };
static void __io_poll_execute(struct io_kiocb *req, int mask)
{
unsigned flags = 0;
io_req_set_res(req, mask, 0);
req->io_task_work.func = io_poll_task_func;
trace_io_uring_task_add(req, mask);
if (!(req->flags & REQ_F_POLL_NO_LAZY))
flags = IOU_F_TWQ_LAZY_WAKE;
__io_req_task_work_add(req, flags);
}
static inline void io_poll_execute(struct io_kiocb *req, int res)
{
if (io_poll_get_ownership(req))
__io_poll_execute(req, res);
}
/* /*
* All poll tw should go through this. Checks for poll events, manages * All poll tw should go through this. Checks for poll events, manages
* references, does rewait, etc. * references, does rewait, etc.
...@@ -309,6 +330,8 @@ static int io_poll_check_events(struct io_kiocb *req, struct io_tw_state *ts) ...@@ -309,6 +330,8 @@ static int io_poll_check_events(struct io_kiocb *req, struct io_tw_state *ts)
int ret = io_poll_issue(req, ts); int ret = io_poll_issue(req, ts);
if (ret == IOU_STOP_MULTISHOT) if (ret == IOU_STOP_MULTISHOT)
return IOU_POLL_REMOVE_POLL_USE_RES; return IOU_POLL_REMOVE_POLL_USE_RES;
else if (ret == IOU_REQUEUE)
return IOU_POLL_REQUEUE;
if (ret < 0) if (ret < 0)
return ret; return ret;
} }
...@@ -331,8 +354,12 @@ void io_poll_task_func(struct io_kiocb *req, struct io_tw_state *ts) ...@@ -331,8 +354,12 @@ void io_poll_task_func(struct io_kiocb *req, struct io_tw_state *ts)
int ret; int ret;
ret = io_poll_check_events(req, ts); ret = io_poll_check_events(req, ts);
if (ret == IOU_POLL_NO_ACTION) if (ret == IOU_POLL_NO_ACTION) {
return;
} else if (ret == IOU_POLL_REQUEUE) {
__io_poll_execute(req, 0);
return; return;
}
io_poll_remove_entries(req); io_poll_remove_entries(req);
io_poll_tw_hash_eject(req, ts); io_poll_tw_hash_eject(req, ts);
...@@ -364,26 +391,6 @@ void io_poll_task_func(struct io_kiocb *req, struct io_tw_state *ts) ...@@ -364,26 +391,6 @@ void io_poll_task_func(struct io_kiocb *req, struct io_tw_state *ts)
} }
} }
static void __io_poll_execute(struct io_kiocb *req, int mask)
{
unsigned flags = 0;
io_req_set_res(req, mask, 0);
req->io_task_work.func = io_poll_task_func;
trace_io_uring_task_add(req, mask);
if (!(req->flags & REQ_F_POLL_NO_LAZY))
flags = IOU_F_TWQ_LAZY_WAKE;
__io_req_task_work_add(req, flags);
}
static inline void io_poll_execute(struct io_kiocb *req, int res)
{
if (io_poll_get_ownership(req))
__io_poll_execute(req, res);
}
static void io_poll_cancel_req(struct io_kiocb *req) static void io_poll_cancel_req(struct io_kiocb *req)
{ {
io_poll_mark_cancelled(req); io_poll_mark_cancelled(req);
......
...@@ -24,6 +24,15 @@ struct async_poll { ...@@ -24,6 +24,15 @@ struct async_poll {
struct io_poll *double_poll; struct io_poll *double_poll;
}; };
/*
* Must only be called inside issue_flags & IO_URING_F_MULTISHOT, or
* potentially other cases where we already "own" this poll request.
*/
static inline void io_poll_multishot_retry(struct io_kiocb *req)
{
atomic_inc(&req->poll_refs);
}
int io_poll_add_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe); int io_poll_add_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe);
int io_poll_add(struct io_kiocb *req, unsigned int issue_flags); int io_poll_add(struct io_kiocb *req, unsigned int issue_flags);
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "opdef.h" #include "opdef.h"
#include "kbuf.h" #include "kbuf.h"
#include "rsrc.h" #include "rsrc.h"
#include "poll.h"
#include "rw.h" #include "rw.h"
struct io_rw { struct io_rw {
...@@ -962,8 +963,15 @@ int io_read_mshot(struct io_kiocb *req, unsigned int issue_flags) ...@@ -962,8 +963,15 @@ int io_read_mshot(struct io_kiocb *req, unsigned int issue_flags)
if (io_fill_cqe_req_aux(req, if (io_fill_cqe_req_aux(req,
issue_flags & IO_URING_F_COMPLETE_DEFER, issue_flags & IO_URING_F_COMPLETE_DEFER,
ret, cflags | IORING_CQE_F_MORE)) { ret, cflags | IORING_CQE_F_MORE)) {
if (issue_flags & IO_URING_F_MULTISHOT) if (issue_flags & IO_URING_F_MULTISHOT) {
/*
* Force retry, as we might have more data to
* be read and otherwise it won't get retried
* until (if ever) another poll is triggered.
*/
io_poll_multishot_retry(req);
return IOU_ISSUE_SKIP_COMPLETE; return IOU_ISSUE_SKIP_COMPLETE;
}
return -EAGAIN; return -EAGAIN;
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment