From: Pavel Begunkov <asml.silence@xxxxxxxxx> commit 674ee8e1b4a41d2fdffc885c55350c3fbb38c22a upstream. As io_remove_next_linked() is now under ->timeout_lock (see io_link_timeout_fn), we should update locking around io_for_each_link() and io_match_task() to use the new lock. Cc: stable@xxxxxxxxxx # 5.15+ Fixes: 89850fce16a1a ("io_uring: run timeouts from task_work") Signed-off-by: Pavel Begunkov <asml.silence@xxxxxxxxx> Link: https://lore.kernel.org/r/b54541cedf7de59cb5ae36109e58529ca16e66aa.1637631883.git.asml.silence@xxxxxxxxx Signed-off-by: Jens Axboe <axboe@xxxxxxxxx> Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx> --- fs/io_uring.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -1430,10 +1430,10 @@ static void io_prep_async_link(struct io if (req->flags & REQ_F_LINK_TIMEOUT) { struct io_ring_ctx *ctx = req->ctx; - spin_lock(&ctx->completion_lock); + spin_lock_irq(&ctx->timeout_lock); io_for_each_link(cur, req) io_prep_async_work(cur); - spin_unlock(&ctx->completion_lock); + spin_unlock_irq(&ctx->timeout_lock); } else { io_for_each_link(cur, req) io_prep_async_work(cur); @@ -5697,6 +5697,7 @@ static bool io_poll_remove_all(struct io int posted = 0, i; spin_lock(&ctx->completion_lock); + spin_lock_irq(&ctx->timeout_lock); for (i = 0; i < (1U << ctx->cancel_hash_bits); i++) { struct hlist_head *list; @@ -5706,6 +5707,7 @@ static bool io_poll_remove_all(struct io posted += io_poll_remove_one(req); } } + spin_unlock_irq(&ctx->timeout_lock); spin_unlock(&ctx->completion_lock); if (posted) @@ -9523,9 +9525,9 @@ static bool io_cancel_task_cb(struct io_ struct io_ring_ctx *ctx = req->ctx; /* protect against races with linked timeouts */ - spin_lock(&ctx->completion_lock); + spin_lock_irq(&ctx->timeout_lock); ret = io_match_task(req, cancel->task, cancel->all); - spin_unlock(&ctx->completion_lock); + spin_unlock_irq(&ctx->timeout_lock); } else { ret = io_match_task(req, cancel->task, cancel->all); } @@ -9539,12 +9541,14 @@ static bool io_cancel_defer_files(struct LIST_HEAD(list); spin_lock(&ctx->completion_lock); + spin_lock_irq(&ctx->timeout_lock); list_for_each_entry_reverse(de, &ctx->defer_list, list) { if (io_match_task(de->req, task, cancel_all)) { list_cut_position(&list, &ctx->defer_list, &de->list); break; } } + spin_unlock_irq(&ctx->timeout_lock); spin_unlock(&ctx->completion_lock); if (list_empty(&list)) return false;