Remove aux CQE caches
Subsystems (3)
v3 cover Remove aux CQE caches
Mandate ctx locking for task_work. Then, remove aux CQE caches mostly used by multishot requests and post them directly into the CQ.
It's leaving some of the pre existing issue_flags and state conversion non conformant chunks, which will need to clean up later.
v3: pass IO_URING_F_COMPLETE_DEFER to the cmd cancellation path
drop patch moving request cancellation list removal to tw drop all other ublk changes
v2: Add Patch 3, which fixes deadlock due to nested locking
introduced in v1
Remove a fix, which was taken separately
Pile up more cleanups (Patches 12-14)
Pavel Begunkov (13):
io_uring/cmd: move io_uring_try_cancel_uring_cmd() io_uring/cmd: kill one issue_flags to tw conversion io_uring/cmd: fix tw <-> issue_flags conversion io_uring/cmd: introduce io_uring_cmd_complete nvme/io_uring: don't hard code IO_URING_F_UNLOCKED io_uring/rw: avoid punting to io-wq directly
io_uring: force tw ctx locking
io_uring: remove struct io_tw_state::locked
io_uring: refactor io_fill_cqe_req_aux
io_uring: get rid of intermediate aux cqe caches
io_uring: remove current check from complete_post
io_uring: refactor io_req_complete_post()
io_uring: clean up io_lockdep_assert_cq_locked
drivers/nvme/host/ioctl.c | 9 +- include/linux/io_uring/cmd.h | 28 +++++ include/linux/io_uring_types.h | 5 +- io_uring/io_uring.c | 198 ++++++++------------------------- io_uring/io_uring.h | 24 ++-- io_uring/net.c | 6 +- io_uring/poll.c | 3 +- io_uring/rw.c | 18 +-- io_uring/timeout.c | 8 +- io_uring/uring_cmd.c | 45 +++++++- io_uring/uring_cmd.h | 3 + io_uring/waitid.c | 2 +- 12 files changed, 143 insertions(+), 206 deletions(-)
Signature · 1 line
2.44.0
PATCH v3 1/13 io_uring/cmd: move io_uring_try_cancel_uring_cmd()
io_uring_try_cancel_uring_cmd() is a part of the cmd handling so let's move it closer to all cmd bits into uring_cmd.c
--- io_uring/io_uring.c | 39 +-------------------------------------- io_uring/io_uring.h | 7 +++++++ io_uring/uring_cmd.c | 30 ++++++++++++++++++++++++++++++ io_uring/uring_cmd.h | 3 +++ 4 files changed, 41 insertions(+), 38 deletions(-)
io_uring/io_uring.c +1 -38
@@ -95,6 +95,7 @@#include "waitid.h" #include "futex.h" #include "napi.h" +#include "uring_cmd.h" #include "timeout.h" #include "poll.h"
@@ -173,13 +174,6 @@ static struct ctl_table kernel_io_uring_disabled_table[] = { };#endif -static inline void io_submit_flush_completions (struct io_ring_ctx *ctx) -{ -if (!wq_list_empty (&ctx->submit_state.compl_reqs) || - ctx->submit_state.cqes_count) -__io_submit_flush_completions (ctx); -} -static inline unsigned int __io_cqring_events (struct io_ring_ctx *ctx) {return ctx->cached_cq_tail -READ_ONCE (ctx->rings->cq.head);
@@ -3237,37 +3231,6 @@ static __cold bool io_uring_try_cancel_iowq(struct io_ring_ctx *ctx)return ret; } -static bool io_uring_try_cancel_uring_cmd (struct io_ring_ctx *ctx, -struct task_struct *task,bool cancel_all) -{ -struct hlist_node *tmp; -struct io_kiocb *req; -bool ret = false; - -lockdep_assert_held (&ctx->uring_lock); - -hlist_for_each_entry_safe (req, tmp, &ctx->cancelable_uring_cmd, - hash_node) { -struct io_uring_cmd *cmd =io_kiocb_to_cmd (req, - struct io_uring_cmd); -struct file *file = req->file; - -if (!cancel_all && req->task != task) -continue ; - -if (cmd->flags &IORING_URING_CMD_CANCELABLE ) { -/* ->sqe isn't available if no async data */ -if (!req_has_async_data (req)) - cmd->sqe =NULL ; - file->f_op->uring_cmd (cmd,IO_URING_F_CANCEL ); - ret = true; - } - } -io_submit_flush_completions (ctx); - -return ret; -} -static __cold bool io_uring_try_cancel_requests (struct io_ring_ctx *ctx,struct task_struct *task,bool cancel_all)
io_uring/io_uring.h +7 -0
@@ -154,6 +154,13 @@ static inline void io_req_task_work_add(struct io_kiocb *req)__io_req_task_work_add (req,0 ); } +static inline void io_submit_flush_completions (struct io_ring_ctx *ctx) +{ +if (!wq_list_empty (&ctx->submit_state.compl_reqs) || + ctx->submit_state.cqes_count) +__io_submit_flush_completions (ctx); +} +#define io_for_each_link (pos, head) \ for (pos = (head); pos; pos = pos->link)
io_uring/uring_cmd.c +30 -0
@@ -14,6 +14,36 @@#include "rsrc.h" #include "uring_cmd.h" +bool io_uring_try_cancel_uring_cmd (struct io_ring_ctx *ctx, +struct task_struct *task,bool cancel_all) +{ +struct hlist_node *tmp; +struct io_kiocb *req; +bool ret = false; + +lockdep_assert_held (&ctx->uring_lock); + +hlist_for_each_entry_safe (req, tmp, &ctx->cancelable_uring_cmd, + hash_node) { +struct io_uring_cmd *cmd =io_kiocb_to_cmd (req, + struct io_uring_cmd); +struct file *file = req->file; + +if (!cancel_all && req->task != task) +continue ; + +if (cmd->flags &IORING_URING_CMD_CANCELABLE ) { +/* ->sqe isn't available if no async data */ +if (!req_has_async_data (req)) + cmd->sqe =NULL ; + file->f_op->uring_cmd (cmd,IO_URING_F_CANCEL ); + ret = true; + } + } +io_submit_flush_completions (ctx); +return ret; +} +static void io_uring_cmd_del_cancelable (struct io_uring_cmd *cmd,unsigned int issue_flags) {
io_uring/uring_cmd.h +3 -0
@@ -3,3 +3,6 @@int io_uring_cmd (struct io_kiocb *req,unsigned int issue_flags);int io_uring_cmd_prep (struct io_kiocb *req,const struct io_uring_sqe *sqe);int io_uring_cmd_prep_async (struct io_kiocb *req); + +bool io_uring_try_cancel_uring_cmd (struct io_ring_ctx *ctx, +struct task_struct *task,bool cancel_all);
Signature · 1 line
2.44.0
Re: [PATCH v3 01/13] io_uring/cmd: move io_uring_try_cancel_uring_cmd()
Quoted 4 lines · Pavel Begunkov
io_uring_try_cancel_uring_cmd()is a part of the cmd handling so let's move it closer to all cmd bits intouring_cmd.cSigned-off-by Pavel Begunkov <[email protected]>
Thanks
Ming
PATCH v3 2/13 io_uring/cmd: kill one issue_flags to tw conversion
In reply to Pavel Begunkovio_uring cmd converts struct io_tw_state to issue_flags and later back to io_tw_state, it's awfully ill-fated, not to mention that intermediate issue_flags state is not correct.
Get rid of the last conversion, drag through tw everything that came with IO_URING_F_UNLOCKED, and replace io_req_complete_defer() with a direct call to io_req_complete_defer(), at least for the time being.
--- io_uring/uring_cmd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
io_uring/uring_cmd.c +4 -4
@@ -130,11 +130,11 @@ void io_uring_cmd_done(struct io_uring_cmd *ioucmd, ssize_t ret, ssize_t res2,if (req->ctx->flags &IORING_SETUP_IOPOLL ) {/* order with io_iopoll_req_issued() checking ->iopoll_complete */ smp_store_release (&req->iopoll_completed,1 ); + }else if (!(issue_flags &IO_URING_F_UNLOCKED )) { +io_req_complete_defer (req); }else { -struct io_tw_state ts = { - .locked = !(issue_flags &IO_URING_F_UNLOCKED ), - }; -io_req_task_complete (req, &ts); + req->io_task_work.func = io_req_task_complete; +io_req_task_work_add (req); } }EXPORT_SYMBOL_GPL (io_uring_cmd_done);
Signature · 1 line
2.44.0
Re: [PATCH v3 02/13] io_uring/cmd: kill one issue_flags to tw conversion
Quoted 9 lines · Pavel Begunkov
io_uringcmd converts structio_tw_statetoissue_flagsand later back toio_tw_state, it's awfully ill-fated, not to mention that intermediateissue_flagsstate is not correct.Get rid of the last conversion, drag through tw everything that came with
IO_URING_F_UNLOCKED, and replaceio_req_complete_defer()with a direct call toio_req_complete_defer(), at least for the time being.Signed-off-by Pavel Begunkov <[email protected]>
Thanks,
Ming
PATCH v3 3/13 io_uring/cmd: fix tw <-> issue_flags conversion
In reply to Pavel Begunkov!IO_URING_F_UNLOCKED does not translate to availability of the deferred completion infra, IO_URING_F_COMPLETE_DEFER does, that what we should pass and look for to use io_req_complete_defer() and other variants.
Luckily, it's not a real problem as two wrongs actually made it right, at least as far as io_uring_cmd_work() goes.
--- io_uring/uring_cmd.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
io_uring/uring_cmd.c +10 -3
@@ -36,7 +36,8 @@ bool io_uring_try_cancel_uring_cmd(struct io_ring_ctx *ctx,/* ->sqe isn't available if no async data */ if (!req_has_async_data (req)) cmd->sqe =NULL ; - file->f_op->uring_cmd (cmd,IO_URING_F_CANCEL ); + file->f_op->uring_cmd (cmd,IO_URING_F_CANCEL | +IO_URING_F_COMPLETE_DEFER ); ret = true; } }
@@ -86,7 +87,11 @@ EXPORT_SYMBOL_GPL(io_uring_cmd_mark_cancelable);static void io_uring_cmd_work (struct io_kiocb *req,struct io_tw_state *ts) { struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req, struct io_uring_cmd); -unsigned issue_flags = ts->locked ?0 :IO_URING_F_UNLOCKED ; +unsigned issue_flags =IO_URING_F_UNLOCKED ; + +/* locked task_work executor checks the deffered list completion */ +if (ts->locked) + issue_flags =IO_URING_F_COMPLETE_DEFER ; ioucmd->task_work_cb (ioucmd, issue_flags); }
@@ -130,7 +135,9 @@ void io_uring_cmd_done(struct io_uring_cmd *ioucmd, ssize_t ret, ssize_t res2,if (req->ctx->flags &IORING_SETUP_IOPOLL ) {/* order with io_iopoll_req_issued() checking ->iopoll_complete */ smp_store_release (&req->iopoll_completed,1 ); - }else if (!(issue_flags &IO_URING_F_UNLOCKED )) { + }else if (issue_flags &IO_URING_F_COMPLETE_DEFER ) { +if (WARN_ON_ONCE (issue_flags &IO_URING_F_UNLOCKED )) +return ;io_req_complete_defer (req); }else { req->io_task_work.func = io_req_task_complete;
Signature · 1 line
2.44.0
Re: [PATCH v3 03/13] io_uring/cmd: fix tw <-> issue_flags conversion
Quoted 8 lines · Pavel Begunkov
!IO_URING_F_UNLOCKED does not translate to availability of the deferred completion infra,
IO_URING_F_COMPLETE_DEFERdoes, that what we should pass and look for to useio_req_complete_defer()and other variants.Luckily, it's not a real problem as two wrongs actually made it right, at least as far as
io_uring_cmd_work()goes.Signed-off-by Pavel Begunkov <[email protected]>
Thanks
Ming
PATCH v3 4/13 io_uring/cmd: introduce io_uring_cmd_complete
In reply to Pavel Begunkovio_uring_cmd_complete() does exactly what io_uring_cmd_done() does, that is completing the request, but doesn't ask for issue_flags argument. We have a couple of users hardcoding some random issue_flags values in drivers, which they absolutely should not do. This function will be used to get rid of them. Also, add comments warning users that they're only allowed to pass issue_flags that were given from io_uring.
--- include/linux/io_uring/cmd.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)
include/linux/io_uring/cmd.h +28 -0
@@ -26,12 +26,25 @@ static inline const void *io_uring_sqe_cmd(const struct io_uring_sqe *sqe)#if defined(CONFIG_IO_URING )int io_uring_cmd_import_fixed (u64 ubuf,unsigned long len,int rw,struct iov_iter *iter,void *ioucmd); + +/* +* Completes the request, i.e. posts an io_uring CQE and deallocates @ioucmd +* and the corresponding io_uring request. +* +* Note: the caller should never hard code @issue_flags and is only allowed +* to pass the mask provided by the core io_uring code. +*/ void io_uring_cmd_done (struct io_uring_cmd *cmd,ssize_t ret,ssize_t res2,unsigned issue_flags); +void __io_uring_cmd_do_in_task (struct io_uring_cmd *ioucmd,void (*task_work_cb)(struct io_uring_cmd *,unsigned ),unsigned flags); +/* +* Note: the caller should never hard code @issue_flags and only use the +* mask provided by the core io_uring code. +*/ void io_uring_cmd_mark_cancelable (struct io_uring_cmd *cmd,unsigned int issue_flags);
@@ -56,6 +69,21 @@ static inline void io_uring_cmd_mark_cancelable(struct io_uring_cmd *cmd, }#endif +/* +* Completes the request, i.e. posts an io_uring CQE and deallocates @ioucmd +* and the corresponding io_uring request. Similar to io_uring_cmd_done() but +* doesn't need issue_flags. +* +* Note, must not be used with cancellable requests. +*/ +static inline void io_uring_cmd_complete (struct io_uring_cmd *ioucmd, +ssize_t ret,ssize_t res2) +{ +if (WARN_ON_ONCE (ioucmd->flags &IORING_URING_CMD_CANCELABLE )) +return ; +io_uring_cmd_done (ioucmd, ret, res2,IO_URING_F_UNLOCKED ); +} +/* users must follow the IOU_F_TWQ_LAZY_WAKE semantics */ static inline void io_uring_cmd_do_in_task_lazy (struct io_uring_cmd *ioucmd,void (*task_work_cb)(struct io_uring_cmd *,unsigned ))
Signature · 1 line
2.44.0
Re: [PATCH v3 04/13] io_uring/cmd: introduce io_uring_cmd_complete
Quoted 8 lines · Pavel Begunkov
io_uring_cmd_complete()does exactly whatio_uring_cmd_done()does, that is completing the request, but doesn't ask forissue_flagsargument. We have a couple of users hardcoding some randomissue_flagsvalues in drivers, which they absolutely should not do. This function will be used to get rid of them. Also, add comments warning users that they're only allowed to passissue_flagsthat were given fromio_uring.Signed-off-by Pavel Begunkov <[email protected]>
Thanks
Ming
PATCH v3 5/13 nvme/io_uring: don't hard code IO_URING_F_UNLOCKED
In reply to Pavel Begunkovuring_cmd implementations should not try to guess issue_flags, use a freshly added helper io_uring_cmd_complete() instead.
--- drivers/nvme/host/ioctl.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/nvme/host/ioctl.c +6 -3
@@ -426,10 +426,13 @@ static enum rq_end_io_ret nvme_uring_cmd_end_io(struct request *req,* For iopoll, complete it directly. * Otherwise, move the completion to task work. */ -if (blk_rq_is_poll (req)) -nvme_uring_task_cb (ioucmd,IO_URING_F_UNLOCKED ); -else +if (blk_rq_is_poll (req)) { +if (pdu->bio) +blk_rq_unmap_user (pdu->bio); +io_uring_cmd_complete (ioucmd, pdu->status, pdu->result); + }else {io_uring_cmd_do_in_task_lazy (ioucmd, nvme_uring_task_cb); + }return RQ_END_IO_FREE ; }
Signature · 1 line
2.44.0
Re: [PATCH v3 05/13] nvme/io_uring: don't hard code IO_URING_F_UNLOCKED
Quoted 5 lines · Pavel Begunkov
uring_cmdimplementations should not try to guessissue_flags, use a freshly added helperio_uring_cmd_complete()instead.Reviewed-by Kanchan Joshi <[email protected]>Signed-off-by Pavel Begunkov <[email protected]>
Thanks,
Ming
PATCH v3 6/13 io_uring/rw: avoid punting to io-wq directly
In reply to Pavel Begunkovkiocb_done() should care to specifically redirecting requests to io-wq. Remove the hopping to tw to then queue an io-wq, return -EAGAIN and let the core code io_uring handle offloading.
--- io_uring/io_uring.c | 8 ++++---- io_uring/io_uring.h | 1 - io_uring/rw.c | 8 +------- 3 files changed, 5 insertions(+), 12 deletions(-)
io_uring/io_uring.c +4 -4
@@ -492,7 +492,7 @@ static void io_prep_async_link(struct io_kiocb *req) } } -void io_queue_iowq (struct io_kiocb *req,struct io_tw_state *ts_dont_use) +static void io_queue_iowq (struct io_kiocb *req) {struct io_kiocb *link =io_prep_linked_timeout (req);struct io_uring_task *tctx = req->task->io_uring;
@@ -1499,7 +1499,7 @@ void io_req_task_submit(struct io_kiocb *req, struct io_tw_state *ts)if (unlikely (req->task->flags &PF_EXITING ))io_req_defer_failed (req, -EFAULT );else if (req->flags &REQ_F_FORCE_ASYNC ) -io_queue_iowq (req, ts); +io_queue_iowq (req);else io_queue_sqe (req); }
@@ -2082,7 +2082,7 @@ static void io_queue_async(struct io_kiocb *req, int ret)break ;case IO_APOLL_ABORTED :io_kbuf_recycle (req,0 ); -io_queue_iowq (req,NULL ); +io_queue_iowq (req);break ;case IO_APOLL_OK :break ;
@@ -2129,7 +2129,7 @@ static void io_queue_sqe_fallback(struct io_kiocb *req)if (unlikely (req->ctx->drain_active))io_drain_req (req);else -io_queue_iowq (req,NULL ); +io_queue_iowq (req); } }
io_uring/io_uring.h +0 -1
@@ -79,7 +79,6 @@ struct file *io_file_get_fixed(struct io_kiocb *req, int fd,void __io_req_task_work_add (struct io_kiocb *req,unsigned flags);bool io_alloc_async_data (struct io_kiocb *req);void io_req_task_queue (struct io_kiocb *req); -void io_queue_iowq (struct io_kiocb *req,struct io_tw_state *ts_dont_use);void io_req_task_complete (struct io_kiocb *req,struct io_tw_state *ts);void io_req_task_queue_fail (struct io_kiocb *req,int ret);void io_req_task_submit (struct io_kiocb *req,struct io_tw_state *ts);
io_uring/rw.c +1 -7
@@ -187,12 +187,6 @@ static inline loff_t *io_kiocb_update_pos(struct io_kiocb *req)return NULL ; } -static void io_req_task_queue_reissue (struct io_kiocb *req) -{ - req->io_task_work.func = io_queue_iowq; -io_req_task_work_add (req); -} -#ifdef CONFIG_BLOCK static bool io_resubmit_prep (struct io_kiocb *req) {
@@ -405,7 +399,7 @@ static int kiocb_done(struct io_kiocb *req, ssize_t ret,if (req->flags &REQ_F_REISSUE ) { req->flags &= ~REQ_F_REISSUE ;if (io_resubmit_prep (req)) -io_req_task_queue_reissue (req); +return -EAGAIN ;else io_req_task_queue_fail (req, final_ret); }
Signature · 1 line
2.44.0
PATCH v3 7/13 io_uring: force tw ctx locking
In reply to Pavel BegunkovWe can run normal task_work without locking the ctx, however we try to lock anyway and most handlers prefer or require it locked. It might have been interesting to multi-submitter ring with high contention completing async read/write requests via task_work, however that will still need to go through io_req_complete_post() and potentially take the lock for rsrc node putting or some other case.
In other words, it's hard to care about it, so alawys force the locking. The case described would also because of various io_uring caches.
--- io_uring/io_uring.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-)
io_uring/io_uring.c +9 -12
@@ -1185,8 +1185,9 @@ struct llist_node *io_handle_tw_list(struct llist_node *node,if (req->ctx != ctx) {ctx_flush_and_put (ctx, &ts); ctx = req->ctx; -/* if not contended, grab and improve batching */ - ts.locked =mutex_trylock (&ctx->uring_lock); + + ts.locked = true; +mutex_lock (&ctx->uring_lock);percpu_ref_get (&ctx->refs); } INDIRECT_CALL_2(req->io_task_work.func,
@@ -1447,11 +1448,9 @@ static int __io_run_local_work(struct io_ring_ctx *ctx, struct io_tw_state *ts,if (io_run_local_work_continue (ctx, ret, min_events)) goto again; -if (ts->locked) { -io_submit_flush_completions (ctx); -if (io_run_local_work_continue (ctx, ret, min_events)) - goto again; - } +io_submit_flush_completions (ctx); +if (io_run_local_work_continue (ctx, ret, min_events)) + goto again;trace_io_uring_local_work_run (ctx, ret, loops);return ret;
@@ -1475,14 +1474,12 @@ static inline int io_run_local_work_locked(struct io_ring_ctx *ctx,static int io_run_local_work (struct io_ring_ctx *ctx,int min_events) { -struct io_tw_state ts = {}; +struct io_tw_state ts = { .locked = true };int ret; - ts.locked =mutex_trylock (&ctx->uring_lock); +mutex_lock (&ctx->uring_lock); ret =__io_run_local_work (ctx, &ts, min_events); -if (ts.locked) -mutex_unlock (&ctx->uring_lock); - +mutex_unlock (&ctx->uring_lock);return ret; }
Signature · 1 line
2.44.0
PATCH v3 8/13 io_uring: remove struct io_tw_state::locked
In reply to Pavel Begunkovctx is always locked for task_work now, so get rid of struct io_tw_state::locked. Note I'm stopping one step before removing io_tw_state altogether, which is not empty, because it still serves the purpose of indicating which function is a tw callback and forcing users not to invoke them carelessly out of a wrong context. The removal can always be done later.
--- include/linux/io_uring_types.h | 2 -- io_uring/io_uring.c | 31 ++++++++----------------------- io_uring/io_uring.h | 5 +---- io_uring/poll.c | 2 +- io_uring/rw.c | 6 ++---- io_uring/timeout.c | 8 ++------ io_uring/uring_cmd.c | 8 ++------ io_uring/waitid.c | 2 +- 8 files changed, 17 insertions(+), 47 deletions(-)
include/linux/io_uring_types.h +0 -2
@@ -439,8 +439,6 @@ struct io_ring_ctx { };struct io_tw_state { -/* ->uring_lock is taken, callbacks can use io_tw_lock to lock it */ -bool locked; };enum {
io_uring/io_uring.c +8 -23
@@ -246,14 +246,12 @@ static __cold void io_fallback_req_func(struct work_struct *work) fallback_work.work);struct llist_node *node =llist_del_all (&ctx->fallback_llist);struct io_kiocb *req, *tmp; -struct io_tw_state ts = { .locked = true, }; +struct io_tw_state ts = {};percpu_ref_get (&ctx->refs);mutex_lock (&ctx->uring_lock);llist_for_each_entry_safe (req, tmp, node, io_task_work.node) req->io_task_work.func (req, &ts); -if (WARN_ON_ONCE (!ts.locked)) -return ;io_submit_flush_completions (ctx);mutex_unlock (&ctx->uring_lock);percpu_ref_put (&ctx->refs);
@@ -1157,11 +1155,9 @@ static void ctx_flush_and_put(struct io_ring_ctx *ctx, struct io_tw_state *ts)return ;if (ctx->flags &IORING_SETUP_TASKRUN_FLAG )atomic_andnot (IORING_SQ_TASKRUN , &ctx->rings->sq_flags); -if (ts->locked) { -io_submit_flush_completions (ctx); -mutex_unlock (&ctx->uring_lock); - ts->locked = false; - } + +io_submit_flush_completions (ctx); +mutex_unlock (&ctx->uring_lock);percpu_ref_put (&ctx->refs); }
@@ -1185,8 +1181,6 @@ struct llist_node *io_handle_tw_list(struct llist_node *node,if (req->ctx != ctx) {ctx_flush_and_put (ctx, &ts); ctx = req->ctx; - - ts.locked = true;mutex_lock (&ctx->uring_lock);percpu_ref_get (&ctx->refs); }
@@ -1459,22 +1453,16 @@ static int __io_run_local_work(struct io_ring_ctx *ctx, struct io_tw_state *ts,static inline int io_run_local_work_locked (struct io_ring_ctx *ctx,int min_events) { -struct io_tw_state ts = { .locked = true, }; -int ret; +struct io_tw_state ts = {};if (llist_empty (&ctx->work_llist))return 0 ; - - ret =__io_run_local_work (ctx, &ts, min_events); -/* shouldn't happen! */ -if (WARN_ON_ONCE (!ts.locked)) -mutex_lock (&ctx->uring_lock); -return ret; +return __io_run_local_work (ctx, &ts, min_events); }static int io_run_local_work (struct io_ring_ctx *ctx,int min_events) { -struct io_tw_state ts = { .locked = true }; +struct io_tw_state ts = {};int ret;mutex_lock (&ctx->uring_lock);
@@ -1702,10 +1690,7 @@ static int io_iopoll_check(struct io_ring_ctx *ctx, long min)void io_req_task_complete (struct io_kiocb *req,struct io_tw_state *ts) { -if (ts->locked) -io_req_complete_defer (req); -else -io_req_complete_post (req,IO_URING_F_UNLOCKED ); +io_req_complete_defer (req); }/*
io_uring/io_uring.h +1 -4
@@ -351,10 +351,7 @@ static inline bool io_task_work_pending(struct io_ring_ctx *ctx)static inline void io_tw_lock (struct io_ring_ctx *ctx,struct io_tw_state *ts) { -if (!ts->locked) { -mutex_lock (&ctx->uring_lock); - ts->locked = true; - } +lockdep_assert_held (&ctx->uring_lock); }/*
io_uring/poll.c +1 -1
@@ -322,7 +322,7 @@ static int io_poll_check_events(struct io_kiocb *req, struct io_tw_state *ts)__poll_t mask =mangle_poll (req->cqe.res & req->apoll_events); -if (!io_fill_cqe_req_aux (req, ts->locked, mask, +if (!io_fill_cqe_req_aux (req, true, mask,IORING_CQE_F_MORE )) {io_req_set_res (req, mask,0 );return IOU_POLL_REMOVE_POLL_USE_RES ;
io_uring/rw.c +2 -4
@@ -305,11 +305,9 @@ void io_req_rw_complete(struct io_kiocb *req, struct io_tw_state *ts)io_req_io_end (req); -if (req->flags & (REQ_F_BUFFER_SELECTED |REQ_F_BUFFER_RING )) { -unsigned issue_flags = ts->locked ?0 :IO_URING_F_UNLOCKED ; +if (req->flags & (REQ_F_BUFFER_SELECTED |REQ_F_BUFFER_RING )) + req->cqe.flags |=io_put_kbuf (req,0 ); - req->cqe.flags |=io_put_kbuf (req, issue_flags); - }io_req_task_complete (req, ts); }
io_uring/timeout.c +2 -6
@@ -72,10 +72,7 @@ static void io_timeout_complete(struct io_kiocb *req, struct io_tw_state *ts)struct io_ring_ctx *ctx = req->ctx;if (!io_timeout_finish (timeout, data)) { -bool filled; - filled =io_fill_cqe_req_aux (req, ts->locked, -ETIME , -IORING_CQE_F_MORE ); -if (filled) { +if (io_fill_cqe_req_aux (req, true, -ETIME ,IORING_CQE_F_MORE )) {/* re-arm timer */ spin_lock_irq (&ctx->timeout_lock);list_add (&timeout->list, ctx->timeout_list.prev);
@@ -301,7 +298,6 @@ int io_timeout_cancel(struct io_ring_ctx *ctx, struct io_cancel_data *cd)static void io_req_task_link_timeout (struct io_kiocb *req,struct io_tw_state *ts) { -unsigned issue_flags = ts->locked ?0 :IO_URING_F_UNLOCKED ; struct io_timeout *timeout = io_kiocb_to_cmd(req, struct io_timeout);struct io_kiocb *prev = timeout->prev;int ret = -ENOENT ;
@@ -313,7 +309,7 @@ static void io_req_task_link_timeout(struct io_kiocb *req, struct io_tw_state *t .data = prev->cqe.user_data, }; - ret =io_try_cancel (req->task->io_uring, &cd, issue_flags); + ret =io_try_cancel (req->task->io_uring, &cd,0 ); }io_req_set_res (req, ret ?: -ETIME ,0 );io_req_task_complete (req, ts);
io_uring/uring_cmd.c +2 -6
@@ -87,13 +87,9 @@ EXPORT_SYMBOL_GPL(io_uring_cmd_mark_cancelable);static void io_uring_cmd_work (struct io_kiocb *req,struct io_tw_state *ts) { struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req, struct io_uring_cmd); -unsigned issue_flags =IO_URING_F_UNLOCKED ; -/* locked task_work executor checks the deffered list completion */ -if (ts->locked) - issue_flags =IO_URING_F_COMPLETE_DEFER ; - - ioucmd->task_work_cb (ioucmd, issue_flags); +/* task_work executor checks the deffered list completion */ + ioucmd->task_work_cb (ioucmd,IO_URING_F_COMPLETE_DEFER ); } void __io_uring_cmd_do_in_task(struct io_uring_cmd *ioucmd,
io_uring/waitid.c +1 -1
@@ -118,7 +118,7 @@ static int io_waitid_finish(struct io_kiocb *req, int ret)static void io_waitid_complete (struct io_kiocb *req,int ret) { struct io_waitid *iw = io_kiocb_to_cmd(req, struct io_waitid); -struct io_tw_state ts = { .locked = true }; +struct io_tw_state ts = {};/* anyone completing better be holding a reference */ WARN_ON_ONCE (!(atomic_read (&iw->refs) &IO_WAITID_REF_MASK ));
Signature · 1 line
2.44.0
PATCH v3 9/13 io_uring: refactor io_fill_cqe_req_aux
In reply to Pavel BegunkovThe restriction on multishot execution context disallowing io-wq is driven by rules of io_fill_cqe_req_aux(), it should only be called in the master task context, either from the syscall path or in task_work. Since task_work now always takes the ctx lock implying IO_URING_F_COMPLETE_DEFER, we can just assume that the function is always called with its defer argument set to true.
Kill the argument. Also rename the function for more consistency as "fill" in CQE related functions was usually meant for raw interfaces only copying data into the CQ without any locking, waking the user and other accounting "post" functions take care of.
--- io_uring/io_uring.c | 16 +++------------- io_uring/io_uring.h | 2 +- io_uring/net.c | 6 ++---- io_uring/poll.c | 3 +-- io_uring/rw.c | 4 +--- io_uring/timeout.c | 2 +- 6 files changed, 9 insertions(+), 24 deletions(-)
io_uring/io_uring.c +3 -13
@@ -907,40 +907,30 @@ static void __io_flush_post_cqes(struct io_ring_ctx *ctx) state->cqes_count =0 ; } -static bool __io_post_aux_cqe (struct io_ring_ctx *ctx,u64 user_data,s32 res,u32 cflags, -bool allow_overflow) +bool io_post_aux_cqe (struct io_ring_ctx *ctx,u64 user_data,s32 res,u32 cflags) {bool filled;io_cq_lock (ctx); filled =io_fill_cqe_aux (ctx, user_data, res, cflags); -if (!filled && allow_overflow) +if (!filled) filled =io_cqring_event_overflow (ctx, user_data, res, cflags,0 ,0 );io_cq_unlock_post (ctx);return filled; } -bool io_post_aux_cqe (struct io_ring_ctx *ctx,u64 user_data,s32 res,u32 cflags) -{ -return __io_post_aux_cqe (ctx, user_data, res, cflags, true); -} -/* * A helper for multishot requests posting additional CQEs. * Should only be used from a task_work including IO_URING_F_MULTISHOT. */ -bool io_fill_cqe_req_aux (struct io_kiocb *req,bool defer,s32 res,u32 cflags) +bool io_req_post_cqe (struct io_kiocb *req,s32 res,u32 cflags) {struct io_ring_ctx *ctx = req->ctx;u64 user_data = req->cqe.user_data;struct io_uring_cqe *cqe;lockdep_assert (!io_wq_current_is_worker ()); - -if (!defer) -return __io_post_aux_cqe (ctx, user_data, res, cflags, false); -lockdep_assert_held (&ctx->uring_lock);if (ctx->submit_state.cqes_count ==ARRAY_SIZE (ctx->completion_cqes)) {
io_uring/io_uring.h +1 -1
@@ -67,7 +67,7 @@ int io_run_task_work_sig(struct io_ring_ctx *ctx);void io_req_defer_failed (struct io_kiocb *req,s32 res);void io_req_complete_post (struct io_kiocb *req,unsigned issue_flags);bool io_post_aux_cqe (struct io_ring_ctx *ctx,u64 user_data,s32 res,u32 cflags); -bool io_fill_cqe_req_aux (struct io_kiocb *req,bool defer,s32 res,u32 cflags); +bool io_req_post_cqe (struct io_kiocb *req,s32 res,u32 cflags);void __io_commit_cqring_flush (struct io_ring_ctx *ctx);struct page **io_pin_pages (unsigned long ubuf,unsigned long len,int *npages);
io_uring/net.c +2 -4
@@ -706,8 +706,7 @@ static inline bool io_recv_finish(struct io_kiocb *req, int *ret,* receive from this socket. */ if ((req->flags &REQ_F_APOLL_MULTISHOT ) && !mshot_finished && -io_fill_cqe_req_aux (req, issue_flags &IO_URING_F_COMPLETE_DEFER , - *ret, cflags |IORING_CQE_F_MORE )) { +io_req_post_cqe (req, *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 ;
@@ -1428,8 +1427,7 @@ int io_accept(struct io_kiocb *req, unsigned int issue_flags)if (ret <0 )return ret; -if (io_fill_cqe_req_aux (req, issue_flags &IO_URING_F_COMPLETE_DEFER , - ret,IORING_CQE_F_MORE )) +if (io_req_post_cqe (req, ret,IORING_CQE_F_MORE )) goto retry;io_req_set_res (req, ret,0 );
io_uring/poll.c +1 -2
@@ -322,8 +322,7 @@ static int io_poll_check_events(struct io_kiocb *req, struct io_tw_state *ts)__poll_t mask =mangle_poll (req->cqe.res & req->apoll_events); -if (!io_fill_cqe_req_aux (req, true, mask, -IORING_CQE_F_MORE )) { +if (!io_req_post_cqe (req, mask,IORING_CQE_F_MORE )) {io_req_set_res (req, mask,0 );return IOU_POLL_REMOVE_POLL_USE_RES ; }
io_uring/rw.c +1 -3
@@ -955,9 +955,7 @@ int io_read_mshot(struct io_kiocb *req, unsigned int issue_flags) cflags =io_put_kbuf (req, issue_flags); rw->len =0 ;/* similarly to above, reset len to 0 */ -if (io_fill_cqe_req_aux (req, - issue_flags &IO_URING_F_COMPLETE_DEFER , - ret, cflags |IORING_CQE_F_MORE )) { +if (io_req_post_cqe (req, ret, cflags |IORING_CQE_F_MORE )) {if (issue_flags &IO_URING_F_MULTISHOT ) {/* * Force retry, as we might have more data to
io_uring/timeout.c +1 -1
@@ -72,7 +72,7 @@ static void io_timeout_complete(struct io_kiocb *req, struct io_tw_state *ts)struct io_ring_ctx *ctx = req->ctx;if (!io_timeout_finish (timeout, data)) { -if (io_fill_cqe_req_aux (req, true, -ETIME ,IORING_CQE_F_MORE )) { +if (io_req_post_cqe (req, -ETIME ,IORING_CQE_F_MORE )) {/* re-arm timer */ spin_lock_irq (&ctx->timeout_lock);list_add (&timeout->list, ctx->timeout_list.prev);
Signature · 1 line
2.44.0
PATCH v3 10/13 io_uring: get rid of intermediate aux cqe caches
In reply to Pavel Begunkovio_post_aux_cqe(), which is used for multishot requests, delays completions by putting CQEs into a temporary array for the purpose completion lock/flush batching.
DEFER_TASKRUN doesn't need any locking, so for it we can put completions directly into the CQ and defer post completion handling with a flag. That leaves !DEFER_TASKRUN, which is not that interesting / hot for multishot requests, so have conditional locking with deferred flush for them.
--- include/linux/io_uring_types.h | 3 +- io_uring/io_uring.c | 62 +++++++--------------------------- io_uring/io_uring.h | 2 +- 3 files changed, 15 insertions(+), 52 deletions(-)
include/linux/io_uring_types.h +1 -2
@@ -205,6 +205,7 @@ struct io_submit_state {bool plug_started;bool need_plug; +bool cq_flush;unsigned short submit_nr;unsigned int cqes_count;struct blk_plug plug;
@@ -342,8 +343,6 @@ struct io_ring_ctx {unsigned cq_last_tm_flush; }____cacheline_aligned_in_smp ; -struct io_uring_cqe completion_cqes[16 ]; -spinlock_t completion_lock;/* IRQ completion list, under ->completion_lock */
io_uring/io_uring.c +13 -49
96 lines not on this page · raw message
io_uring/io_uring.h +1 -1
@@ -156,7 +156,7 @@ static inline void io_req_task_work_add(struct io_kiocb *req)static inline void io_submit_flush_completions (struct io_ring_ctx *ctx) {if (!wq_list_empty (&ctx->submit_state.compl_reqs) || - ctx->submit_state.cqes_count) + ctx->submit_state.cq_flush)__io_submit_flush_completions (ctx); }
Signature · 1 line
2.44.0
PATCH v3 11/13 io_uring: remove current check from complete_post
In reply to Pavel Begunkovtask_work execution is now always locked, and we shouldn't get into io_req_complete_post() from them. That means that complete_post() is always called out of the original task context and we don't even need to check current.
--- io_uring/io_uring.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
io_uring/io_uring.c +1 -1
@@ -972,7 +972,7 @@ void io_req_complete_post(struct io_kiocb *req, unsigned issue_flags) {struct io_ring_ctx *ctx = req->ctx; -if (ctx->task_complete && ctx->submitter_task != current) { +if (ctx->task_complete) { req->io_task_work.func = io_req_task_complete;io_req_task_work_add (req); } else if (!(issue_flags & IO_URING_F_UNLOCKED) ||
Signature · 1 line
2.44.0
PATCH v3 12/13 io_uring: refactor io_req_complete_post()
In reply to Pavel BegunkovMake io_req_complete_post() to push all IORING_SETUP_IOPOLL requests to task_work, it's much cleaner and should normally happen. We couldn't do it before because there was a possibility of looping in
complete_post() -> tw -> complete_post() -> ...
Also, unexport the function and inline __io_req_complete_post().
--- io_uring/io_uring.c | 29 +++++++++++------------------ io_uring/io_uring.h | 1 - 2 files changed, 11 insertions(+), 19 deletions(-)
io_uring/io_uring.c +11 -18
@@ -920,11 +920,21 @@ bool io_req_post_cqe(struct io_kiocb *req, s32 res, u32 cflags)return posted; } -static void __io_req_complete_post (struct io_kiocb *req,unsigned issue_flags) +static void io_req_complete_post (struct io_kiocb *req,unsigned issue_flags) {struct io_ring_ctx *ctx = req->ctx;struct io_rsrc_node *rsrc_node =NULL ; +/* +* Handle special CQ sync cases via task_work. DEFER_TASKRUN requires +* the submitter task context, IOPOLL protects with uring_lock. +*/ +if (ctx->task_complete || (ctx->flags &IORING_SETUP_IOPOLL )) { + req->io_task_work.func = io_req_task_complete; +io_req_task_work_add (req); +return ; + } +io_cq_lock (ctx);if (!(req->flags &REQ_F_CQE_SKIP )) {if (!io_fill_cqe_req (ctx, req))
@@ -968,23 +978,6 @@ static void __io_req_complete_post(struct io_kiocb *req, unsigned issue_flags) } } -void io_req_complete_post (struct io_kiocb *req,unsigned issue_flags) -{ -struct io_ring_ctx *ctx = req->ctx; - -if (ctx->task_complete) { - req->io_task_work.func = io_req_task_complete; -io_req_task_work_add (req); - }else if (!(issue_flags &IO_URING_F_UNLOCKED ) || - !(ctx->flags &IORING_SETUP_IOPOLL )) { -__io_req_complete_post (req, issue_flags); - }else { -mutex_lock (&ctx->uring_lock); -__io_req_complete_post (req, issue_flags & ~IO_URING_F_UNLOCKED ); -mutex_unlock (&ctx->uring_lock); - } -} -void io_req_defer_failed (struct io_kiocb *req,s32 res) __must_hold(&ctx->uring_lock) {
io_uring/io_uring.h +0 -1
@@ -65,7 +65,6 @@ bool io_cqe_cache_refill(struct io_ring_ctx *ctx, bool overflow);void io_req_cqe_overflow (struct io_kiocb *req);int io_run_task_work_sig (struct io_ring_ctx *ctx);void io_req_defer_failed (struct io_kiocb *req,s32 res); -void io_req_complete_post (struct io_kiocb *req,unsigned issue_flags);bool io_post_aux_cqe (struct io_ring_ctx *ctx,u64 user_data,s32 res,u32 cflags);bool io_req_post_cqe (struct io_kiocb *req,s32 res,u32 cflags);void __io_commit_cqring_flush (struct io_ring_ctx *ctx);
Signature · 1 line
2.44.0
PATCH v3 13/13 io_uring: clean up io_lockdep_assert_cq_locked
In reply to Pavel BegunkovMove CONFIG_PROVE_LOCKING checks inside of io_lockdep_assert_cq_locked() and kill the else branch.
--- io_uring/io_uring.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-)
io_uring/io_uring.h +2 -6
@@ -119,9 +119,9 @@ enum {void io_eventfd_ops (struct rcu_head *rcu);void io_activate_pollwq (struct io_ring_ctx *ctx); -#if defined(CONFIG_PROVE_LOCKING )static inline void io_lockdep_assert_cq_locked (struct io_ring_ctx *ctx) { +#if defined(CONFIG_PROVE_LOCKING )lockdep_assert (in_task ());if (ctx->flags &IORING_SETUP_IOPOLL ) {
@@ -140,12 +140,8 @@ static inline void io_lockdep_assert_cq_locked(struct io_ring_ctx *ctx) else lockdep_assert(current == ctx->submitter_task); } -} -#else -static inline void io_lockdep_assert_cq_locked (struct io_ring_ctx *ctx) -{ -}#endif +}static inline void io_req_task_work_add (struct io_kiocb *req) {
Signature · 1 line
2.44.0
Quoted 9 lines · Pavel Begunkov
Mandate ctx locking for
task_work. Then, remove aux CQE caches mostly used by multishot requests and post them directly into the CQ.It's leaving some of the pre existing
issue_flagsand state conversion non conformant chunks, which will need to clean up later.v3: pass
IO_URING_F_COMPLETE_DEFERto the cmd cancellation path
drop patch moving request cancellation list removal to tw drop all other ublk changes
For the series, pass ublksrv test.
Thanks,
Ming
Quoted 11 lines · Pavel Begunkov
Mandate ctx locking for
task_work. Then, remove aux CQE caches mostly used by multishot requests and post them directly into the CQ.It's leaving some of the pre existing
issue_flagsand state conversion non conformant chunks, which will need to clean up later.v3: pass
IO_URING_F_COMPLETE_DEFERto the cmd cancellation path
drop patch moving request cancellation list removal to tw drop all other ublk changes[...]
Applied, thanks!
[01/13] io_uring/cmd: move io_uring_try_cancel_uring_cmd()
commit: c877857e86396576260d12eaae2f777fa4fd835f
[02/13] io_uring/cmd: kill one issue_flags to tw conversion
commit: c6740905f9862b2780cc9a9a3e1714ea153d6c74
[03/13] io_uring/cmd: fix tw <-> issue_flags conversion
commit: 17c8e3f66f16256d33a10555d0a63d64405ab046
[04/13] io_uring/cmd: introduce io_uring_cmd_complete
commit: 23b0bed538ac9b73518c670925ebb64f0239b54f
[05/13] nvme/io_uring: don't hard code IO_URING_F_UNLOCKED
commit: 1a587b0d65d09d61bd8f0db728923ab68d8fb9c2
[06/13] io_uring/rw: avoid punting to io-wq directly
commit: 7f3b8125c3aee86b5dea06d9a9738b16aa55cbdd
[07/13] io_uring: force tw ctx locking
commit: 2a475207f98db597043251be32bb8f16d3617af9
[08/13] io_uring: remove struct io_tw_state::locked
commit: ccb464aeb6e563d1df179aacbb7c514369ceb8f0
[09/13] io_uring: refactor io_fill_cqe_req_aux
commit: 39c25ce47d211f4decc47f09f9561b8630aab84e
[10/13] io_uring: get rid of intermediate aux cqe caches
commit: 1a7520889e02de50c8334e215a3f187ff9a92456
[11/13] io_uring: remove current check from complete_post
commit: 7af89eaee7d17c2f15e483c859d4fcc09dda6dce
[12/13] io_uring: refactor io_req_complete_post()
commit: 838070b49a0b1466156661b85f6c97dc4033902b
[13/13] io_uring: clean up io_lockdep_assert_cq_locked
commit: 3cba2895da4a77b5f555f29821487db42f084324Best regards,
Signature · 1 line
Jens Axboe