If _io_uring_get_cqe() submits SQEs and then times out waiting for completions, it returns the submit count instead of -ETIME: 1. The first enter submits the SQEs; because submit > 0 the kernel returns the submit count, not -ETIME, and it is stored in err. 2. On the next iteration the has_ts shortcut wants to report -ETIME, but the 'if (!err)' guard sees the non-zero submit count and keeps it, so -ETIME is dropped. That contradicts io_uring_submit_and_wait_timeout(3) and io_uring_wait_cqes(3), which document -ETIME on timeout. At these two sites (lines 113 and 118) err is only ever 0 or a positive submit count. A negative error from __io_uring_peek_cqe() or a prior enter breaks out of the loop before reaching here. So the change is functionally equivalent to dropping the err condition entirely; we change '!err' to 'err >= 0' so -ETIME is successfully synthesized whenever no CQE was seen. The guards were added in 2f61e849 ("src/queue: don't wait twice if looping in _io_uring_get_cqe()") to carry the submit count across iterations for the partial-completion case (got some CQEs, no error); that case still returns the count because both sites remain guarded by !cqe. Signed-off-by: Prateek Reviewed-by: Gabriel Krisman Bertazi --- src/queue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/queue.c b/src/queue.c index fcd3c702..e2e5a061 100644 --- a/src/queue.c +++ b/src/queue.c @@ -110,12 +110,12 @@ static int _io_uring_get_cqe(struct io_uring *ring, * timeout, so treat any timeout the same as -ETIME here. */ if (data->get_flags & IORING_ENTER_EXT_ARG_REG) { - if (!cqe && !err) + if (!cqe && err >= 0) err = -ETIME; } else { struct io_uring_getevents_arg *arg = data->arg; - if (!cqe && arg->ts && !err) + if (!cqe && arg->ts && err >= 0) err = -ETIME; } break; -- 2.43.0