mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-10-26 10:57:38 +08:00
io_uring/timeout: don't skip over reached sequences
io_flush_timeouts() copes with the wrap of the CQ sequence by looking at
the distance from ->cq_last_tm_flush. That relies on the target of each
pending timeout being at or ahead of the last flush.
io_timeout() breaks that. It sets ->cq_last_tm_flush to the current
sequence, on the assumption that nothing has been posted since the last
flush. That isn't true. A multishot request posts its CQEs right when it
is issued or retried, but they are committed, and the timeouts flushed,
at the end of the submission or task_work run:
X: IORING_OP_TIMEOUT, off == 1 target_seq == N + 1
io_submit_sqes()
multishot recv, posts two CQEs sequence == N + 2
Y: IORING_OP_TIMEOUT, off == 100 ->cq_last_tm_flush = N + 2
io_flush_timeouts()
X: events_needed = N + 1 - (N + 2) // 0xffffffff
events_got = 0 // not yet
X isn't completed although the completions it was waiting for have
happened, only its timer can end it now. And as the list is sorted and
the walk ends at the first timeout that isn't due, it gets in the way of
the ones behind it. The insertion sort has the same problem, it measures
the distance of the pending targets from the current sequence.
->cq_last_tm_flush only needs to be brought up to date if no timeout is
waiting for a sequence, as io_flush_timeouts() isn't called before the
first one is set up. Otherwise it is current enough. Timeouts with a
sequence come first in the list, so looking at the first entry will do.
Use the last flush as the base for the insertion sort as well.
Fixes: f010505b78 ("io_uring: flush timeouts that should already have expired")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-29-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
1 parent
e4507a62c2
commit
c6c0b3eba8
1 file changed
+14
-7
+14
-7
@@ -645,8 +645,9 @@ int io_timeout(struct io_kiocb *req, unsigned int issue_flags)
|
||||
struct io_timeout *timeout = io_kiocb_to_cmd(req, struct io_timeout);
|
||||
struct io_ring_ctx *ctx = req->ctx;
|
||||
struct io_timeout_data *data = req->async_data;
|
||||
struct io_timeout *first;
|
||||
struct list_head *entry;
|
||||
u32 tail, off = timeout->off;
|
||||
u32 tail, base, off = timeout->off;
|
||||
|
||||
raw_spin_lock_irq(&ctx->timeout_lock);
|
||||
|
||||
@@ -663,11 +664,17 @@ int io_timeout(struct io_kiocb *req, unsigned int issue_flags)
|
||||
tail = data_race(ctx->cached_cq_tail) - atomic_read(&ctx->cq_timeouts);
|
||||
timeout->target_seq = tail + off;
|
||||
|
||||
/* Update the last seq here in case io_flush_timeouts() hasn't.
|
||||
* This is safe because ->completion_lock is held, and submissions
|
||||
* and completions are never mixed in the same ->completion_lock section.
|
||||
/*
|
||||
* Update the last seq here in case io_flush_timeouts() hasn't, it only
|
||||
* gets called once sequences are in use. Leave it alone if there are
|
||||
* timeouts waiting for one. CQEs may have been posted since the last
|
||||
* flush, and a target that has been reached must not fall behind it.
|
||||
*/
|
||||
ctx->cq_last_tm_flush = tail;
|
||||
first = list_first_entry_or_null(&ctx->timeout_list, struct io_timeout,
|
||||
list);
|
||||
if (!first || io_is_timeout_noseq(cmd_to_io_kiocb(first)))
|
||||
ctx->cq_last_tm_flush = tail;
|
||||
base = ctx->cq_last_tm_flush;
|
||||
|
||||
/*
|
||||
* Insertion sort, ensuring the first entry in the list is always
|
||||
@@ -679,8 +686,8 @@ int io_timeout(struct io_kiocb *req, unsigned int issue_flags)
|
||||
|
||||
if (io_is_timeout_noseq(nxt))
|
||||
continue;
|
||||
/* nxt.seq is behind @tail, otherwise would've been completed */
|
||||
if (off >= nextt->target_seq - tail)
|
||||
/* no target is behind the last flush, see io_flush_timeouts() */
|
||||
if (timeout->target_seq - base >= nextt->target_seq - base)
|
||||
break;
|
||||
}
|
||||
add:
|
||||
|
||||
Reference in new issue
Block a user