* [PATCH 0/2] io_uring: fix short read links and align link timeout cancel
@ 2026-06-11 1:22 Yang Xiuwei
2026-06-11 1:22 ` [PATCH 1/2] io_uring/rw: fix link failure on successful pipe short reads Yang Xiuwei
2026-06-11 1:22 ` [PATCH 2/2] io_uring/timeout: cancel pending link timeouts from ltimeout_list Yang Xiuwei
0 siblings, 2 replies; 7+ messages in thread
From: Yang Xiuwei @ 2026-06-11 1:22 UTC (permalink / raw)
To: axboe; +Cc: io-uring, Yang Xiuwei
This series addresses two independent issues affecting linked requests
behind a link timeout, found while exercising read -> link timeout ->
nop chains on pipes.
1) Short non-regular file read completion (patch 1, bug fix)
__io_read() treats a short read on non-regular files as success and
returns without filling the iov. __io_complete_rw_common() still
treated ret != cqe.res as failure and set REQ_F_FAIL. The head request
could therefore post a successful CQE while internally failing the link
chain, incorrectly canceling subsequent linked requests.
2) Pending link timeout cancel (patch 2, behavior alignment)
TIMEOUT_REMOVE and IORING_OP_ASYNC_CANCEL use io_timeout_cancel(), which
only scanned timeout_list. Pending link timeouts live on ltimeout_list
instead, so cancel/remove by user_data returned -ENOENT even though the
timeout was still armed.
IORING_OP_TIMEOUT_REMOVE with IORING_LINK_TIMEOUT_UPDATE already handles
link timeouts through a separate path. Extend io_timeout_cancel() to
fall back to ltimeout_list and reuse __io_disarm_linked_timeout(), so
plain remove/cancel behaves consistently.
The two patches are independent and may be applied separately, though
both are needed for full link-timeout chain test coverage.
Patch summary
-------------
1/2 io_uring/rw: fix link failure on successful pipe short reads
2/2 io_uring/timeout: cancel pending link timeouts from ltimeout_list
Test plan
---------
Matching liburing tests are submitted separately. With both kernel
patches applied:
$ make -C test link-timeout.t
$ ./test/link-timeout.t
Expected highlights:
- test_link_timeout_natural_disarm_chain: read=1, lt=-ECANCELED, nop=0
- test_link_timeout_remove_chain: remove=0, lt=-ECANCELED, read=1,
nop=0
io_uring/rw: fix link failure on successful pipe short reads
io_uring/timeout: cancel pending link timeouts from ltimeout_list
--
2.25.1
Signed-off-by: Yang Xiuwei <yangxiuwei@kylinos.cn>
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH 1/2] io_uring/rw: fix link failure on successful pipe short reads
2026-06-11 1:22 [PATCH 0/2] io_uring: fix short read links and align link timeout cancel Yang Xiuwei
@ 2026-06-11 1:22 ` Yang Xiuwei
2026-06-11 18:36 ` Jens Axboe
2026-06-11 1:22 ` [PATCH 2/2] io_uring/timeout: cancel pending link timeouts from ltimeout_list Yang Xiuwei
1 sibling, 1 reply; 7+ messages in thread
From: Yang Xiuwei @ 2026-06-11 1:22 UTC (permalink / raw)
To: axboe; +Cc: io-uring, Yang Xiuwei
__io_read() treats a short read on pipes and sockets as success and
returns without filling the iov. However, __io_complete_rw_common()
compared the transfer length against the original iov size and set
REQ_F_FAIL when they did not match. That incorrectly failed linked
requests behind a successful head request, for example a nop after a
naturally disarmed link timeout.
Treat short reads and writes on non-regular files as success in
__io_complete_rw_common(), matching the issue path.
Signed-off-by: Yang Xiuwei <yangxiuwei@kylinos.cn>
---
io_uring/rw.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/io_uring/rw.c b/io_uring/rw.c
index 0c4834645279..dd3f24b380b1 100644
--- a/io_uring/rw.c
+++ b/io_uring/rw.c
@@ -547,10 +547,23 @@ static void io_req_io_end(struct io_kiocb *req)
}
}
+static bool need_complete_io(struct io_kiocb *req)
+{
+ return req->flags & REQ_F_ISREG ||
+ S_ISBLK(file_inode(req->file)->i_mode);
+}
+
static void __io_complete_rw_common(struct io_kiocb *req, long res)
{
if (res == req->cqe.res)
return;
+ /*
+ * For non-regular files, __io_read()/__io_write() may return a short
+ * transfer without looping to fill the iter. That is success, not a
+ * failure to be propagated to linked requests.
+ */
+ if (res > 0 && res < req->cqe.res && !need_complete_io(req))
+ return;
if ((res == -EOPNOTSUPP || res == -EAGAIN) && io_rw_should_reissue(req)) {
req->flags |= REQ_F_REISSUE | REQ_F_BL_NO_RECYCLE;
} else {
@@ -839,12 +852,6 @@ static inline int io_iter_do_read(struct io_rw *rw, struct iov_iter *iter)
return -EINVAL;
}
-static bool need_complete_io(struct io_kiocb *req)
-{
- return req->flags & REQ_F_ISREG ||
- S_ISBLK(file_inode(req->file)->i_mode);
-}
-
static int io_rw_init_file(struct io_kiocb *req, fmode_t mode, int rw_type)
{
struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw);
--
2.25.1
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH 1/2] io_uring/rw: fix link failure on successful pipe short reads
2026-06-11 1:22 ` [PATCH 1/2] io_uring/rw: fix link failure on successful pipe short reads Yang Xiuwei
@ 2026-06-11 18:36 ` Jens Axboe
2026-06-12 0:59 ` Yang Xiuwei
0 siblings, 1 reply; 7+ messages in thread
From: Jens Axboe @ 2026-06-11 18:36 UTC (permalink / raw)
To: Yang Xiuwei; +Cc: io-uring
On 6/10/26 7:22 PM, Yang Xiuwei wrote:
> __io_read() treats a short read on pipes and sockets as success and
> returns without filling the iov. However, __io_complete_rw_common()
> compared the transfer length against the original iov size and set
> REQ_F_FAIL when they did not match. That incorrectly failed linked
> requests behind a successful head request, for example a nop after a
> naturally disarmed link timeout.
>
> Treat short reads and writes on non-regular files as success in
> __io_complete_rw_common(), matching the issue path.
Not sure I follow - the "short read/write is an IOSQE_IO_LINK failure"
is widely documented. So not sure I agree with this approach.
--
Jens Axboe
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] io_uring/rw: fix link failure on successful pipe short reads
2026-06-11 18:36 ` Jens Axboe
@ 2026-06-12 0:59 ` Yang Xiuwei
2026-06-12 6:29 ` Yang Xiuwei
0 siblings, 1 reply; 7+ messages in thread
From: Yang Xiuwei @ 2026-06-12 0:59 UTC (permalink / raw)
To: axboe; +Cc: io-uring
On Thu, Jun 11, 2026 at 12:36:22PM -0600, Jens Axboe wrote:
> Not sure I follow - the "short read/write is an IOSQE_IO_LINK failure"
> is widely documented. So not sure I agree with this approach.
Thank you for the feedback.
Looking at the history, the IOSQE_IO_LINK short-read behaviour was
documented in 94163513 (Feb 2020), while 9a173346bd9e (Jan 2021) later
changed __io_read() so that non-regular files no longer retry on short reads.
That issue-path change post-dates the documentation,
and I am not sure the two were ever reconciled.
Is the documentation still the intended rule for pipes as well? If so, I
will drop this patch and treat the current completion behaviour as
correct.
Thanks,
Yang Xiuwei
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] io_uring/rw: fix link failure on successful pipe short reads
2026-06-12 0:59 ` Yang Xiuwei
@ 2026-06-12 6:29 ` Yang Xiuwei
2026-06-12 21:41 ` Jens Axboe
0 siblings, 1 reply; 7+ messages in thread
From: Yang Xiuwei @ 2026-06-12 6:29 UTC (permalink / raw)
To: axboe; +Cc: io-uring
Hi Jens,
Following up on my note below.
Patch 1/2 was motivated by __io_read() returning short reads on pipes
and sockets without retrying, while __io_complete_rw_common() still
failed the link chain. I had not fully understood IOSQE_IO_LINK at the
time. When a chain depends on reading a full buffer from a pipe or
socket, a short read means that dependency is not met and the chain
should fail. IOSQE_IO_HARDLINK is the right option when later requests
must still run despite a short read. Sorry for the confusion. I will
drop patch 1/2.
Regarding patch 2/2: the current code does not handle TIMEOUT_REMOVE
against pending link timeouts on ltimeout_list, while
IORING_LINK_TIMEOUT_UPDATE already has a separate path for them. Was
leaving ltimeout_list out of the remove/cancel path intentional, or
simply an oversight? If the current behaviour is intended, I will drop
patch 2/2 as well.
Thanks,
Yang Xiuwei
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] io_uring/rw: fix link failure on successful pipe short reads
2026-06-12 6:29 ` Yang Xiuwei
@ 2026-06-12 21:41 ` Jens Axboe
0 siblings, 0 replies; 7+ messages in thread
From: Jens Axboe @ 2026-06-12 21:41 UTC (permalink / raw)
To: Yang Xiuwei; +Cc: io-uring
On 6/12/26 12:29 AM, Yang Xiuwei wrote:
> Hi Jens,
>
> Following up on my note below.
>
> Patch 1/2 was motivated by __io_read() returning short reads on pipes
> and sockets without retrying, while __io_complete_rw_common() still
> failed the link chain. I had not fully understood IOSQE_IO_LINK at the
> time. When a chain depends on reading a full buffer from a pipe or
> socket, a short read means that dependency is not met and the chain
> should fail. IOSQE_IO_HARDLINK is the right option when later requests
> must still run despite a short read. Sorry for the confusion. I will
> drop patch 1/2.
All good.
> Regarding patch 2/2: the current code does not handle TIMEOUT_REMOVE
> against pending link timeouts on ltimeout_list, while
> IORING_LINK_TIMEOUT_UPDATE already has a separate path for them. Was
> leaving ltimeout_list out of the remove/cancel path intentional, or
> simply an oversight? If the current behaviour is intended, I will drop
> patch 2/2 as well.
I think that one could get done, even if it is a special kind of
timeout. But the devil is in the details, easy to get that wrong.
I'll take a look at this, but it'll be post the 7.2 slated changes
as the merge window is just about to open.
--
Jens Axboe
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 2/2] io_uring/timeout: cancel pending link timeouts from ltimeout_list
2026-06-11 1:22 [PATCH 0/2] io_uring: fix short read links and align link timeout cancel Yang Xiuwei
2026-06-11 1:22 ` [PATCH 1/2] io_uring/rw: fix link failure on successful pipe short reads Yang Xiuwei
@ 2026-06-11 1:22 ` Yang Xiuwei
1 sibling, 0 replies; 7+ messages in thread
From: Yang Xiuwei @ 2026-06-11 1:22 UTC (permalink / raw)
To: axboe; +Cc: io-uring, Yang Xiuwei
TIMEOUT_REMOVE and IORING_OP_ASYNC_CANCEL look up pending timeouts via
io_timeout_cancel(), but that path only scans timeout_list. Pending
link timeouts live on ltimeout_list instead, so cancel/remove by
user_data returns -ENOENT.
Fall back to ltimeout_list when the initial lookup fails, reusing
__io_disarm_linked_timeout(). Complete the disarmed link timeout via
io_req_queue_tw_complete() rather than io_req_task_queue_fail(), and
clean up the link timeout state on the head request.
Signed-off-by: Yang Xiuwei <yangxiuwei@kylinos.cn>
---
io_uring/timeout.c | 36 +++++++++++++++++++++++++++++++++++-
1 file changed, 35 insertions(+), 1 deletion(-)
diff --git a/io_uring/timeout.c b/io_uring/timeout.c
index c4dd26cf342d..8ecc9a7f1597 100644
--- a/io_uring/timeout.c
+++ b/io_uring/timeout.c
@@ -348,18 +348,52 @@ static struct io_kiocb *io_timeout_extract(struct io_ring_ctx *ctx,
return req;
}
+static struct io_kiocb *io_linked_timeout_cancel(struct io_ring_ctx *ctx,
+ struct io_cancel_data *cd)
+ __must_hold(&ctx->completion_lock)
+ __must_hold(&ctx->timeout_lock)
+{
+ struct io_timeout *timeout;
+
+ list_for_each_entry(timeout, &ctx->ltimeout_list, list) {
+ struct io_kiocb *link = cmd_to_io_kiocb(timeout);
+ struct io_kiocb *head;
+
+ if (!io_cancel_req_match(link, cd))
+ continue;
+ head = timeout->head;
+ if (!head)
+ return ERR_PTR(-EALREADY);
+ link = __io_disarm_linked_timeout(head, link);
+ if (!link)
+ return ERR_PTR(-EALREADY);
+ head->flags &= ~REQ_F_LINK_TIMEOUT;
+ return link;
+ }
+ return ERR_PTR(-ENOENT);
+}
+
int io_timeout_cancel(struct io_ring_ctx *ctx, struct io_cancel_data *cd)
__must_hold(&ctx->completion_lock)
{
struct io_kiocb *req;
+ bool linked = false;
raw_spin_lock_irq(&ctx->timeout_lock);
req = io_timeout_extract(ctx, cd);
+ if (req == ERR_PTR(-ENOENT)) {
+ req = io_linked_timeout_cancel(ctx, cd);
+ if (!IS_ERR(req))
+ linked = true;
+ }
raw_spin_unlock_irq(&ctx->timeout_lock);
if (IS_ERR(req))
return PTR_ERR(req);
- io_req_task_queue_fail(req, -ECANCELED);
+ if (linked)
+ io_req_queue_tw_complete(req, -ECANCELED);
+ else
+ io_req_task_queue_fail(req, -ECANCELED);
return 0;
}
--
2.25.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-06-12 21:41 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-11 1:22 [PATCH 0/2] io_uring: fix short read links and align link timeout cancel Yang Xiuwei
2026-06-11 1:22 ` [PATCH 1/2] io_uring/rw: fix link failure on successful pipe short reads Yang Xiuwei
2026-06-11 18:36 ` Jens Axboe
2026-06-12 0:59 ` Yang Xiuwei
2026-06-12 6:29 ` Yang Xiuwei
2026-06-12 21:41 ` Jens Axboe
2026-06-11 1:22 ` [PATCH 2/2] io_uring/timeout: cancel pending link timeouts from ltimeout_list Yang Xiuwei
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox