| From c79f52f0656eeb3e4a12f7f358f760077ae111b6 Mon Sep 17 00:00:00 2001 |
| From: Jens Axboe <axboe@kernel.dk> |
| Date: Sat, 27 Jan 2024 13:44:58 -0700 |
| Subject: io_uring/rw: ensure poll based multishot read retries appropriately |
| |
| From: Jens Axboe <axboe@kernel.dk> |
| |
| commit c79f52f0656eeb3e4a12f7f358f760077ae111b6 upstream. |
| |
| io_read_mshot() always relies on poll triggering retries, and this works |
| fine as long as we do a retry per size of the buffer being read. The |
| buffer size is given by the size of the buffer(s) in the given buffer |
| group ID. |
| |
| But if we're reading less than what is available, then we don't always |
| get to read everything that is available. For example, if the buffers |
| available are 32 bytes and we have 64 bytes to read, then we'll |
| correctly read the first 32 bytes and then wait for another poll trigger |
| before we attempt the next read. This next poll trigger may never |
| happen, in which case we just sit forever and never make progress, or it |
| may trigger at some point in the future, and now we're just delivering |
| the available data much later than we should have. |
| |
| io_read_mshot() could do retries itself, but that is wasteful as we'll |
| be going through all of __io_read() again, and most likely in vain. |
| Rather than do that, bump our poll reference count and have |
| io_poll_check_events() do one more loop and check with vfs_poll() if we |
| have more data to read. If we do, io_read_mshot() will get invoked again |
| directly and we'll read the next chunk. |
| |
| io_poll_multishot_retry() must only get called from inside |
| io_poll_issue(), which is our multishot retry handler, as we know we |
| already "own" the request at this point. |
| |
| Cc: stable@vger.kernel.org |
| Link: https://github.com/axboe/liburing/issues/1041 |
| Fixes: fc68fcda0491 ("io_uring/rw: add support for IORING_OP_READ_MULTISHOT") |
| Signed-off-by: Jens Axboe <axboe@kernel.dk> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| io_uring/poll.h | 9 +++++++++ |
| io_uring/rw.c | 10 +++++++++- |
| 2 files changed, 18 insertions(+), 1 deletion(-) |
| |
| --- a/io_uring/poll.h |
| +++ b/io_uring/poll.h |
| @@ -24,6 +24,15 @@ struct async_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(struct io_kiocb *req, unsigned int issue_flags); |
| |
| --- a/io_uring/rw.c |
| +++ b/io_uring/rw.c |
| @@ -18,6 +18,7 @@ |
| #include "opdef.h" |
| #include "kbuf.h" |
| #include "rsrc.h" |
| +#include "poll.h" |
| #include "rw.h" |
| |
| struct io_rw { |
| @@ -956,8 +957,15 @@ int io_read_mshot(struct io_kiocb *req, |
| if (io_fill_cqe_req_aux(req, |
| issue_flags & IO_URING_F_COMPLETE_DEFER, |
| 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 -EAGAIN; |
| } |
| } |