mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
io_uring/cancel: don't forget that the work was running
If the request to be cancelled is being run by io-wq, io_async_cancel_one() flags the work, signals the worker and returns -EALREADY. Since commitccbf726171("io_uring: perform poll removal even if async work removal is successful") io_try_cancel() goes on regardless, as poll may be armed for the request. If it isn't, and nothing else turns up, the result is the -ENOENT of the last search. __io_async_cancel() makes up for that. On -ENOENT it tries the io-wq of every task that uses the ring, and that of the caller is one of them. So the work is found a second time, and the result is -EALREADY after all. Unless the worker is done with it by then. If the work is flagged before io_wq_submit_work() looks at IO_WQ_WORK_CANCEL, all the worker does is to queue the request to be failed by task work. Then it isn't anywhere to be found until the task work has run, and - IORING_REGISTER_SYNC_CANCEL returns -ENOENT, - IORING_OP_ASYNC_CANCEL completes with -ENOENT, for a request that they have just cancelled, and that completes with -ECANCELED. With a read of an empty pipe, IOSQE_ASYNC, and a sync cancel right behind it, 1 to 3 of 1,000,000 calls return -ENOENT, and 50 to 110 with IORING_SETUP_DEFER_TASKRUN (4 CPUs, KVM). Nothing is written to the pipe, and all of these reads end with -ECANCELED. Remember that the work was found. IORING_OP_ASYNC_CANCEL completes with -EALREADY, as it does if the worker is a bit slower. For IORING_REGISTER_SYNC_CANCEL it means to try again, which ends with 0. The result of a linked timeout, which calls io_try_cancel() by itself, and the number that is returned for IORING_ASYNC_CANCEL_ALL and _ANY don't change. Fixes:ccbf726171("io_uring: perform poll removal even if async work removal is successful") Assisted-by: LLM Signed-off-by: Josef Bacik <josef@toxicpanda.com> Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-16-c1556f5d7bf5@toxicpanda.com Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
5cb049d220
commit
8aa0e5db48
|
|
@ -117,6 +117,8 @@ int io_try_cancel(struct io_uring_task *tctx, struct io_cancel_data *cd,
|
|||
*/
|
||||
if (!ret)
|
||||
return 0;
|
||||
if (ret == -EALREADY)
|
||||
cd->running = true;
|
||||
|
||||
ret = io_poll_cancel(ctx, cd, issue_flags);
|
||||
if (ret != -ENOENT)
|
||||
|
|
@ -180,6 +182,7 @@ static int __io_async_cancel(struct io_cancel_data *cd,
|
|||
struct io_tctx_node *node;
|
||||
int ret, nr = 0;
|
||||
|
||||
cd->running = false;
|
||||
do {
|
||||
ret = io_try_cancel(tctx, cd, issue_flags);
|
||||
if (ret == -ENOENT)
|
||||
|
|
@ -204,7 +207,15 @@ static int __io_async_cancel(struct io_cancel_data *cd,
|
|||
}
|
||||
mutex_unlock(&ctx->tctx_lock);
|
||||
io_ring_submit_unlock(ctx, issue_flags);
|
||||
return all ? nr : ret;
|
||||
if (all)
|
||||
return nr;
|
||||
/*
|
||||
* The worker may be done with the work that was found running, and
|
||||
* flagged, above. That isn't the same as not having found it.
|
||||
*/
|
||||
if (ret == -ENOENT && cd->running)
|
||||
ret = -EALREADY;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int io_async_cancel(struct io_kiocb *req, unsigned int issue_flags)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ struct io_cancel_data {
|
|||
u8 opcode;
|
||||
u32 flags;
|
||||
int seq;
|
||||
/* io_try_cancel() has found it being run by io-wq, and flagged it */
|
||||
bool running;
|
||||
};
|
||||
|
||||
int io_async_cancel_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user