mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
io_uring/cancel: drop sqd->lock while SQPOLL waits
An exiting SQPOLL thread calls io_uring_cancel_generic() with sqd->lock
held, and keeps it across cond_resched() and the sleep until nothing is
in flight anymore. A zero-copy send notification can't be canceled and
stays pending as long as the peer doesn't read, so the lock is held for
as long as a user cares to. io_ring_exit_work() needs it to park the
thread, and so do IORING_REGISTER_IOWQ_AFF and IOWQ_MAX_WORKERS. With
64 such rings sharing the thread, every iou_exit slot sleeps on the lock
and no ring gets freed for anyone.
Drop the lock around cond_resched() and the sleep, like the main loop of
the thread does. Each walk of ctx_list is still under one hold, and
nothing is kept across the unlock. The thread drops its io-wq, frees its
tctx and clears sqd->thread under one hold after the loop, so whoever
holds the lock and finds sqd->thread set can still use them.
Fixes: 63809137eb ("io_uring: flush notifiers after sendzc")
Signed-off-by: Chris Mason <mason@kernel.org>
[axboe: adapted to the schedule_timeout() based loop]
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
360395e8db
commit
cb0306c52f
|
|
@ -703,7 +703,16 @@ __cold void io_uring_cancel_generic(bool cancel_all, struct io_sq_data *sqd)
|
|||
}
|
||||
|
||||
if (loop) {
|
||||
/*
|
||||
* A request that runs in io-wq and doesn't end makes
|
||||
* each round come here. Don't hold sqd->lock for that
|
||||
* long either.
|
||||
*/
|
||||
if (sqd)
|
||||
mutex_unlock(&sqd->lock);
|
||||
cond_resched();
|
||||
if (sqd)
|
||||
mutex_lock(&sqd->lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -741,7 +750,18 @@ __cold void io_uring_cancel_generic(bool cancel_all, struct io_sq_data *sqd)
|
|||
|
||||
if (state & TASK_INTERRUPTIBLE)
|
||||
timeout = MAX_SCHEDULE_TIMEOUT;
|
||||
/*
|
||||
* A zero-copy notification can't be canceled, and a
|
||||
* peer that doesn't read keeps it pending. Don't hold
|
||||
* sqd->lock while waiting for it, or whoever wants the
|
||||
* lock sleeps as long. No ring is used across this,
|
||||
* the next round walks ->ctx_list anew.
|
||||
*/
|
||||
if (sqd)
|
||||
mutex_unlock(&sqd->lock);
|
||||
schedule_timeout(timeout);
|
||||
if (sqd)
|
||||
mutex_lock(&sqd->lock);
|
||||
}
|
||||
end_wait:
|
||||
finish_wait(&tctx->wait, &wait);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user