* FAILED: patch "[PATCH] io_uring/waitid: honor task_work cancellation" failed to apply to 6.12-stable tree
@ 2026-09-03 14:52 gregkh
2026-09-09 16:27 ` [PATCH 6.12.y 1/3] io_uring/waitid: have io_waitid_complete() remove wait queue entry Sasha Levin
0 siblings, 1 reply; 5+ messages in thread
From: gregkh @ 2026-09-03 14:52 UTC (permalink / raw)
To: sh_def, axboe; +Cc: stable
The patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x 14572de82e5022899e5856008bc9cac97004a88c
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '2026090345-dolly-jolliness-3485@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 14572de82e5022899e5856008bc9cac97004a88c Mon Sep 17 00:00:00 2001
From: Hui Su <sh_def@163.com>
Date: Tue, 18 Aug 2026 18:33:36 +0800
Subject: [PATCH] io_uring/waitid: honor task_work cancellation
io_waitid_cb() may run through the fallback task_work path when
task_work_add() can no longer queue work to the originating task. The
fallback runs from a kworker and io_uring marks such task work as
canceled through tw.cancel.
io_waitid_cb() currently ignores tw.cancel and calls __do_wait().
waitid is task-context dependent: __do_wait() performs child lookup
relative to current, and the retry path also uses
current->signal->wait_chldexit. If the callback runs from the fallback
kworker, current is therefore not the task that submitted the request.
Honor tw.cancel before entering __do_wait(). Complete the request with
-ECANCELED and skip the siginfo copy, since canceled task work may run
without the submitting task's userspace execution context.
Keep the existing siginfo handling for normal waitid completion and
explicit cancellation.
Fixes: f31ecf671ddc ("io_uring: add IORING_OP_WAITID support")
Cc: stable@vger.kernel.org
Signed-off-by: Hui Su <sh_def@163.com>
Link: https://patch.msgid.link/20260818103336.1922818-2-sh_def@163.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
diff --git a/io_uring/waitid.c b/io_uring/waitid.c
index 76af129ba8ca..596f2da40260 100644
--- a/io_uring/waitid.c
+++ b/io_uring/waitid.c
@@ -125,7 +125,7 @@ static void io_waitid_remove_wq(struct io_kiocb *req)
}
}
-static void io_waitid_complete(struct io_kiocb *req, int ret)
+static void io_waitid_complete(struct io_kiocb *req, int ret, bool copy_si)
{
struct io_waitid *iw = io_kiocb_to_cmd(req, struct io_waitid);
@@ -137,7 +137,10 @@ static void io_waitid_complete(struct io_kiocb *req, int ret)
hlist_del_init(&req->hash_node);
io_waitid_remove_wq(req);
- ret = io_waitid_finish(req, ret);
+ if (copy_si)
+ ret = io_waitid_finish(req, ret);
+ else
+ io_waitid_free(req);
if (ret < 0)
req_set_fail(req);
io_req_set_res(req, ret, 0);
@@ -159,7 +162,7 @@ static bool __io_waitid_cancel(struct io_kiocb *req)
if (atomic_fetch_inc(&iw->refs) & IO_WAITID_REF_MASK)
return false;
- io_waitid_complete(req, -ECANCELED);
+ io_waitid_complete(req, -ECANCELED, true);
io_req_queue_tw_complete(req, -ECANCELED);
return true;
}
@@ -202,6 +205,11 @@ static void io_waitid_cb(struct io_tw_req tw_req, io_tw_token_t tw)
int ret;
io_tw_lock(ctx, tw);
+ if (unlikely(tw.cancel)) {
+ io_waitid_complete(req, -ECANCELED, false);
+ io_req_task_complete(tw_req, tw);
+ return;
+ }
ret = __do_wait(&iwa->wo);
@@ -229,7 +237,7 @@ static void io_waitid_cb(struct io_tw_req tw_req, io_tw_token_t tw)
}
}
- io_waitid_complete(req, ret);
+ io_waitid_complete(req, ret, true);
io_req_task_complete(tw_req, tw);
}
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 6.12.y 1/3] io_uring/waitid: have io_waitid_complete() remove wait queue entry 2026-09-03 14:52 FAILED: patch "[PATCH] io_uring/waitid: honor task_work cancellation" failed to apply to 6.12-stable tree gregkh @ 2026-09-09 16:27 ` Sasha Levin 2026-09-09 16:27 ` [PATCH 6.12.y 2/3] io_uring: add wrapper type for io_req_tw_func_t arg Sasha Levin 2026-09-09 16:27 ` [PATCH 6.12.y 3/3] io_uring/waitid: honor task_work cancellation Sasha Levin 0 siblings, 2 replies; 5+ messages in thread From: Sasha Levin @ 2026-09-09 16:27 UTC (permalink / raw) To: stable; +Cc: Jens Axboe, Sasha Levin From: Jens Axboe <axboe@kernel.dk> [ Upstream commit a48c0cbf28c03f6c590a14ceb31bf6e619c2f6da ] Both callers of this need the entry potentially removed, so shift the removal into the completion side and kill it from the two callers. While at it, add a helper for removing the wait_queue_entry based on the passed in io_kiocb. Signed-off-by: Jens Axboe <axboe@kernel.dk> Stable-dep-of: 14572de82e50 ("io_uring/waitid: honor task_work cancellation") Signed-off-by: Sasha Levin <sashal@kernel.org> --- io_uring/waitid.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/io_uring/waitid.c b/io_uring/waitid.c index ef2283728c2b3..b67b6d5bc6830 100644 --- a/io_uring/waitid.c +++ b/io_uring/waitid.c @@ -115,6 +115,22 @@ static int io_waitid_finish(struct io_kiocb *req, int ret) return ret; } +static void io_waitid_remove_wq(struct io_kiocb *req) +{ + struct io_waitid *iw = io_kiocb_to_cmd(req, struct io_waitid); + struct wait_queue_head *head; + + head = READ_ONCE(iw->head); + if (head) { + struct io_waitid_async *iwa = req->async_data; + + iw->head = NULL; + spin_lock_irq(&head->lock); + list_del_init(&iwa->wo.child_wait.entry); + spin_unlock_irq(&head->lock); + } +} + static void io_waitid_complete(struct io_kiocb *req, int ret) { struct io_waitid *iw = io_kiocb_to_cmd(req, struct io_waitid); @@ -125,6 +141,7 @@ static void io_waitid_complete(struct io_kiocb *req, int ret) lockdep_assert_held(&req->ctx->uring_lock); hlist_del_init(&req->hash_node); + io_waitid_remove_wq(req); ret = io_waitid_finish(req, ret); if (ret < 0) @@ -135,7 +152,8 @@ static void io_waitid_complete(struct io_kiocb *req, int ret) static bool __io_waitid_cancel(struct io_ring_ctx *ctx, struct io_kiocb *req) { struct io_waitid *iw = io_kiocb_to_cmd(req, struct io_waitid); - struct io_waitid_async *iwa = req->async_data; + + lockdep_assert_held(&req->ctx->uring_lock); /* * Mark us canceled regardless of ownership. This will prevent a @@ -147,9 +165,6 @@ static bool __io_waitid_cancel(struct io_ring_ctx *ctx, struct io_kiocb *req) if (atomic_fetch_inc(&iw->refs) & IO_WAITID_REF_MASK) return false; - spin_lock_irq(&iw->head->lock); - list_del_init(&iwa->wo.child_wait.entry); - spin_unlock_irq(&iw->head->lock); io_waitid_complete(req, -ECANCELED); io_req_queue_tw_complete(req, -ECANCELED); return true; @@ -251,8 +266,7 @@ static void io_waitid_cb(struct io_kiocb *req, struct io_tw_state *ts) io_waitid_drop_issue_ref(req); return; } - - remove_wait_queue(iw->head, &iwa->wo.child_wait); + /* fall through to complete, will kill waitqueue */ } } -- 2.53.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 6.12.y 2/3] io_uring: add wrapper type for io_req_tw_func_t arg 2026-09-09 16:27 ` [PATCH 6.12.y 1/3] io_uring/waitid: have io_waitid_complete() remove wait queue entry Sasha Levin @ 2026-09-09 16:27 ` Sasha Levin 2026-09-09 16:52 ` Caleb Sander Mateos 2026-09-09 16:27 ` [PATCH 6.12.y 3/3] io_uring/waitid: honor task_work cancellation Sasha Levin 1 sibling, 1 reply; 5+ messages in thread From: Sasha Levin @ 2026-09-09 16:27 UTC (permalink / raw) To: stable; +Cc: Caleb Sander Mateos, Jens Axboe, Sasha Levin From: Caleb Sander Mateos <csander@purestorage.com> [ Upstream commit c33e779aba6804778c1440192a8033a145ba588d ] In preparation for uring_cmd implementations to implement functions with the io_req_tw_func_t signature, introduce a wrapper struct io_tw_req to hide the struct io_kiocb * argument. The intention is for only the io_uring core to access the inner struct io_kiocb *. uring_cmd implementations should instead call a helper from io_uring/cmd.h to convert struct io_tw_req to struct io_uring_cmd *. Signed-off-by: Caleb Sander Mateos <csander@purestorage.com> Signed-off-by: Jens Axboe <axboe@kernel.dk> Stable adaptation for 6.12: Keep the request-wrapper conversion and supply the missing by-value io_tw_token_t interface. The existing io_tw_state is already empty and io_tw_lock() only asserts that uring_lock is held, so no locking change is required. Convert the stable-only token users as well, and include io_uring_types.h in poll.h and rw.h for the new typedef. Add the cancel member and initialize it from the existing io_should_terminate_tw() in the fallback, per-task, and local task-work executors. This supplies the interface required by target 14572de82e50 without importing the unrelated task-exit or callback cancellation refactoring. Keep the stable futex cache and async-data cleanup paths. No functions are added. The target waitid patch applies unchanged. Stable-dep-of: 14572de82e50 ("io_uring/waitid: honor task_work cancellation") Signed-off-by: Sasha Levin <sashal@kernel.org> --- include/linux/io_uring_types.h | 14 ++++++++++- io_uring/futex.c | 20 +++++++++------ io_uring/io_uring.c | 46 +++++++++++++++++++--------------- io_uring/io_uring.h | 8 +++--- io_uring/msg_ring.c | 3 ++- io_uring/notif.c | 5 ++-- io_uring/poll.c | 25 +++++++++--------- io_uring/poll.h | 4 ++- io_uring/rw.c | 5 ++-- io_uring/rw.h | 3 ++- io_uring/timeout.c | 20 +++++++++------ io_uring/uring_cmd.c | 3 ++- io_uring/waitid.c | 9 ++++--- 13 files changed, 100 insertions(+), 65 deletions(-) diff --git a/include/linux/io_uring_types.h b/include/linux/io_uring_types.h index 6d47d0d3fde01..1d32c66a9a052 100644 --- a/include/linux/io_uring_types.h +++ b/include/linux/io_uring_types.h @@ -422,8 +422,16 @@ struct io_ring_ctx { struct page **sqe_pages; }; +/* + * Token indicating function is called in task work context: + * ctx->uring_lock is held and any completions generated will be flushed. + * ONLY core io_uring.c should instantiate this struct. + */ struct io_tw_state { + bool cancel; }; +/* Alias to use in code that doesn't instantiate struct io_tw_state */ +typedef struct io_tw_state io_tw_token_t; enum { REQ_F_FIXED_FILE_BIT = IOSQE_FIXED_FILE_BIT, @@ -549,7 +557,11 @@ enum { REQ_F_BUF_MORE = IO_REQ_FLAG(REQ_F_BUF_MORE_BIT), }; -typedef void (*io_req_tw_func_t)(struct io_kiocb *req, struct io_tw_state *ts); +struct io_tw_req { + struct io_kiocb *req; +}; + +typedef void (*io_req_tw_func_t)(struct io_tw_req tw_req, io_tw_token_t tw); struct io_task_work { struct llist_node node; diff --git a/io_uring/futex.c b/io_uring/futex.c index a3d2b700b480c..cd125e4671d35 100644 --- a/io_uring/futex.c +++ b/io_uring/futex.c @@ -44,30 +44,34 @@ void io_futex_cache_free(struct io_ring_ctx *ctx) io_alloc_cache_free(&ctx->futex_cache, kfree); } -static void __io_futex_complete(struct io_kiocb *req, struct io_tw_state *ts) +static void __io_futex_complete(struct io_tw_req tw_req, io_tw_token_t tw) { + struct io_kiocb *req = tw_req.req; + req->async_data = NULL; hlist_del_init(&req->hash_node); - io_req_task_complete(req, ts); + io_req_task_complete(tw_req, tw); } -static void io_futex_complete(struct io_kiocb *req, struct io_tw_state *ts) +static void io_futex_complete(struct io_tw_req tw_req, io_tw_token_t tw) { + struct io_kiocb *req = tw_req.req; struct io_futex_data *ifd = req->async_data; struct io_ring_ctx *ctx = req->ctx; - io_tw_lock(ctx, ts); + io_tw_lock(ctx, tw); if (!io_alloc_cache_put(&ctx->futex_cache, ifd)) kfree(ifd); - __io_futex_complete(req, ts); + __io_futex_complete(tw_req, tw); } -static void io_futexv_complete(struct io_kiocb *req, struct io_tw_state *ts) +static void io_futexv_complete(struct io_tw_req tw_req, io_tw_token_t tw) { + struct io_kiocb *req = tw_req.req; struct io_futex *iof = io_kiocb_to_cmd(req, struct io_futex); struct futex_vector *futexv = req->async_data; - io_tw_lock(req->ctx, ts); + io_tw_lock(req->ctx, tw); if (!iof->futexv_unqueued) { int res; @@ -79,7 +83,7 @@ static void io_futexv_complete(struct io_kiocb *req, struct io_tw_state *ts) kfree(req->async_data); req->flags &= ~REQ_F_ASYNC_DATA; - __io_futex_complete(req, ts); + __io_futex_complete(tw_req, tw); } static bool io_futexv_claim(struct io_futex *iof) diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c index 03c6ab505d74a..c00768f616c3f 100644 --- a/io_uring/io_uring.c +++ b/io_uring/io_uring.c @@ -251,8 +251,9 @@ static __cold void io_fallback_req_func(struct work_struct *work) percpu_ref_get(&ctx->refs); mutex_lock(&ctx->uring_lock); + ts.cancel = io_should_terminate_tw(ctx); llist_for_each_entry_safe(req, tmp, node, io_task_work.node) - req->io_task_work.func(req, &ts); + req->io_task_work.func((struct io_tw_req){req}, ts); io_submit_flush_completions(ctx); mutex_unlock(&ctx->uring_lock); percpu_ref_put(&ctx->refs); @@ -509,9 +510,9 @@ static void io_queue_iowq(struct io_kiocb *req) io_wq_enqueue(tctx->io_wq, &req->work); } -static void io_req_queue_iowq_tw(struct io_kiocb *req, struct io_tw_state *ts) +static void io_req_queue_iowq_tw(struct io_tw_req tw_req, io_tw_token_t tw) { - io_queue_iowq(req); + io_queue_iowq(tw_req.req); } void io_req_queue_iowq(struct io_kiocb *req) @@ -1012,7 +1013,7 @@ static inline struct io_kiocb *io_req_find_next(struct io_kiocb *req) return nxt; } -static void ctx_flush_and_put(struct io_ring_ctx *ctx, struct io_tw_state *ts) +static void ctx_flush_and_put(struct io_ring_ctx *ctx, io_tw_token_t tw) { if (!ctx) return; @@ -1042,24 +1043,25 @@ struct llist_node *io_handle_tw_list(struct llist_node *node, io_task_work.node); if (req->ctx != ctx) { - ctx_flush_and_put(ctx, &ts); + ctx_flush_and_put(ctx, ts); ctx = req->ctx; mutex_lock(&ctx->uring_lock); percpu_ref_get(&ctx->refs); + ts.cancel = io_should_terminate_tw(ctx); } INDIRECT_CALL_2(req->io_task_work.func, io_poll_task_func, io_req_rw_complete, - req, &ts); + (struct io_tw_req){req}, ts); node = next; (*count)++; if (unlikely(need_resched())) { - ctx_flush_and_put(ctx, &ts); + ctx_flush_and_put(ctx, ts); ctx = NULL; cond_resched(); } } while (node && *count < max_entries); - ctx_flush_and_put(ctx, &ts); + ctx_flush_and_put(ctx, ts); return node; } @@ -1280,7 +1282,7 @@ static bool io_run_local_work_continue(struct io_ring_ctx *ctx, int events, return false; } -static int __io_run_local_work(struct io_ring_ctx *ctx, struct io_tw_state *ts, +static int __io_run_local_work(struct io_ring_ctx *ctx, io_tw_token_t tw, int min_events) { struct llist_node *node; @@ -1292,6 +1294,7 @@ static int __io_run_local_work(struct io_ring_ctx *ctx, struct io_tw_state *ts, if (ctx->flags & IORING_SETUP_TASKRUN_FLAG) atomic_andnot(IORING_SQ_TASKRUN, &ctx->rings->sq_flags); again: + tw.cancel = io_should_terminate_tw(ctx); /* * llists are in reverse order, flip it back the right way before * running the pending items. @@ -1303,7 +1306,7 @@ static int __io_run_local_work(struct io_ring_ctx *ctx, struct io_tw_state *ts, io_task_work.node); INDIRECT_CALL_2(req->io_task_work.func, io_poll_task_func, io_req_rw_complete, - req, ts); + (struct io_tw_req){req}, tw); ret++; node = next; } @@ -1326,7 +1329,7 @@ static inline int io_run_local_work_locked(struct io_ring_ctx *ctx, if (llist_empty(&ctx->work_llist)) return 0; - return __io_run_local_work(ctx, &ts, min_events); + return __io_run_local_work(ctx, ts, min_events); } static int io_run_local_work(struct io_ring_ctx *ctx, int min_events) @@ -1335,22 +1338,25 @@ static int io_run_local_work(struct io_ring_ctx *ctx, int min_events) int ret; mutex_lock(&ctx->uring_lock); - ret = __io_run_local_work(ctx, &ts, min_events); + ret = __io_run_local_work(ctx, ts, min_events); mutex_unlock(&ctx->uring_lock); return ret; } -static void io_req_task_cancel(struct io_kiocb *req, struct io_tw_state *ts) +static void io_req_task_cancel(struct io_tw_req tw_req, io_tw_token_t tw) { - io_tw_lock(req->ctx, ts); + struct io_kiocb *req = tw_req.req; + + io_tw_lock(req->ctx, tw); io_req_defer_failed(req, req->cqe.res); } -void io_req_task_submit(struct io_kiocb *req, struct io_tw_state *ts) +void io_req_task_submit(struct io_tw_req tw_req, io_tw_token_t tw) { + struct io_kiocb *req = tw_req.req; struct io_ring_ctx *ctx = req->ctx; - io_tw_lock(ctx, ts); + io_tw_lock(ctx, tw); if (unlikely(io_should_terminate_tw(ctx))) io_req_defer_failed(req, -EFAULT); else if (req->flags & REQ_F_FORCE_ASYNC) @@ -1557,9 +1563,9 @@ static int io_iopoll_check(struct io_ring_ctx *ctx, long min) return 0; } -void io_req_task_complete(struct io_kiocb *req, struct io_tw_state *ts) +void io_req_task_complete(struct io_tw_req tw_req, io_tw_token_t tw) { - io_req_complete_defer(req); + io_req_complete_defer(tw_req.req); } /* @@ -1760,9 +1766,9 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags) return ret; } -int io_poll_issue(struct io_kiocb *req, struct io_tw_state *ts) +int io_poll_issue(struct io_kiocb *req, io_tw_token_t tw) { - io_tw_lock(req->ctx, ts); + io_tw_lock(req->ctx, tw); return io_issue_sqe(req, IO_URING_F_NONBLOCK|IO_URING_F_MULTISHOT| IO_URING_F_COMPLETE_DEFER); } diff --git a/io_uring/io_uring.h b/io_uring/io_uring.h index f1d3b49289977..8439deda9ca47 100644 --- a/io_uring/io_uring.h +++ b/io_uring/io_uring.h @@ -82,9 +82,9 @@ void io_req_task_work_add_remote(struct io_kiocb *req, struct io_ring_ctx *ctx, unsigned flags); bool io_alloc_async_data(struct io_kiocb *req); void io_req_task_queue(struct io_kiocb *req); -void io_req_task_complete(struct io_kiocb *req, struct io_tw_state *ts); +void io_req_task_complete(struct io_tw_req tw_req, io_tw_token_t tw); 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); +void io_req_task_submit(struct io_tw_req tw_req, io_tw_token_t tw); struct llist_node *io_handle_tw_list(struct llist_node *node, unsigned int *count, unsigned int max_entries); struct llist_node *tctx_task_work_run(struct io_uring_task *tctx, unsigned int max_entries, unsigned int *count); void tctx_task_work(struct callback_head *cb); @@ -96,7 +96,7 @@ int io_ring_add_registered_file(struct io_uring_task *tctx, struct file *file, int start, int end); void io_req_queue_iowq(struct io_kiocb *req); -int io_poll_issue(struct io_kiocb *req, struct io_tw_state *ts); +int io_poll_issue(struct io_kiocb *req, io_tw_token_t tw); int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr); int io_do_iopoll(struct io_ring_ctx *ctx, bool force_nonspin); void __io_submit_flush_completions(struct io_ring_ctx *ctx); @@ -347,7 +347,7 @@ static inline bool io_task_work_pending(struct io_ring_ctx *ctx) return task_work_pending(current) || !llist_empty(&ctx->work_llist); } -static inline void io_tw_lock(struct io_ring_ctx *ctx, struct io_tw_state *ts) +static inline void io_tw_lock(struct io_ring_ctx *ctx, io_tw_token_t tw) { lockdep_assert_held(&ctx->uring_lock); } diff --git a/io_uring/msg_ring.c b/io_uring/msg_ring.c index 3cf59d8c58073..5c7d35f2f0b86 100644 --- a/io_uring/msg_ring.c +++ b/io_uring/msg_ring.c @@ -70,8 +70,9 @@ static inline bool io_msg_need_remote(struct io_ring_ctx *target_ctx) return target_ctx->task_complete; } -static void io_msg_tw_complete(struct io_kiocb *req, struct io_tw_state *ts) +static void io_msg_tw_complete(struct io_tw_req tw_req, io_tw_token_t tw) { + struct io_kiocb *req = tw_req.req; struct io_ring_ctx *ctx = req->ctx; io_add_aux_cqe(ctx, req->cqe.user_data, req->cqe.res, req->cqe.flags); diff --git a/io_uring/notif.c b/io_uring/notif.c index f50964c7dab82..cc9fc345da327 100644 --- a/io_uring/notif.c +++ b/io_uring/notif.c @@ -11,8 +11,9 @@ static const struct ubuf_info_ops io_ubuf_ops; -static void io_notif_tw_complete(struct io_kiocb *notif, struct io_tw_state *ts) +static void io_notif_tw_complete(struct io_tw_req tw_req, io_tw_token_t tw) { + struct io_kiocb *notif = tw_req.req; struct io_notif_data *nd = io_notif_to_data(notif); struct io_ring_ctx *ctx = notif->ctx; @@ -34,7 +35,7 @@ static void io_notif_tw_complete(struct io_kiocb *notif, struct io_tw_state *ts) } nd = nd->next; - io_req_task_complete(notif, ts); + io_req_task_complete((struct io_tw_req){notif}, tw); } while (nd); } diff --git a/io_uring/poll.c b/io_uring/poll.c index 002f1ae830b8a..39c7f47c1f5b5 100644 --- a/io_uring/poll.c +++ b/io_uring/poll.c @@ -150,7 +150,7 @@ static void io_poll_req_insert_locked(struct io_kiocb *req) hlist_add_head(&req->hash_node, &table->hbs[index].list); } -static void io_poll_tw_hash_eject(struct io_kiocb *req, struct io_tw_state *ts) +static void io_poll_tw_hash_eject(struct io_kiocb *req, io_tw_token_t tw) { struct io_ring_ctx *ctx = req->ctx; @@ -161,7 +161,7 @@ static void io_poll_tw_hash_eject(struct io_kiocb *req, struct io_tw_state *ts) * already grabbed the mutex for us, but there is a chance it * failed. */ - io_tw_lock(ctx, ts); + io_tw_lock(ctx, tw); hash_del(&req->hash_node); req->flags &= ~REQ_F_HASH_LOCKED; } else { @@ -261,7 +261,7 @@ static inline void io_poll_execute(struct io_kiocb *req, int res) * req->cqe.res. IOU_POLL_REMOVE_POLL_USE_RES indicates to remove multishot * poll and that the result is stored in req->cqe. */ -static int io_poll_check_events(struct io_kiocb *req, struct io_tw_state *ts) +static int io_poll_check_events(struct io_kiocb *req, io_tw_token_t tw) { int v; @@ -333,7 +333,7 @@ static int io_poll_check_events(struct io_kiocb *req, struct io_tw_state *ts) /* multiple refs and HUP, ensure we loop once more */ if ((req->cqe.res & (POLLHUP | POLLRDHUP)) && v != 1) v--; - ret = io_poll_issue(req, ts); + ret = io_poll_issue(req, tw); if (ret == IOU_STOP_MULTISHOT) return IOU_POLL_REMOVE_POLL_USE_RES; else if (ret == IOU_REQUEUE) @@ -355,11 +355,12 @@ static int io_poll_check_events(struct io_kiocb *req, struct io_tw_state *ts) return IOU_POLL_NO_ACTION; } -void io_poll_task_func(struct io_kiocb *req, struct io_tw_state *ts) +void io_poll_task_func(struct io_tw_req tw_req, io_tw_token_t tw) { + struct io_kiocb *req = tw_req.req; int ret; - ret = io_poll_check_events(req, ts); + ret = io_poll_check_events(req, tw); if (ret == IOU_POLL_NO_ACTION) { return; } else if (ret == IOU_POLL_REQUEUE) { @@ -367,7 +368,7 @@ void io_poll_task_func(struct io_kiocb *req, struct io_tw_state *ts) return; } io_poll_remove_entries(req); - io_poll_tw_hash_eject(req, ts); + io_poll_tw_hash_eject(req, tw); if (req->opcode == IORING_OP_POLL_ADD) { if (ret == IOU_POLL_DONE) { @@ -376,7 +377,7 @@ void io_poll_task_func(struct io_kiocb *req, struct io_tw_state *ts) poll = io_kiocb_to_cmd(req, struct io_poll); req->cqe.res = mangle_poll(req->cqe.res & poll->events); } else if (ret == IOU_POLL_REISSUE) { - io_req_task_submit(req, ts); + io_req_task_submit(tw_req, tw); return; } else if (ret != IOU_POLL_REMOVE_POLL_USE_RES) { req->cqe.res = ret; @@ -384,14 +385,14 @@ void io_poll_task_func(struct io_kiocb *req, struct io_tw_state *ts) } io_req_set_res(req, req->cqe.res, 0); - io_req_task_complete(req, ts); + io_req_task_complete(tw_req, tw); } else { - io_tw_lock(req->ctx, ts); + io_tw_lock(req->ctx, tw); if (ret == IOU_POLL_REMOVE_POLL_USE_RES) - io_req_task_complete(req, ts); + io_req_task_complete(tw_req, tw); else if (ret == IOU_POLL_DONE || ret == IOU_POLL_REISSUE) - io_req_task_submit(req, ts); + io_req_task_submit(tw_req, tw); else io_req_defer_failed(req, ret); } diff --git a/io_uring/poll.h b/io_uring/poll.h index b0e3745f5a29b..bf894dc6b9f86 100644 --- a/io_uring/poll.h +++ b/io_uring/poll.h @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 +#include <linux/io_uring_types.h> + #define IO_POLL_ALLOC_CACHE_MAX 32 enum { @@ -43,4 +45,4 @@ int io_arm_poll_handler(struct io_kiocb *req, unsigned issue_flags); bool io_poll_remove_all(struct io_ring_ctx *ctx, struct task_struct *tsk, bool cancel_all); -void io_poll_task_func(struct io_kiocb *req, struct io_tw_state *ts); +void io_poll_task_func(struct io_tw_req tw_req, io_tw_token_t tw); diff --git a/io_uring/rw.c b/io_uring/rw.c index a74664c4a05ef..55e5bff818b10 100644 --- a/io_uring/rw.c +++ b/io_uring/rw.c @@ -508,8 +508,9 @@ static inline int io_fixup_rw_res(struct io_kiocb *req, long res) return res; } -void io_req_rw_complete(struct io_kiocb *req, struct io_tw_state *ts) +void io_req_rw_complete(struct io_tw_req tw_req, io_tw_token_t tw) { + struct io_kiocb *req = tw_req.req; struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); struct kiocb *kiocb = &rw->kiocb; @@ -525,7 +526,7 @@ void io_req_rw_complete(struct io_kiocb *req, struct io_tw_state *ts) req->cqe.flags |= io_put_kbuf(req, req->cqe.res, NULL); io_req_rw_cleanup(req, 0); - io_req_task_complete(req, ts); + io_req_task_complete(tw_req, tw); } static void io_complete_rw(struct kiocb *kiocb, long res) diff --git a/io_uring/rw.h b/io_uring/rw.h index 3f432dc75441f..283beeeb05fbc 100644 --- a/io_uring/rw.h +++ b/io_uring/rw.h @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 +#include <linux/io_uring_types.h> #include <linux/pagemap.h> struct io_async_rw { @@ -22,7 +23,7 @@ int io_read(struct io_kiocb *req, unsigned int issue_flags); int io_write(struct io_kiocb *req, unsigned int issue_flags); void io_readv_writev_cleanup(struct io_kiocb *req); void io_rw_fail(struct io_kiocb *req); -void io_req_rw_complete(struct io_kiocb *req, struct io_tw_state *ts); +void io_req_rw_complete(struct io_tw_req tw_req, io_tw_token_t tw); int io_read_mshot_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe); int io_read_mshot(struct io_kiocb *req, unsigned int issue_flags); void io_rw_cache_free(const void *entry); diff --git a/io_uring/timeout.c b/io_uring/timeout.c index 9cc5776103feb..1f0c66b643e3c 100644 --- a/io_uring/timeout.c +++ b/io_uring/timeout.c @@ -65,8 +65,9 @@ static inline bool io_timeout_finish(struct io_timeout *timeout, static enum hrtimer_restart io_timeout_fn(struct hrtimer *timer); -static void io_timeout_complete(struct io_kiocb *req, struct io_tw_state *ts) +static void io_timeout_complete(struct io_tw_req tw_req, io_tw_token_t tw) { + struct io_kiocb *req = tw_req.req; struct io_timeout *timeout = io_kiocb_to_cmd(req, struct io_timeout); struct io_timeout_data *data = req->async_data; struct io_ring_ctx *ctx = req->ctx; @@ -83,7 +84,7 @@ static void io_timeout_complete(struct io_kiocb *req, struct io_tw_state *ts) } } - io_req_task_complete(req, ts); + io_req_task_complete(tw_req, tw); } static bool io_kill_timeout(struct io_kiocb *req, int status) @@ -138,9 +139,11 @@ __cold void io_flush_timeouts(struct io_ring_ctx *ctx) spin_unlock_irq(&ctx->timeout_lock); } -static void io_req_tw_fail_links(struct io_kiocb *link, struct io_tw_state *ts) +static void io_req_tw_fail_links(struct io_tw_req tw_req, io_tw_token_t tw) { - io_tw_lock(link->ctx, ts); + struct io_kiocb *link = tw_req.req; + + io_tw_lock(link->ctx, tw); while (link) { struct io_kiocb *nxt = link->link; long res = -ECANCELED; @@ -149,7 +152,7 @@ static void io_req_tw_fail_links(struct io_kiocb *link, struct io_tw_state *ts) res = link->cqe.res; link->link = NULL; io_req_set_res(link, res, 0); - io_req_task_complete(link, ts); + io_req_task_complete((struct io_tw_req){link}, tw); link = nxt; } } @@ -296,8 +299,9 @@ int io_timeout_cancel(struct io_ring_ctx *ctx, struct io_cancel_data *cd) return 0; } -static void io_req_task_link_timeout(struct io_kiocb *req, struct io_tw_state *ts) +static void io_req_task_link_timeout(struct io_tw_req tw_req, io_tw_token_t tw) { + struct io_kiocb *req = tw_req.req; struct io_timeout *timeout = io_kiocb_to_cmd(req, struct io_timeout); struct io_kiocb *prev = timeout->prev; int ret = -ENOENT; @@ -312,11 +316,11 @@ static void io_req_task_link_timeout(struct io_kiocb *req, struct io_tw_state *t 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_req_task_complete(tw_req, tw); io_put_req(prev); } else { io_req_set_res(req, -ETIME, 0); - io_req_task_complete(req, ts); + io_req_task_complete(tw_req, tw); } } diff --git a/io_uring/uring_cmd.c b/io_uring/uring_cmd.c index e5d0cc8a8a562..0dfd27963fc0d 100644 --- a/io_uring/uring_cmd.c +++ b/io_uring/uring_cmd.c @@ -113,8 +113,9 @@ void io_uring_cmd_mark_cancelable(struct io_uring_cmd *cmd, } EXPORT_SYMBOL_GPL(io_uring_cmd_mark_cancelable); -static void io_uring_cmd_work(struct io_kiocb *req, struct io_tw_state *ts) +static void io_uring_cmd_work(struct io_tw_req tw_req, io_tw_token_t tw) { + struct io_kiocb *req = tw_req.req; struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req, struct io_uring_cmd); unsigned int flags = IO_URING_F_COMPLETE_DEFER; diff --git a/io_uring/waitid.c b/io_uring/waitid.c index b67b6d5bc6830..f3222cb30cdea 100644 --- a/io_uring/waitid.c +++ b/io_uring/waitid.c @@ -16,7 +16,7 @@ #include "waitid.h" #include "../kernel/exit.h" -static void io_waitid_cb(struct io_kiocb *req, struct io_tw_state *ts); +static void io_waitid_cb(struct io_tw_req tw_req, io_tw_token_t tw); #define IO_WAITID_CANCEL_FLAG BIT(31) #define IO_WAITID_REF_MASK GENMASK(30, 0) @@ -236,13 +236,14 @@ static inline bool io_waitid_drop_issue_ref(struct io_kiocb *req) return true; } -static void io_waitid_cb(struct io_kiocb *req, struct io_tw_state *ts) +static void io_waitid_cb(struct io_tw_req tw_req, io_tw_token_t tw) { + struct io_kiocb *req = tw_req.req; struct io_waitid_async *iwa = req->async_data; struct io_ring_ctx *ctx = req->ctx; int ret; - io_tw_lock(ctx, ts); + io_tw_lock(ctx, tw); ret = __do_wait(&iwa->wo); @@ -271,7 +272,7 @@ static void io_waitid_cb(struct io_kiocb *req, struct io_tw_state *ts) } io_waitid_complete(req, ret); - io_req_task_complete(req, ts); + io_req_task_complete(tw_req, tw); } static int io_waitid_wait(struct wait_queue_entry *wait, unsigned mode, -- 2.53.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 6.12.y 2/3] io_uring: add wrapper type for io_req_tw_func_t arg 2026-09-09 16:27 ` [PATCH 6.12.y 2/3] io_uring: add wrapper type for io_req_tw_func_t arg Sasha Levin @ 2026-09-09 16:52 ` Caleb Sander Mateos 0 siblings, 0 replies; 5+ messages in thread From: Caleb Sander Mateos @ 2026-09-09 16:52 UTC (permalink / raw) To: Sasha Levin; +Cc: stable, Jens Axboe On Wed, Sep 9, 2026 at 9:27 AM Sasha Levin <sashal@kernel.org> wrote: > > From: Caleb Sander Mateos <csander@purestorage.com> > > [ Upstream commit c33e779aba6804778c1440192a8033a145ba588d ] > > In preparation for uring_cmd implementations to implement functions > with the io_req_tw_func_t signature, introduce a wrapper struct > io_tw_req to hide the struct io_kiocb * argument. The intention is for > only the io_uring core to access the inner struct io_kiocb *. uring_cmd > implementations should instead call a helper from io_uring/cmd.h to > convert struct io_tw_req to struct io_uring_cmd *. > > Signed-off-by: Caleb Sander Mateos <csander@purestorage.com> > Signed-off-by: Jens Axboe <axboe@kernel.dk> > > Stable adaptation for 6.12: > > Keep the request-wrapper conversion and supply the missing by-value > io_tw_token_t interface. The existing io_tw_state is already empty and > io_tw_lock() only asserts that uring_lock is held, so no locking change > is required. Convert the stable-only token users as well, and include > io_uring_types.h in poll.h and rw.h for the new typedef. > > Add the cancel member and initialize it from the existing > io_should_terminate_tw() in the fallback, per-task, and local task-work > executors. This supplies the interface required by target 14572de82e50 > without importing the unrelated task-exit or callback cancellation > refactoring. Keep the stable futex cache and async-data cleanup paths. struct io_tw_state's cancel field was added by commit 7be20254a743 ("io_uring: unify task_work cancelation checks"). Can we backport that commit as well instead of folding it into this this unrelated commit? Best, Caleb > > No functions are added. The target waitid patch applies unchanged. > > Stable-dep-of: 14572de82e50 ("io_uring/waitid: honor task_work cancellation") > Signed-off-by: Sasha Levin <sashal@kernel.org> > --- > include/linux/io_uring_types.h | 14 ++++++++++- > io_uring/futex.c | 20 +++++++++------ > io_uring/io_uring.c | 46 +++++++++++++++++++--------------- > io_uring/io_uring.h | 8 +++--- > io_uring/msg_ring.c | 3 ++- > io_uring/notif.c | 5 ++-- > io_uring/poll.c | 25 +++++++++--------- > io_uring/poll.h | 4 ++- > io_uring/rw.c | 5 ++-- > io_uring/rw.h | 3 ++- > io_uring/timeout.c | 20 +++++++++------ > io_uring/uring_cmd.c | 3 ++- > io_uring/waitid.c | 9 ++++--- > 13 files changed, 100 insertions(+), 65 deletions(-) > > diff --git a/include/linux/io_uring_types.h b/include/linux/io_uring_types.h > index 6d47d0d3fde01..1d32c66a9a052 100644 > --- a/include/linux/io_uring_types.h > +++ b/include/linux/io_uring_types.h > @@ -422,8 +422,16 @@ struct io_ring_ctx { > struct page **sqe_pages; > }; > > +/* > + * Token indicating function is called in task work context: > + * ctx->uring_lock is held and any completions generated will be flushed. > + * ONLY core io_uring.c should instantiate this struct. > + */ > struct io_tw_state { > + bool cancel; > }; > +/* Alias to use in code that doesn't instantiate struct io_tw_state */ > +typedef struct io_tw_state io_tw_token_t; > > enum { > REQ_F_FIXED_FILE_BIT = IOSQE_FIXED_FILE_BIT, > @@ -549,7 +557,11 @@ enum { > REQ_F_BUF_MORE = IO_REQ_FLAG(REQ_F_BUF_MORE_BIT), > }; > > -typedef void (*io_req_tw_func_t)(struct io_kiocb *req, struct io_tw_state *ts); > +struct io_tw_req { > + struct io_kiocb *req; > +}; > + > +typedef void (*io_req_tw_func_t)(struct io_tw_req tw_req, io_tw_token_t tw); > > struct io_task_work { > struct llist_node node; > diff --git a/io_uring/futex.c b/io_uring/futex.c > index a3d2b700b480c..cd125e4671d35 100644 > --- a/io_uring/futex.c > +++ b/io_uring/futex.c > @@ -44,30 +44,34 @@ void io_futex_cache_free(struct io_ring_ctx *ctx) > io_alloc_cache_free(&ctx->futex_cache, kfree); > } > > -static void __io_futex_complete(struct io_kiocb *req, struct io_tw_state *ts) > +static void __io_futex_complete(struct io_tw_req tw_req, io_tw_token_t tw) > { > + struct io_kiocb *req = tw_req.req; > + > req->async_data = NULL; > hlist_del_init(&req->hash_node); > - io_req_task_complete(req, ts); > + io_req_task_complete(tw_req, tw); > } > > -static void io_futex_complete(struct io_kiocb *req, struct io_tw_state *ts) > +static void io_futex_complete(struct io_tw_req tw_req, io_tw_token_t tw) > { > + struct io_kiocb *req = tw_req.req; > struct io_futex_data *ifd = req->async_data; > struct io_ring_ctx *ctx = req->ctx; > > - io_tw_lock(ctx, ts); > + io_tw_lock(ctx, tw); > if (!io_alloc_cache_put(&ctx->futex_cache, ifd)) > kfree(ifd); > - __io_futex_complete(req, ts); > + __io_futex_complete(tw_req, tw); > } > > -static void io_futexv_complete(struct io_kiocb *req, struct io_tw_state *ts) > +static void io_futexv_complete(struct io_tw_req tw_req, io_tw_token_t tw) > { > + struct io_kiocb *req = tw_req.req; > struct io_futex *iof = io_kiocb_to_cmd(req, struct io_futex); > struct futex_vector *futexv = req->async_data; > > - io_tw_lock(req->ctx, ts); > + io_tw_lock(req->ctx, tw); > > if (!iof->futexv_unqueued) { > int res; > @@ -79,7 +83,7 @@ static void io_futexv_complete(struct io_kiocb *req, struct io_tw_state *ts) > > kfree(req->async_data); > req->flags &= ~REQ_F_ASYNC_DATA; > - __io_futex_complete(req, ts); > + __io_futex_complete(tw_req, tw); > } > > static bool io_futexv_claim(struct io_futex *iof) > diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c > index 03c6ab505d74a..c00768f616c3f 100644 > --- a/io_uring/io_uring.c > +++ b/io_uring/io_uring.c > @@ -251,8 +251,9 @@ static __cold void io_fallback_req_func(struct work_struct *work) > > percpu_ref_get(&ctx->refs); > mutex_lock(&ctx->uring_lock); > + ts.cancel = io_should_terminate_tw(ctx); > llist_for_each_entry_safe(req, tmp, node, io_task_work.node) > - req->io_task_work.func(req, &ts); > + req->io_task_work.func((struct io_tw_req){req}, ts); > io_submit_flush_completions(ctx); > mutex_unlock(&ctx->uring_lock); > percpu_ref_put(&ctx->refs); > @@ -509,9 +510,9 @@ static void io_queue_iowq(struct io_kiocb *req) > io_wq_enqueue(tctx->io_wq, &req->work); > } > > -static void io_req_queue_iowq_tw(struct io_kiocb *req, struct io_tw_state *ts) > +static void io_req_queue_iowq_tw(struct io_tw_req tw_req, io_tw_token_t tw) > { > - io_queue_iowq(req); > + io_queue_iowq(tw_req.req); > } > > void io_req_queue_iowq(struct io_kiocb *req) > @@ -1012,7 +1013,7 @@ static inline struct io_kiocb *io_req_find_next(struct io_kiocb *req) > return nxt; > } > > -static void ctx_flush_and_put(struct io_ring_ctx *ctx, struct io_tw_state *ts) > +static void ctx_flush_and_put(struct io_ring_ctx *ctx, io_tw_token_t tw) > { > if (!ctx) > return; > @@ -1042,24 +1043,25 @@ struct llist_node *io_handle_tw_list(struct llist_node *node, > io_task_work.node); > > if (req->ctx != ctx) { > - ctx_flush_and_put(ctx, &ts); > + ctx_flush_and_put(ctx, ts); > ctx = req->ctx; > mutex_lock(&ctx->uring_lock); > percpu_ref_get(&ctx->refs); > + ts.cancel = io_should_terminate_tw(ctx); > } > INDIRECT_CALL_2(req->io_task_work.func, > io_poll_task_func, io_req_rw_complete, > - req, &ts); > + (struct io_tw_req){req}, ts); > node = next; > (*count)++; > if (unlikely(need_resched())) { > - ctx_flush_and_put(ctx, &ts); > + ctx_flush_and_put(ctx, ts); > ctx = NULL; > cond_resched(); > } > } while (node && *count < max_entries); > > - ctx_flush_and_put(ctx, &ts); > + ctx_flush_and_put(ctx, ts); > return node; > } > > @@ -1280,7 +1282,7 @@ static bool io_run_local_work_continue(struct io_ring_ctx *ctx, int events, > return false; > } > > -static int __io_run_local_work(struct io_ring_ctx *ctx, struct io_tw_state *ts, > +static int __io_run_local_work(struct io_ring_ctx *ctx, io_tw_token_t tw, > int min_events) > { > struct llist_node *node; > @@ -1292,6 +1294,7 @@ static int __io_run_local_work(struct io_ring_ctx *ctx, struct io_tw_state *ts, > if (ctx->flags & IORING_SETUP_TASKRUN_FLAG) > atomic_andnot(IORING_SQ_TASKRUN, &ctx->rings->sq_flags); > again: > + tw.cancel = io_should_terminate_tw(ctx); > /* > * llists are in reverse order, flip it back the right way before > * running the pending items. > @@ -1303,7 +1306,7 @@ static int __io_run_local_work(struct io_ring_ctx *ctx, struct io_tw_state *ts, > io_task_work.node); > INDIRECT_CALL_2(req->io_task_work.func, > io_poll_task_func, io_req_rw_complete, > - req, ts); > + (struct io_tw_req){req}, tw); > ret++; > node = next; > } > @@ -1326,7 +1329,7 @@ static inline int io_run_local_work_locked(struct io_ring_ctx *ctx, > > if (llist_empty(&ctx->work_llist)) > return 0; > - return __io_run_local_work(ctx, &ts, min_events); > + return __io_run_local_work(ctx, ts, min_events); > } > > static int io_run_local_work(struct io_ring_ctx *ctx, int min_events) > @@ -1335,22 +1338,25 @@ static int io_run_local_work(struct io_ring_ctx *ctx, int min_events) > int ret; > > mutex_lock(&ctx->uring_lock); > - ret = __io_run_local_work(ctx, &ts, min_events); > + ret = __io_run_local_work(ctx, ts, min_events); > mutex_unlock(&ctx->uring_lock); > return ret; > } > > -static void io_req_task_cancel(struct io_kiocb *req, struct io_tw_state *ts) > +static void io_req_task_cancel(struct io_tw_req tw_req, io_tw_token_t tw) > { > - io_tw_lock(req->ctx, ts); > + struct io_kiocb *req = tw_req.req; > + > + io_tw_lock(req->ctx, tw); > io_req_defer_failed(req, req->cqe.res); > } > > -void io_req_task_submit(struct io_kiocb *req, struct io_tw_state *ts) > +void io_req_task_submit(struct io_tw_req tw_req, io_tw_token_t tw) > { > + struct io_kiocb *req = tw_req.req; > struct io_ring_ctx *ctx = req->ctx; > > - io_tw_lock(ctx, ts); > + io_tw_lock(ctx, tw); > if (unlikely(io_should_terminate_tw(ctx))) > io_req_defer_failed(req, -EFAULT); > else if (req->flags & REQ_F_FORCE_ASYNC) > @@ -1557,9 +1563,9 @@ static int io_iopoll_check(struct io_ring_ctx *ctx, long min) > return 0; > } > > -void io_req_task_complete(struct io_kiocb *req, struct io_tw_state *ts) > +void io_req_task_complete(struct io_tw_req tw_req, io_tw_token_t tw) > { > - io_req_complete_defer(req); > + io_req_complete_defer(tw_req.req); > } > > /* > @@ -1760,9 +1766,9 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags) > return ret; > } > > -int io_poll_issue(struct io_kiocb *req, struct io_tw_state *ts) > +int io_poll_issue(struct io_kiocb *req, io_tw_token_t tw) > { > - io_tw_lock(req->ctx, ts); > + io_tw_lock(req->ctx, tw); > return io_issue_sqe(req, IO_URING_F_NONBLOCK|IO_URING_F_MULTISHOT| > IO_URING_F_COMPLETE_DEFER); > } > diff --git a/io_uring/io_uring.h b/io_uring/io_uring.h > index f1d3b49289977..8439deda9ca47 100644 > --- a/io_uring/io_uring.h > +++ b/io_uring/io_uring.h > @@ -82,9 +82,9 @@ void io_req_task_work_add_remote(struct io_kiocb *req, struct io_ring_ctx *ctx, > unsigned flags); > bool io_alloc_async_data(struct io_kiocb *req); > void io_req_task_queue(struct io_kiocb *req); > -void io_req_task_complete(struct io_kiocb *req, struct io_tw_state *ts); > +void io_req_task_complete(struct io_tw_req tw_req, io_tw_token_t tw); > 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); > +void io_req_task_submit(struct io_tw_req tw_req, io_tw_token_t tw); > struct llist_node *io_handle_tw_list(struct llist_node *node, unsigned int *count, unsigned int max_entries); > struct llist_node *tctx_task_work_run(struct io_uring_task *tctx, unsigned int max_entries, unsigned int *count); > void tctx_task_work(struct callback_head *cb); > @@ -96,7 +96,7 @@ int io_ring_add_registered_file(struct io_uring_task *tctx, struct file *file, > int start, int end); > void io_req_queue_iowq(struct io_kiocb *req); > > -int io_poll_issue(struct io_kiocb *req, struct io_tw_state *ts); > +int io_poll_issue(struct io_kiocb *req, io_tw_token_t tw); > int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr); > int io_do_iopoll(struct io_ring_ctx *ctx, bool force_nonspin); > void __io_submit_flush_completions(struct io_ring_ctx *ctx); > @@ -347,7 +347,7 @@ static inline bool io_task_work_pending(struct io_ring_ctx *ctx) > return task_work_pending(current) || !llist_empty(&ctx->work_llist); > } > > -static inline void io_tw_lock(struct io_ring_ctx *ctx, struct io_tw_state *ts) > +static inline void io_tw_lock(struct io_ring_ctx *ctx, io_tw_token_t tw) > { > lockdep_assert_held(&ctx->uring_lock); > } > diff --git a/io_uring/msg_ring.c b/io_uring/msg_ring.c > index 3cf59d8c58073..5c7d35f2f0b86 100644 > --- a/io_uring/msg_ring.c > +++ b/io_uring/msg_ring.c > @@ -70,8 +70,9 @@ static inline bool io_msg_need_remote(struct io_ring_ctx *target_ctx) > return target_ctx->task_complete; > } > > -static void io_msg_tw_complete(struct io_kiocb *req, struct io_tw_state *ts) > +static void io_msg_tw_complete(struct io_tw_req tw_req, io_tw_token_t tw) > { > + struct io_kiocb *req = tw_req.req; > struct io_ring_ctx *ctx = req->ctx; > > io_add_aux_cqe(ctx, req->cqe.user_data, req->cqe.res, req->cqe.flags); > diff --git a/io_uring/notif.c b/io_uring/notif.c > index f50964c7dab82..cc9fc345da327 100644 > --- a/io_uring/notif.c > +++ b/io_uring/notif.c > @@ -11,8 +11,9 @@ > > static const struct ubuf_info_ops io_ubuf_ops; > > -static void io_notif_tw_complete(struct io_kiocb *notif, struct io_tw_state *ts) > +static void io_notif_tw_complete(struct io_tw_req tw_req, io_tw_token_t tw) > { > + struct io_kiocb *notif = tw_req.req; > struct io_notif_data *nd = io_notif_to_data(notif); > struct io_ring_ctx *ctx = notif->ctx; > > @@ -34,7 +35,7 @@ static void io_notif_tw_complete(struct io_kiocb *notif, struct io_tw_state *ts) > } > > nd = nd->next; > - io_req_task_complete(notif, ts); > + io_req_task_complete((struct io_tw_req){notif}, tw); > } while (nd); > } > > diff --git a/io_uring/poll.c b/io_uring/poll.c > index 002f1ae830b8a..39c7f47c1f5b5 100644 > --- a/io_uring/poll.c > +++ b/io_uring/poll.c > @@ -150,7 +150,7 @@ static void io_poll_req_insert_locked(struct io_kiocb *req) > hlist_add_head(&req->hash_node, &table->hbs[index].list); > } > > -static void io_poll_tw_hash_eject(struct io_kiocb *req, struct io_tw_state *ts) > +static void io_poll_tw_hash_eject(struct io_kiocb *req, io_tw_token_t tw) > { > struct io_ring_ctx *ctx = req->ctx; > > @@ -161,7 +161,7 @@ static void io_poll_tw_hash_eject(struct io_kiocb *req, struct io_tw_state *ts) > * already grabbed the mutex for us, but there is a chance it > * failed. > */ > - io_tw_lock(ctx, ts); > + io_tw_lock(ctx, tw); > hash_del(&req->hash_node); > req->flags &= ~REQ_F_HASH_LOCKED; > } else { > @@ -261,7 +261,7 @@ static inline void io_poll_execute(struct io_kiocb *req, int res) > * req->cqe.res. IOU_POLL_REMOVE_POLL_USE_RES indicates to remove multishot > * poll and that the result is stored in req->cqe. > */ > -static int io_poll_check_events(struct io_kiocb *req, struct io_tw_state *ts) > +static int io_poll_check_events(struct io_kiocb *req, io_tw_token_t tw) > { > int v; > > @@ -333,7 +333,7 @@ static int io_poll_check_events(struct io_kiocb *req, struct io_tw_state *ts) > /* multiple refs and HUP, ensure we loop once more */ > if ((req->cqe.res & (POLLHUP | POLLRDHUP)) && v != 1) > v--; > - ret = io_poll_issue(req, ts); > + ret = io_poll_issue(req, tw); > if (ret == IOU_STOP_MULTISHOT) > return IOU_POLL_REMOVE_POLL_USE_RES; > else if (ret == IOU_REQUEUE) > @@ -355,11 +355,12 @@ static int io_poll_check_events(struct io_kiocb *req, struct io_tw_state *ts) > return IOU_POLL_NO_ACTION; > } > > -void io_poll_task_func(struct io_kiocb *req, struct io_tw_state *ts) > +void io_poll_task_func(struct io_tw_req tw_req, io_tw_token_t tw) > { > + struct io_kiocb *req = tw_req.req; > int ret; > > - ret = io_poll_check_events(req, ts); > + ret = io_poll_check_events(req, tw); > if (ret == IOU_POLL_NO_ACTION) { > return; > } else if (ret == IOU_POLL_REQUEUE) { > @@ -367,7 +368,7 @@ void io_poll_task_func(struct io_kiocb *req, struct io_tw_state *ts) > return; > } > io_poll_remove_entries(req); > - io_poll_tw_hash_eject(req, ts); > + io_poll_tw_hash_eject(req, tw); > > if (req->opcode == IORING_OP_POLL_ADD) { > if (ret == IOU_POLL_DONE) { > @@ -376,7 +377,7 @@ void io_poll_task_func(struct io_kiocb *req, struct io_tw_state *ts) > poll = io_kiocb_to_cmd(req, struct io_poll); > req->cqe.res = mangle_poll(req->cqe.res & poll->events); > } else if (ret == IOU_POLL_REISSUE) { > - io_req_task_submit(req, ts); > + io_req_task_submit(tw_req, tw); > return; > } else if (ret != IOU_POLL_REMOVE_POLL_USE_RES) { > req->cqe.res = ret; > @@ -384,14 +385,14 @@ void io_poll_task_func(struct io_kiocb *req, struct io_tw_state *ts) > } > > io_req_set_res(req, req->cqe.res, 0); > - io_req_task_complete(req, ts); > + io_req_task_complete(tw_req, tw); > } else { > - io_tw_lock(req->ctx, ts); > + io_tw_lock(req->ctx, tw); > > if (ret == IOU_POLL_REMOVE_POLL_USE_RES) > - io_req_task_complete(req, ts); > + io_req_task_complete(tw_req, tw); > else if (ret == IOU_POLL_DONE || ret == IOU_POLL_REISSUE) > - io_req_task_submit(req, ts); > + io_req_task_submit(tw_req, tw); > else > io_req_defer_failed(req, ret); > } > diff --git a/io_uring/poll.h b/io_uring/poll.h > index b0e3745f5a29b..bf894dc6b9f86 100644 > --- a/io_uring/poll.h > +++ b/io_uring/poll.h > @@ -1,5 +1,7 @@ > // SPDX-License-Identifier: GPL-2.0 > > +#include <linux/io_uring_types.h> > + > #define IO_POLL_ALLOC_CACHE_MAX 32 > > enum { > @@ -43,4 +45,4 @@ int io_arm_poll_handler(struct io_kiocb *req, unsigned issue_flags); > bool io_poll_remove_all(struct io_ring_ctx *ctx, struct task_struct *tsk, > bool cancel_all); > > -void io_poll_task_func(struct io_kiocb *req, struct io_tw_state *ts); > +void io_poll_task_func(struct io_tw_req tw_req, io_tw_token_t tw); > diff --git a/io_uring/rw.c b/io_uring/rw.c > index a74664c4a05ef..55e5bff818b10 100644 > --- a/io_uring/rw.c > +++ b/io_uring/rw.c > @@ -508,8 +508,9 @@ static inline int io_fixup_rw_res(struct io_kiocb *req, long res) > return res; > } > > -void io_req_rw_complete(struct io_kiocb *req, struct io_tw_state *ts) > +void io_req_rw_complete(struct io_tw_req tw_req, io_tw_token_t tw) > { > + struct io_kiocb *req = tw_req.req; > struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw); > struct kiocb *kiocb = &rw->kiocb; > > @@ -525,7 +526,7 @@ void io_req_rw_complete(struct io_kiocb *req, struct io_tw_state *ts) > req->cqe.flags |= io_put_kbuf(req, req->cqe.res, NULL); > > io_req_rw_cleanup(req, 0); > - io_req_task_complete(req, ts); > + io_req_task_complete(tw_req, tw); > } > > static void io_complete_rw(struct kiocb *kiocb, long res) > diff --git a/io_uring/rw.h b/io_uring/rw.h > index 3f432dc75441f..283beeeb05fbc 100644 > --- a/io_uring/rw.h > +++ b/io_uring/rw.h > @@ -1,5 +1,6 @@ > // SPDX-License-Identifier: GPL-2.0 > > +#include <linux/io_uring_types.h> > #include <linux/pagemap.h> > > struct io_async_rw { > @@ -22,7 +23,7 @@ int io_read(struct io_kiocb *req, unsigned int issue_flags); > int io_write(struct io_kiocb *req, unsigned int issue_flags); > void io_readv_writev_cleanup(struct io_kiocb *req); > void io_rw_fail(struct io_kiocb *req); > -void io_req_rw_complete(struct io_kiocb *req, struct io_tw_state *ts); > +void io_req_rw_complete(struct io_tw_req tw_req, io_tw_token_t tw); > int io_read_mshot_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe); > int io_read_mshot(struct io_kiocb *req, unsigned int issue_flags); > void io_rw_cache_free(const void *entry); > diff --git a/io_uring/timeout.c b/io_uring/timeout.c > index 9cc5776103feb..1f0c66b643e3c 100644 > --- a/io_uring/timeout.c > +++ b/io_uring/timeout.c > @@ -65,8 +65,9 @@ static inline bool io_timeout_finish(struct io_timeout *timeout, > > static enum hrtimer_restart io_timeout_fn(struct hrtimer *timer); > > -static void io_timeout_complete(struct io_kiocb *req, struct io_tw_state *ts) > +static void io_timeout_complete(struct io_tw_req tw_req, io_tw_token_t tw) > { > + struct io_kiocb *req = tw_req.req; > struct io_timeout *timeout = io_kiocb_to_cmd(req, struct io_timeout); > struct io_timeout_data *data = req->async_data; > struct io_ring_ctx *ctx = req->ctx; > @@ -83,7 +84,7 @@ static void io_timeout_complete(struct io_kiocb *req, struct io_tw_state *ts) > } > } > > - io_req_task_complete(req, ts); > + io_req_task_complete(tw_req, tw); > } > > static bool io_kill_timeout(struct io_kiocb *req, int status) > @@ -138,9 +139,11 @@ __cold void io_flush_timeouts(struct io_ring_ctx *ctx) > spin_unlock_irq(&ctx->timeout_lock); > } > > -static void io_req_tw_fail_links(struct io_kiocb *link, struct io_tw_state *ts) > +static void io_req_tw_fail_links(struct io_tw_req tw_req, io_tw_token_t tw) > { > - io_tw_lock(link->ctx, ts); > + struct io_kiocb *link = tw_req.req; > + > + io_tw_lock(link->ctx, tw); > while (link) { > struct io_kiocb *nxt = link->link; > long res = -ECANCELED; > @@ -149,7 +152,7 @@ static void io_req_tw_fail_links(struct io_kiocb *link, struct io_tw_state *ts) > res = link->cqe.res; > link->link = NULL; > io_req_set_res(link, res, 0); > - io_req_task_complete(link, ts); > + io_req_task_complete((struct io_tw_req){link}, tw); > link = nxt; > } > } > @@ -296,8 +299,9 @@ int io_timeout_cancel(struct io_ring_ctx *ctx, struct io_cancel_data *cd) > return 0; > } > > -static void io_req_task_link_timeout(struct io_kiocb *req, struct io_tw_state *ts) > +static void io_req_task_link_timeout(struct io_tw_req tw_req, io_tw_token_t tw) > { > + struct io_kiocb *req = tw_req.req; > struct io_timeout *timeout = io_kiocb_to_cmd(req, struct io_timeout); > struct io_kiocb *prev = timeout->prev; > int ret = -ENOENT; > @@ -312,11 +316,11 @@ static void io_req_task_link_timeout(struct io_kiocb *req, struct io_tw_state *t > 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_req_task_complete(tw_req, tw); > io_put_req(prev); > } else { > io_req_set_res(req, -ETIME, 0); > - io_req_task_complete(req, ts); > + io_req_task_complete(tw_req, tw); > } > } > > diff --git a/io_uring/uring_cmd.c b/io_uring/uring_cmd.c > index e5d0cc8a8a562..0dfd27963fc0d 100644 > --- a/io_uring/uring_cmd.c > +++ b/io_uring/uring_cmd.c > @@ -113,8 +113,9 @@ void io_uring_cmd_mark_cancelable(struct io_uring_cmd *cmd, > } > EXPORT_SYMBOL_GPL(io_uring_cmd_mark_cancelable); > > -static void io_uring_cmd_work(struct io_kiocb *req, struct io_tw_state *ts) > +static void io_uring_cmd_work(struct io_tw_req tw_req, io_tw_token_t tw) > { > + struct io_kiocb *req = tw_req.req; > struct io_uring_cmd *ioucmd = io_kiocb_to_cmd(req, struct io_uring_cmd); > unsigned int flags = IO_URING_F_COMPLETE_DEFER; > > diff --git a/io_uring/waitid.c b/io_uring/waitid.c > index b67b6d5bc6830..f3222cb30cdea 100644 > --- a/io_uring/waitid.c > +++ b/io_uring/waitid.c > @@ -16,7 +16,7 @@ > #include "waitid.h" > #include "../kernel/exit.h" > > -static void io_waitid_cb(struct io_kiocb *req, struct io_tw_state *ts); > +static void io_waitid_cb(struct io_tw_req tw_req, io_tw_token_t tw); > > #define IO_WAITID_CANCEL_FLAG BIT(31) > #define IO_WAITID_REF_MASK GENMASK(30, 0) > @@ -236,13 +236,14 @@ static inline bool io_waitid_drop_issue_ref(struct io_kiocb *req) > return true; > } > > -static void io_waitid_cb(struct io_kiocb *req, struct io_tw_state *ts) > +static void io_waitid_cb(struct io_tw_req tw_req, io_tw_token_t tw) > { > + struct io_kiocb *req = tw_req.req; > struct io_waitid_async *iwa = req->async_data; > struct io_ring_ctx *ctx = req->ctx; > int ret; > > - io_tw_lock(ctx, ts); > + io_tw_lock(ctx, tw); > > ret = __do_wait(&iwa->wo); > > @@ -271,7 +272,7 @@ static void io_waitid_cb(struct io_kiocb *req, struct io_tw_state *ts) > } > > io_waitid_complete(req, ret); > - io_req_task_complete(req, ts); > + io_req_task_complete(tw_req, tw); > } > > static int io_waitid_wait(struct wait_queue_entry *wait, unsigned mode, > -- > 2.53.0 > ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 6.12.y 3/3] io_uring/waitid: honor task_work cancellation 2026-09-09 16:27 ` [PATCH 6.12.y 1/3] io_uring/waitid: have io_waitid_complete() remove wait queue entry Sasha Levin 2026-09-09 16:27 ` [PATCH 6.12.y 2/3] io_uring: add wrapper type for io_req_tw_func_t arg Sasha Levin @ 2026-09-09 16:27 ` Sasha Levin 1 sibling, 0 replies; 5+ messages in thread From: Sasha Levin @ 2026-09-09 16:27 UTC (permalink / raw) To: stable; +Cc: Hui Su, Jens Axboe, Sasha Levin From: Hui Su <sh_def@163.com> [ Upstream commit 14572de82e5022899e5856008bc9cac97004a88c ] io_waitid_cb() may run through the fallback task_work path when task_work_add() can no longer queue work to the originating task. The fallback runs from a kworker and io_uring marks such task work as canceled through tw.cancel. io_waitid_cb() currently ignores tw.cancel and calls __do_wait(). waitid is task-context dependent: __do_wait() performs child lookup relative to current, and the retry path also uses current->signal->wait_chldexit. If the callback runs from the fallback kworker, current is therefore not the task that submitted the request. Honor tw.cancel before entering __do_wait(). Complete the request with -ECANCELED and skip the siginfo copy, since canceled task work may run without the submitting task's userspace execution context. Keep the existing siginfo handling for normal waitid completion and explicit cancellation. Fixes: f31ecf671ddc ("io_uring: add IORING_OP_WAITID support") Cc: stable@vger.kernel.org Signed-off-by: Hui Su <sh_def@163.com> Link: https://patch.msgid.link/20260818103336.1922818-2-sh_def@163.com Signed-off-by: Jens Axboe <axboe@kernel.dk> Signed-off-by: Sasha Levin <sashal@kernel.org> --- io_uring/waitid.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/io_uring/waitid.c b/io_uring/waitid.c index f3222cb30cdea..d0ae5194191bf 100644 --- a/io_uring/waitid.c +++ b/io_uring/waitid.c @@ -131,7 +131,7 @@ static void io_waitid_remove_wq(struct io_kiocb *req) } } -static void io_waitid_complete(struct io_kiocb *req, int ret) +static void io_waitid_complete(struct io_kiocb *req, int ret, bool copy_si) { struct io_waitid *iw = io_kiocb_to_cmd(req, struct io_waitid); @@ -143,7 +143,10 @@ static void io_waitid_complete(struct io_kiocb *req, int ret) hlist_del_init(&req->hash_node); io_waitid_remove_wq(req); - ret = io_waitid_finish(req, ret); + if (copy_si) + ret = io_waitid_finish(req, ret); + else + io_waitid_free(req); if (ret < 0) req_set_fail(req); io_req_set_res(req, ret, 0); @@ -165,7 +168,7 @@ static bool __io_waitid_cancel(struct io_ring_ctx *ctx, struct io_kiocb *req) if (atomic_fetch_inc(&iw->refs) & IO_WAITID_REF_MASK) return false; - io_waitid_complete(req, -ECANCELED); + io_waitid_complete(req, -ECANCELED, true); io_req_queue_tw_complete(req, -ECANCELED); return true; } @@ -244,6 +247,11 @@ static void io_waitid_cb(struct io_tw_req tw_req, io_tw_token_t tw) int ret; io_tw_lock(ctx, tw); + if (unlikely(tw.cancel)) { + io_waitid_complete(req, -ECANCELED, false); + io_req_task_complete(tw_req, tw); + return; + } ret = __do_wait(&iwa->wo); @@ -271,7 +279,7 @@ static void io_waitid_cb(struct io_tw_req tw_req, io_tw_token_t tw) } } - io_waitid_complete(req, ret); + io_waitid_complete(req, ret, true); io_req_task_complete(tw_req, tw); } -- 2.53.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-09 16:53 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-09-03 14:52 FAILED: patch "[PATCH] io_uring/waitid: honor task_work cancellation" failed to apply to 6.12-stable tree gregkh 2026-09-09 16:27 ` [PATCH 6.12.y 1/3] io_uring/waitid: have io_waitid_complete() remove wait queue entry Sasha Levin 2026-09-09 16:27 ` [PATCH 6.12.y 2/3] io_uring: add wrapper type for io_req_tw_func_t arg Sasha Levin 2026-09-09 16:52 ` Caleb Sander Mateos 2026-09-09 16:27 ` [PATCH 6.12.y 3/3] io_uring/waitid: honor task_work cancellation Sasha Levin
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox