io_uring: revise completion_lock locking
authorPavel Begunkov <asml.silence@gmail.com>
Fri, 2 Dec 2022 17:47:23 +0000 (17:47 +0000)
committerJens Axboe <axboe@kernel.dk>
Wed, 14 Dec 2022 15:53:04 +0000 (08:53 -0700)
io_kill_timeouts() doesn't post any events but queues everything to
task_work. Locking there is needed for protecting linked requests
traversing, we should grab completion_lock directly instead of using
io_cq_[un]lock helpers. Same goes for __io_req_find_next_prep().

Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
Link: https://lore.kernel.org/r/88e75d481a65dc295cb59722bb1cf76402d1c06b.1670002973.git.asml.silence@gmail.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
io_uring/io_uring.c
io_uring/io_uring.h
io_uring/timeout.c

index b521186efa5c75ac0e0d49c63c0a555c032dcf86..698c54f951eaf8ea6b5d8bf81a5eab8bafe01f47 100644 (file)
@@ -597,6 +597,18 @@ static inline void __io_cq_unlock(struct io_ring_ctx *ctx)
                spin_unlock(&ctx->completion_lock);
 }
 
+static inline void io_cq_lock(struct io_ring_ctx *ctx)
+       __acquires(ctx->completion_lock)
+{
+       spin_lock(&ctx->completion_lock);
+}
+
+static inline void io_cq_unlock(struct io_ring_ctx *ctx)
+       __releases(ctx->completion_lock)
+{
+       spin_unlock(&ctx->completion_lock);
+}
+
 /* keep it inlined for io_submit_flush_completions() */
 static inline void __io_cq_unlock_post(struct io_ring_ctx *ctx)
        __releases(ctx->completion_lock)
@@ -1074,9 +1086,9 @@ static void __io_req_find_next_prep(struct io_kiocb *req)
 {
        struct io_ring_ctx *ctx = req->ctx;
 
-       io_cq_lock(ctx);
+       spin_lock(&ctx->completion_lock);
        io_disarm_next(req);
-       io_cq_unlock_post(ctx);
+       spin_unlock(&ctx->completion_lock);
 }
 
 static inline struct io_kiocb *io_req_find_next(struct io_kiocb *req)
index 1b2f0b2cc888c37f3984989ba22a1b9458e71f6a..c117e029c8dccfd0cd12aa664cfc49992ad61e6b 100644 (file)
@@ -87,17 +87,6 @@ static inline void io_req_task_work_add(struct io_kiocb *req)
 #define io_for_each_link(pos, head) \
        for (pos = (head); pos; pos = pos->link)
 
-static inline void io_cq_lock(struct io_ring_ctx *ctx)
-       __acquires(ctx->completion_lock)
-{
-       spin_lock(&ctx->completion_lock);
-}
-
-static inline void io_cq_unlock(struct io_ring_ctx *ctx)
-{
-       spin_unlock(&ctx->completion_lock);
-}
-
 void io_cq_unlock_post(struct io_ring_ctx *ctx);
 
 static inline struct io_uring_cqe *io_get_cqe_overflow(struct io_ring_ctx *ctx,
index 4c6a5666541cf7d4cdf71b92c646581df8264bb7..eae005b2d1d2139853b881ff9e778911f4570de2 100644 (file)
@@ -624,7 +624,11 @@ __cold bool io_kill_timeouts(struct io_ring_ctx *ctx, struct task_struct *tsk,
        struct io_timeout *timeout, *tmp;
        int canceled = 0;
 
-       io_cq_lock(ctx);
+       /*
+        * completion_lock is needed for io_match_task(). Take it before
+        * timeout_lockfirst to keep locking ordering.
+        */
+       spin_lock(&ctx->completion_lock);
        spin_lock_irq(&ctx->timeout_lock);
        list_for_each_entry_safe(timeout, tmp, &ctx->timeout_list, list) {
                struct io_kiocb *req = cmd_to_io_kiocb(timeout);
@@ -634,6 +638,6 @@ __cold bool io_kill_timeouts(struct io_ring_ctx *ctx, struct task_struct *tsk,
                        canceled++;
        }
        spin_unlock_irq(&ctx->timeout_lock);
-       io_cq_unlock_post(ctx);
+       spin_unlock(&ctx->completion_lock);
        return canceled != 0;
 }