io_uring: include dying ring in task_work "should cancel" state

JIRA: https://issues.redhat.com/browse/RHEL-141266

commit 3539b1467e94336d5854ebf976d9627bfb65d6c3
Author: Jens Axboe <axboe@kernel.dk>
Date:   Thu Sep 18 10:21:14 2025 -0600

    io_uring: include dying ring in task_work "should cancel" state
    
    When running task_work for an exiting task, rather than perform the
    issue retry attempt, the task_work is canceled. However, this isn't
    done for a ring that has been closed. This can lead to requests being
    successfully completed post the ring being closed, which is somewhat
    confusing and surprising to an application.
    
    Rather than just check the task exit state, also include the ring
    ref state in deciding whether or not to terminate a given request when
    run from task_work.
    
    Cc: stable@vger.kernel.org # 6.1+
    Link: https://github.com/axboe/liburing/discussions/1459
    Reported-by: Benedek Thaler <thaler@thaler.hu>
    Signed-off-by: Jens Axboe <axboe@kernel.dk>

Signed-off-by: Jeff Moyer <jmoyer@redhat.com>
This commit is contained in:
Jeff Moyer
2026-01-14 12:00:17 -05:00
parent b9b48a4039
commit bc9d4addb5
5 changed files with 9 additions and 7 deletions
+4 -2
View File
@@ -1362,8 +1362,10 @@ static void io_req_task_cancel(struct io_kiocb *req, io_tw_token_t tw)
void io_req_task_submit(struct io_kiocb *req, io_tw_token_t tw) void io_req_task_submit(struct io_kiocb *req, io_tw_token_t tw)
{ {
io_tw_lock(req->ctx, tw); struct io_ring_ctx *ctx = req->ctx;
if (unlikely(io_should_terminate_tw()))
io_tw_lock(ctx, tw);
if (unlikely(io_should_terminate_tw(ctx)))
io_req_defer_failed(req, -EFAULT); io_req_defer_failed(req, -EFAULT);
else if (req->flags & REQ_F_FORCE_ASYNC) else if (req->flags & REQ_F_FORCE_ASYNC)
io_queue_iowq(req); io_queue_iowq(req);
+2 -2
View File
@@ -460,9 +460,9 @@ static inline bool io_allowed_run_tw(struct io_ring_ctx *ctx)
* 2) PF_KTHREAD is set, in which case the invoker of the task_work is * 2) PF_KTHREAD is set, in which case the invoker of the task_work is
* our fallback task_work. * our fallback task_work.
*/ */
static inline bool io_should_terminate_tw(void) static inline bool io_should_terminate_tw(struct io_ring_ctx *ctx)
{ {
return current->flags & (PF_KTHREAD | PF_EXITING); return (current->flags & (PF_KTHREAD | PF_EXITING)) || percpu_ref_is_dying(&ctx->refs);
} }
static inline void io_req_queue_tw_complete(struct io_kiocb *req, s32 res) static inline void io_req_queue_tw_complete(struct io_kiocb *req, s32 res)
+1 -1
View File
@@ -224,7 +224,7 @@ static int io_poll_check_events(struct io_kiocb *req, io_tw_token_t tw)
{ {
int v; int v;
if (unlikely(io_should_terminate_tw())) if (unlikely(io_should_terminate_tw(req->ctx)))
return -ECANCELED; return -ECANCELED;
do { do {
+1 -1
View File
@@ -319,7 +319,7 @@ static void io_req_task_link_timeout(struct io_kiocb *req, io_tw_token_t tw)
int ret; int ret;
if (prev) { if (prev) {
if (!io_should_terminate_tw()) { if (!io_should_terminate_tw(req->ctx)) {
struct io_cancel_data cd = { struct io_cancel_data cd = {
.ctx = req->ctx, .ctx = req->ctx,
.data = prev->cqe.user_data, .data = prev->cqe.user_data,
+1 -1
View File
@@ -107,7 +107,7 @@ static void io_uring_cmd_work(struct io_kiocb *req, io_tw_token_t tw)
struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req, struct io_uring_cmd); struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req, struct io_uring_cmd);
unsigned int flags = IO_URING_F_COMPLETE_DEFER; unsigned int flags = IO_URING_F_COMPLETE_DEFER;
if (io_should_terminate_tw()) if (io_should_terminate_tw(req->ctx))
flags |= IO_URING_F_TASK_DEAD; flags |= IO_URING_F_TASK_DEAD;
/* task_work executor checks the deffered list completion */ /* task_work executor checks the deffered list completion */