io_uring/io-wq.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
From: Mark Amirkan <markdamirkan@gmail.com>
io_wq_worker_cancel() asks io_acct_for_each_worker() to stop after one
match when cancel_all is clear. io_acct_cancel_running_work() discards
that result, so io_wq_cancel_running_work() continues into the other
worker account with nr_running already set.
If the first worker there also matches, one non-ALL cancel signals a
request in both the bounded and unbounded accounts.
Return the iterator result and stop scanning accounts after a match.
IORING_ASYNC_CANCEL_ALL is unchanged because its callback does not stop
the iteration.
A test runs a blocking open in the bounded account and a blocking
pipe-to-pipe splice in the unbounded account with the same user_data.
Before this change both operations are interrupted by one cancel. After
the change only the open is interrupted, and the splice completes when
input is supplied.
Fixes: 751eedc4b4b7 ("io_uring/io-wq: move worker lists to struct io_wq_acct")
Cc: stable@vger.kernel.org
Assisted-by: Symbolic
Signed-off-by: Mark Amirkan <markdamirkan@gmail.com>
---
io_uring/io-wq.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
index 2ca223e47d..9f9039e3d3 100644
--- a/io_uring/io-wq.c
+++ b/io_uring/io-wq.c
@@ -1181,12 +1181,15 @@ static void io_wq_cancel_pending_work(struct io_wq *wq,
}
}
-static void io_acct_cancel_running_work(struct io_wq_acct *acct,
+static bool io_acct_cancel_running_work(struct io_wq_acct *acct,
struct io_cb_cancel_data *match)
{
+ bool ret;
+
raw_spin_lock(&acct->workers_lock);
- io_acct_for_each_worker(acct, io_wq_worker_cancel, match);
+ ret = io_acct_for_each_worker(acct, io_wq_worker_cancel, match);
raw_spin_unlock(&acct->workers_lock);
+ return ret;
}
static void io_wq_cancel_running_work(struct io_wq *wq,
@@ -1195,7 +1198,8 @@ static void io_wq_cancel_running_work(struct io_wq *wq,
rcu_read_lock();
for (int i = 0; i < IO_WQ_ACCT_NR; i++)
- io_acct_cancel_running_work(&wq->acct[i], match);
+ if (io_acct_cancel_running_work(&wq->acct[i], match))
+ break;
rcu_read_unlock();
}
---
base-commit: 11773ae6da9c9d92f5d1def78f7e70a9f1fa7b1c
change-id: 20260913-b4-send-io-wq-cancel-507488f72245
Best regards,
--
Mark Amirkan <markdamirkan@gmail.com>
On Sun, Sep 13, 2026 at 12:30 PM Mark Amirkan via B4 Relay
<devnull+markdamirkan.gmail.com@kernel.org> wrote:
> io_wq_worker_cancel() asks io_acct_for_each_worker() to stop after one
> match when cancel_all is clear. io_acct_cancel_running_work() discards
> that result, so io_wq_cancel_running_work() continues into the other
> worker account with nr_running already set.
>
> If the first worker there also matches, one non-ALL cancel signals a
> request in both the bounded and unbounded accounts.
>
> Return the iterator result and stop scanning accounts after a match.
> IORING_ASYNC_CANCEL_ALL is unchanged because its callback does not stop
> the iteration.
>
> A test runs a blocking open in the bounded account and a blocking
> pipe-to-pipe splice in the unbounded account with the same user_data.
> Before this change both operations are interrupted by one cancel. After
> the change only the open is interrupted, and the splice completes when
> input is supplied.
>
> Fixes: 751eedc4b4b7 ("io_uring/io-wq: move worker lists to struct io_wq_acct")
Thanks, that is indeed a detail that I have missed in my commit.
(Bonus points for documenting the new io_acct_cancel_running_work()
return value. That's missing too often in the kernel, even in my own
commit.)
Reviewed-by: Max Kellermann <max.kellermann@ionos.com>
--
Max Kellermann
Principal Architect
Hosting Technology
cm4all | Im Mediapark 6a | 50670 Köln | Germany
General information about the company can be found here:
https://www.cm4all.com/impressum
A member of the IONOS Group
© 2016 - 2026 Red Hat, Inc.