Skip to content

Commit 717ca0b

Browse files
committed
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
2 parents ec86369 + 72bd802 commit 717ca0b

File tree

5 files changed

+89
-37
lines changed

5 files changed

+89
-37
lines changed

io_uring/io_uring.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,17 @@
1515
#include <trace/events/io_uring.h>
1616
#endif
1717

18-
1918
enum {
2019
IOU_OK = 0,
2120
IOU_ISSUE_SKIP_COMPLETE = -EIOCBQUEUED,
2221

22+
/*
23+
* Requeue the task_work to restart operations on this request. The
24+
* actual value isn't important, should just be not an otherwise
25+
* valid error code, yet less than -MAX_ERRNO and valid internally.
26+
*/
27+
IOU_REQUEUE = -3072,
28+
2329
/*
2430
* Intended only when both IO_URING_F_MULTISHOT is passed
2531
* to indicate to the poll runner that multishot should be

io_uring/net.c

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ struct io_sr_msg {
6060
unsigned len;
6161
unsigned done_io;
6262
unsigned msg_flags;
63+
unsigned nr_multishot_loops;
6364
u16 flags;
6465
/* initialised and used only by !msg send variants */
6566
u16 addr_len;
@@ -70,6 +71,13 @@ struct io_sr_msg {
7071
struct io_kiocb *notif;
7172
};
7273

74+
/*
75+
* Number of times we'll try and do receives if there's more data. If we
76+
* exceed this limit, then add us to the back of the queue and retry from
77+
* there. This helps fairness between flooding clients.
78+
*/
79+
#define MULTISHOT_MAX_RETRY 32
80+
7381
static inline bool io_check_multishot(struct io_kiocb *req,
7482
unsigned int issue_flags)
7583
{
@@ -611,6 +619,7 @@ int io_recvmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
611619
sr->msg_flags |= MSG_CMSG_COMPAT;
612620
#endif
613621
sr->done_io = 0;
622+
sr->nr_multishot_loops = 0;
614623
return 0;
615624
}
616625

@@ -645,23 +654,35 @@ static inline bool io_recv_finish(struct io_kiocb *req, int *ret,
645654
return true;
646655
}
647656

648-
if (!mshot_finished) {
649-
if (io_fill_cqe_req_aux(req, issue_flags & IO_URING_F_COMPLETE_DEFER,
650-
*ret, cflags | IORING_CQE_F_MORE)) {
651-
io_recv_prep_retry(req);
652-
/* Known not-empty or unknown state, retry */
653-
if (cflags & IORING_CQE_F_SOCK_NONEMPTY ||
654-
msg->msg_inq == -1)
657+
if (mshot_finished)
658+
goto finish;
659+
660+
/*
661+
* Fill CQE for this receive and see if we should keep trying to
662+
* receive from this socket.
663+
*/
664+
if (io_fill_cqe_req_aux(req, issue_flags & IO_URING_F_COMPLETE_DEFER,
665+
*ret, cflags | IORING_CQE_F_MORE)) {
666+
struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg);
667+
int mshot_retry_ret = IOU_ISSUE_SKIP_COMPLETE;
668+
669+
io_recv_prep_retry(req);
670+
/* Known not-empty or unknown state, retry */
671+
if (cflags & IORING_CQE_F_SOCK_NONEMPTY || msg->msg_inq == -1) {
672+
if (sr->nr_multishot_loops++ < MULTISHOT_MAX_RETRY)
655673
return false;
656-
if (issue_flags & IO_URING_F_MULTISHOT)
657-
*ret = IOU_ISSUE_SKIP_COMPLETE;
658-
else
659-
*ret = -EAGAIN;
660-
return true;
674+
/* mshot retries exceeded, force a requeue */
675+
sr->nr_multishot_loops = 0;
676+
mshot_retry_ret = IOU_REQUEUE;
661677
}
662-
/* Otherwise stop multishot but use the current result. */
678+
if (issue_flags & IO_URING_F_MULTISHOT)
679+
*ret = mshot_retry_ret;
680+
else
681+
*ret = -EAGAIN;
682+
return true;
663683
}
664-
684+
/* Otherwise stop multishot but use the current result. */
685+
finish:
665686
io_req_set_res(req, *ret, cflags);
666687

667688
if (issue_flags & IO_URING_F_MULTISHOT)
@@ -902,6 +923,7 @@ int io_recv(struct io_kiocb *req, unsigned int issue_flags)
902923
if (!buf)
903924
return -ENOBUFS;
904925
sr->buf = buf;
926+
sr->len = len;
905927
}
906928

907929
ret = import_ubuf(ITER_DEST, sr->buf, len, &msg.msg_iter);

io_uring/poll.c

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,29 @@ enum {
226226
IOU_POLL_NO_ACTION = 1,
227227
IOU_POLL_REMOVE_POLL_USE_RES = 2,
228228
IOU_POLL_REISSUE = 3,
229+
IOU_POLL_REQUEUE = 4,
229230
};
230231

232+
static void __io_poll_execute(struct io_kiocb *req, int mask)
233+
{
234+
unsigned flags = 0;
235+
236+
io_req_set_res(req, mask, 0);
237+
req->io_task_work.func = io_poll_task_func;
238+
239+
trace_io_uring_task_add(req, mask);
240+
241+
if (!(req->flags & REQ_F_POLL_NO_LAZY))
242+
flags = IOU_F_TWQ_LAZY_WAKE;
243+
__io_req_task_work_add(req, flags);
244+
}
245+
246+
static inline void io_poll_execute(struct io_kiocb *req, int res)
247+
{
248+
if (io_poll_get_ownership(req))
249+
__io_poll_execute(req, res);
250+
}
251+
231252
/*
232253
* All poll tw should go through this. Checks for poll events, manages
233254
* references, does rewait, etc.
@@ -309,6 +330,8 @@ static int io_poll_check_events(struct io_kiocb *req, struct io_tw_state *ts)
309330
int ret = io_poll_issue(req, ts);
310331
if (ret == IOU_STOP_MULTISHOT)
311332
return IOU_POLL_REMOVE_POLL_USE_RES;
333+
else if (ret == IOU_REQUEUE)
334+
return IOU_POLL_REQUEUE;
312335
if (ret < 0)
313336
return ret;
314337
}
@@ -331,8 +354,12 @@ void io_poll_task_func(struct io_kiocb *req, struct io_tw_state *ts)
331354
int ret;
332355

333356
ret = io_poll_check_events(req, ts);
334-
if (ret == IOU_POLL_NO_ACTION)
357+
if (ret == IOU_POLL_NO_ACTION) {
358+
return;
359+
} else if (ret == IOU_POLL_REQUEUE) {
360+
__io_poll_execute(req, 0);
335361
return;
362+
}
336363
io_poll_remove_entries(req);
337364
io_poll_tw_hash_eject(req, ts);
338365

@@ -364,26 +391,6 @@ void io_poll_task_func(struct io_kiocb *req, struct io_tw_state *ts)
364391
}
365392
}
366393

367-
static void __io_poll_execute(struct io_kiocb *req, int mask)
368-
{
369-
unsigned flags = 0;
370-
371-
io_req_set_res(req, mask, 0);
372-
req->io_task_work.func = io_poll_task_func;
373-
374-
trace_io_uring_task_add(req, mask);
375-
376-
if (!(req->flags & REQ_F_POLL_NO_LAZY))
377-
flags = IOU_F_TWQ_LAZY_WAKE;
378-
__io_req_task_work_add(req, flags);
379-
}
380-
381-
static inline void io_poll_execute(struct io_kiocb *req, int res)
382-
{
383-
if (io_poll_get_ownership(req))
384-
__io_poll_execute(req, res);
385-
}
386-
387394
static void io_poll_cancel_req(struct io_kiocb *req)
388395
{
389396
io_poll_mark_cancelled(req);

io_uring/poll.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ struct async_poll {
2424
struct io_poll *double_poll;
2525
};
2626

27+
/*
28+
* Must only be called inside issue_flags & IO_URING_F_MULTISHOT, or
29+
* potentially other cases where we already "own" this poll request.
30+
*/
31+
static inline void io_poll_multishot_retry(struct io_kiocb *req)
32+
{
33+
atomic_inc(&req->poll_refs);
34+
}
35+
2736
int io_poll_add_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe);
2837
int io_poll_add(struct io_kiocb *req, unsigned int issue_flags);
2938

io_uring/rw.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "opdef.h"
1919
#include "kbuf.h"
2020
#include "rsrc.h"
21+
#include "poll.h"
2122
#include "rw.h"
2223

2324
struct io_rw {
@@ -962,8 +963,15 @@ int io_read_mshot(struct io_kiocb *req, unsigned int issue_flags)
962963
if (io_fill_cqe_req_aux(req,
963964
issue_flags & IO_URING_F_COMPLETE_DEFER,
964965
ret, cflags | IORING_CQE_F_MORE)) {
965-
if (issue_flags & IO_URING_F_MULTISHOT)
966+
if (issue_flags & IO_URING_F_MULTISHOT) {
967+
/*
968+
* Force retry, as we might have more data to
969+
* be read and otherwise it won't get retried
970+
* until (if ever) another poll is triggered.
971+
*/
972+
io_poll_multishot_retry(req);
966973
return IOU_ISSUE_SKIP_COMPLETE;
974+
}
967975
return -EAGAIN;
968976
}
969977
}

0 commit comments

Comments
 (0)