FAILED: patch "[PATCH] io_uring: ensure ctx->rings is stable for task work flags" failed to apply to 6.19-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.19-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 <[email protected]>.
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.19.y
git checkout FETCH_HEAD
git cherry-pick -x 96189080265e6bb5dde3a4afbaf947af493e3f82
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026031717-scowling-sandfish-e480@gregkh' --subject-prefix 'PATCH 6.19.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 96189080265e6bb5dde3a4afbaf947af493e3f82 Mon Sep 17 00:00:00 2001
From: Jens Axboe <[email protected]>
Date: Mon, 9 Mar 2026 14:21:37 -0600
Subject: [PATCH] io_uring: ensure ctx->rings is stable for task work flags
manipulation
If DEFER_TASKRUN | SETUP_TASKRUN is used and task work is added while the ring is being resized, it's possible for the OR'ing of IORING_SQ_TASKRUN to happen in the small window of swapping into the new rings and the old rings being freed.
Prevent this by adding a 2nd ->rings pointer, ->rings_rcu, which is protected by RCU. The task work flags manipulation is inside RCU already, and if the resize ring freeing is done post an RCU synchronize, then there's no need to add locking to the fast path of task work additions.
Note: this is only done for DEFER_TASKRUN, as that's the only setup mode that supports ring resizing. If this ever changes, then they too need to use the io_ctx_mark_taskrun() helper.
include/linux/io_uring_types.h +1 -0
@@ -388,6 +388,7 @@ struct io_ring_ctx {* regularly bounce b/w CPUs. */ struct { +struct io_rings __rcu *rings_rcu;struct llist_head work_llist;struct llist_head retry_llist;unsigned long check_cq;
io_uring/io_uring.c +2 -0
@@ -2066,6 +2066,7 @@ static void io_rings_free(struct io_ring_ctx *ctx)io_free_region (ctx->user, &ctx->sq_region);io_free_region (ctx->user, &ctx->ring_region); ctx->rings =NULL ; +RCU_INIT_POINTER (ctx->rings_rcu,NULL ); ctx->sq_sqes =NULL ; }
@@ -2703,6 +2704,7 @@ static __cold int io_allocate_scq_urings(struct io_ring_ctx *ctx,if (ret)return ret; ctx->rings = rings =io_region_get_ptr (&ctx->ring_region); +rcu_assign_pointer (ctx->rings_rcu, rings);if (!(ctx->flags &IORING_SETUP_NO_SQARRAY )) ctx->sq_array = (u32 *)((char *)rings + rl->sq_array_offset);
io_uring/register.c +11 -0
@@ -633,7 +633,15 @@ static int io_register_resize_rings(struct io_ring_ctx *ctx, void __user *arg) ctx->sq_entries = p->sq_entries; ctx->cq_entries = p->cq_entries; +/* +* Just mark any flag we may have missed and that the application +* should act on unconditionally. Worst case it'll be an extra +* syscall. +*/ +atomic_or (IORING_SQ_TASKRUN |IORING_SQ_NEED_WAKEUP , &n.rings->sq_flags); ctx->rings = n.rings; +rcu_assign_pointer (ctx->rings_rcu, n.rings); + ctx->sq_sqes = n.sq_sqes;swap_old (ctx, o, n, ring_region);swap_old (ctx, o, n, sq_region);
@@ -642,6 +650,9 @@ static int io_register_resize_rings(struct io_ring_ctx *ctx, void __user *arg) out:spin_unlock (&ctx->completion_lock);mutex_unlock (&ctx->mmap_lock); +/* Wait for concurrent io_ctx_mark_taskrun() */ +if (to_free == &o) +synchronize_rcu_expedited ();io_register_free_rings (ctx, to_free);if (ctx->sq_data)
io_uring/tw.c +20 -2
@@ -152,6 +152,21 @@ void tctx_task_work(struct callback_head *cb)WARN_ON_ONCE (ret); } +/* +* Sets IORING_SQ_TASKRUN in the sq_flags shared with userspace, using the +* RCU protected rings pointer to be safe against concurrent ring resizing. +*/ +static void io_ctx_mark_taskrun (struct io_ring_ctx *ctx) +{ +lockdep_assert_in_rcu_read_lock (); + +if (ctx->flags &IORING_SETUP_TASKRUN_FLAG ) { +struct io_rings *rings =rcu_dereference (ctx->rings_rcu); + +atomic_or (IORING_SQ_TASKRUN , &rings->sq_flags); + } +} +void io_req_local_work_add (struct io_kiocb *req,unsigned flags) {struct io_ring_ctx *ctx = req->ctx;
@@ -206,8 +221,7 @@ void io_req_local_work_add(struct io_kiocb *req, unsigned flags)*/ if (!head) { -if (ctx->flags &IORING_SETUP_TASKRUN_FLAG ) -atomic_or (IORING_SQ_TASKRUN , &ctx->rings->sq_flags); +io_ctx_mark_taskrun (ctx);if (ctx->has_evfd)io_eventfd_signal (ctx, false); }
@@ -231,6 +245,10 @@ void io_req_normal_work_add(struct io_kiocb *req)if (!llist_add (&req->io_task_work.node, &tctx->task_list))return ; +/* +* Doesn't need to use ->rings_rcu, as resizing isn't supported for +* !DEFER_TASKRUN. +*/ if (ctx->flags &IORING_SETUP_TASKRUN_FLAG )atomic_or (IORING_SQ_TASKRUN , &ctx->rings->sq_flags);
Jens Axboe
maintainerQuoted 5 lines · On 3/17/26 6:55 AM, [email protected] wrote:
The patch below does not apply to the 6.19-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 <[email protected]>.
Here's this one and the following one too, for 6.19.
Signature · 1 line
Jens Axboe
From 70710c220b58e84e08b924833d4caeb1d1500859 Mon Sep 17 00:00:00 2001
From: Jens Axboe <[email protected]>
Date: Mon, 9 Mar 2026 14:35:49 -0600
Subject: [PATCH 2/2] io_uring/eventfd: use ctx->rings_rcu for flags checking
Commit 177c69432161f6e4bab07ccacf8a1748a6898a6b upstream.
Similarly to what commit e78f7b70e837 did for local task work additions, use ->rings_rcu under RCU rather than dereference ->rings directly. See that commit for more details.
--- io_uring/eventfd.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
io_uring/eventfd.c +7 -3
@@ -76,11 +76,15 @@ void io_eventfd_signal(struct io_ring_ctx *ctx, bool cqe_event) {bool skip = false;struct io_ev_fd *ev_fd; - -if (READ_ONCE (ctx->rings->cq_flags) &IORING_CQ_EVENTFD_DISABLED ) -return ; +struct io_rings *rings;guard (rcu)(); + + rings =rcu_dereference (ctx->rings_rcu); +if (!rings) +return ; +if (READ_ONCE (rings->cq_flags) &IORING_CQ_EVENTFD_DISABLED ) +return ; ev_fd =rcu_dereference (ctx->io_ev_fd);/* * Check again if ev_fd exists in case an io_eventfd_unregister call
Signature · 1 line
2.53.0
From 8e746bd1dadaf93542ca59dc971b233332c46ca6 Mon Sep 17 00:00:00 2001
From: Jens Axboe <[email protected]>
Date: Mon, 9 Mar 2026 14:21:37 -0600
Subject: [PATCH 1/2] io_uring: ensure ctx->rings is stable for task work flags
manipulation
Commit 96189080265e6bb5dde3a4afbaf947af493e3f82 upstream.
If DEFER_TASKRUN | SETUP_TASKRUN is used and task work is added while the ring is being resized, it's possible for the OR'ing of IORING_SQ_TASKRUN to happen in the small window of swapping into the new rings and the old rings being freed.
Prevent this by adding a 2nd ->rings pointer, ->rings_rcu, which is protected by RCU. The task work flags manipulation is inside RCU already, and if the resize ring freeing is done post an RCU synchronize, then there's no need to add locking to the fast path of task work additions.
Note: this is only done for DEFER_TASKRUN, as that's the only setup mode that supports ring resizing. If this ever changes, then they too need to use the io_ctx_mark_taskrun() helper.
--- include/linux/io_uring_types.h | 1 + io_uring/io_uring.c | 24 ++++++++++++++++++++++-- io_uring/register.c | 12 ++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-)
include/linux/io_uring_types.h +1 -0
@@ -371,6 +371,7 @@ struct io_ring_ctx {* regularly bounce b/w CPUs. */ struct { +struct io_rings __rcu *rings_rcu;struct llist_head work_llist;struct llist_head retry_llist;unsigned long check_cq;
io_uring/io_uring.c +22 -2
@@ -1202,6 +1202,21 @@ void tctx_task_work(struct callback_head *cb)WARN_ON_ONCE (ret); } +/* +* Sets IORING_SQ_TASKRUN in the sq_flags shared with userspace, using the +* RCU protected rings pointer to be safe against concurrent ring resizing. +*/ +static void io_ctx_mark_taskrun (struct io_ring_ctx *ctx) +{ +lockdep_assert_in_rcu_read_lock (); + +if (ctx->flags &IORING_SETUP_TASKRUN_FLAG ) { +struct io_rings *rings =rcu_dereference (ctx->rings_rcu); + +atomic_or (IORING_SQ_TASKRUN , &rings->sq_flags); + } +} +static void io_req_local_work_add (struct io_kiocb *req,unsigned flags) {struct io_ring_ctx *ctx = req->ctx;
@@ -1256,8 +1271,7 @@ static void io_req_local_work_add(struct io_kiocb *req, unsigned flags)*/ if (!head) { -if (ctx->flags &IORING_SETUP_TASKRUN_FLAG ) -atomic_or (IORING_SQ_TASKRUN , &ctx->rings->sq_flags); +io_ctx_mark_taskrun (ctx);if (ctx->has_evfd)io_eventfd_signal (ctx, false); }
@@ -1281,6 +1295,10 @@ static void io_req_normal_work_add(struct io_kiocb *req)if (!llist_add (&req->io_task_work.node, &tctx->task_list))return ; +/* +* Doesn't need to use ->rings_rcu, as resizing isn't supported for +* !DEFER_TASKRUN. +*/ if (ctx->flags &IORING_SETUP_TASKRUN_FLAG )atomic_or (IORING_SQ_TASKRUN , &ctx->rings->sq_flags);
@@ -2760,6 +2778,7 @@ static void io_rings_free(struct io_ring_ctx *ctx)io_free_region (ctx->user, &ctx->sq_region);io_free_region (ctx->user, &ctx->ring_region); ctx->rings =NULL ; +RCU_INIT_POINTER (ctx->rings_rcu,NULL ); ctx->sq_sqes =NULL ; }
@@ -3389,6 +3408,7 @@ static __cold int io_allocate_scq_urings(struct io_ring_ctx *ctx,if (ret)return ret; ctx->rings = rings =io_region_get_ptr (&ctx->ring_region); +rcu_assign_pointer (ctx->rings_rcu, rings);if (!(ctx->flags &IORING_SETUP_NO_SQARRAY )) ctx->sq_array = (u32 *)((char *)rings + rl->sq_array_offset);
io_uring/register.c +12 -0
@@ -545,7 +545,15 @@ static int io_register_resize_rings(struct io_ring_ctx *ctx, void __user *arg) ctx->sq_entries = p->sq_entries; ctx->cq_entries = p->cq_entries; +/* +* Just mark any flag we may have missed and that the application +* should act on unconditionally. Worst case it'll be an extra +* syscall. +*/ +atomic_or (IORING_SQ_TASKRUN |IORING_SQ_NEED_WAKEUP , &n.rings->sq_flags); ctx->rings = n.rings; +rcu_assign_pointer (ctx->rings_rcu, n.rings); + ctx->sq_sqes = n.sq_sqes;swap_old (ctx, o, n, ring_region);swap_old (ctx, o, n, sq_region);
@@ -554,6 +562,10 @@ static int io_register_resize_rings(struct io_ring_ctx *ctx, void __user *arg) out:spin_unlock (&ctx->completion_lock);mutex_unlock (&ctx->mmap_lock); + +/* Wait for concurrent io_ctx_mark_taskrun() */ +if (to_free == &o) +synchronize_rcu_expedited ();io_register_free_rings (ctx, to_free);if (ctx->sq_data)
Signature · 1 line
2.53.0
Quoted 8 lines · Jens Axboe
Quoted 5 lines · On 3/17/26 6:55 AM, [email protected] wrote:
The patch below does not apply to the 6.19-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 <[email protected]>.Here's this one and the following one too, for 6.19.
Applied, thanks!
greg k-h