mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
io_uring: don't pass restart error codes to userspace
-ERESTARTSYS and friends are instructions for the system call exit code,
which either restarts the call or turns them into -EINTR. They are not
supposed to be seen by applications. A request isn't a system call,
nobody looks at what it returns before that is put into a CQE.
The read/write and the networking opcodes convert these codes to -EINTR
by hand. The others don't. An io-wq worker that is blocked in the open of
a FIFO, a tty or some other device, or in a splice from an empty pipe,
and that gets interrupted because the request is cancelled, completes it
with -512:
IORING_OP_OPENAT, IOSQE_ASYNC // FIFO without a writer
IORING_OP_ASYNC_CANCEL // -EALREADY, worker is signalled
CQE of the open: res == -512
There are more candidates, like whatever a file system or driver returns
from ->flush(), ->fsync(), ->fallocate() or ->uring_cmd(), or breaking a
lease or delegation.
Take care of it in io_issue_sqe(), which all of them go through. The
helper is moved from rw.c as is. The cost is a compare and a branch that
is not taken per request.
Fixes: 15b71abe7b ("io_uring: add support for IORING_OP_OPENAT")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Link: https://patch.msgid.link/20261015-b4-io-uring-fixes-v1-39-c1556f5d7bf5@toxicpanda.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
a3d7ee1128
commit
e1a64bc648
|
|
@ -1457,6 +1457,9 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags)
|
|||
ret = __io_issue_sqe(req, issue_flags, def);
|
||||
|
||||
if (ret == IOU_COMPLETE) {
|
||||
/* not all handlers take care of the internal restart codes */
|
||||
if (unlikely(req->cqe.res <= -ERESTARTSYS))
|
||||
req->cqe.res = io_fixup_restart_res(req->cqe.res);
|
||||
if (issue_flags & IO_URING_F_COMPLETE_DEFER)
|
||||
io_req_complete_defer(req);
|
||||
else
|
||||
|
|
@ -1471,6 +1474,8 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags)
|
|||
if (req->flags & REQ_F_IOPOLL)
|
||||
io_iopoll_req_issued(req, issue_flags);
|
||||
}
|
||||
if (unlikely(ret <= -ERESTARTSYS))
|
||||
ret = io_fixup_restart_res(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -319,6 +319,24 @@ static __always_inline bool io_fill_cqe_req(struct io_ring_ctx *ctx,
|
|||
return true;
|
||||
}
|
||||
|
||||
static inline ssize_t io_fixup_restart_res(ssize_t ret)
|
||||
{
|
||||
switch (ret) {
|
||||
case -ERESTARTSYS:
|
||||
case -ERESTARTNOINTR:
|
||||
case -ERESTARTNOHAND:
|
||||
case -ERESTART_RESTARTBLOCK:
|
||||
/*
|
||||
* We can't just restart the syscall, since previously
|
||||
* submitted sqes may already be in progress. Just fail
|
||||
* this IO with EINTR.
|
||||
*/
|
||||
return -EINTR;
|
||||
default:
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void *io_uring_alloc_async_data(struct io_alloc_cache *cache,
|
||||
struct io_kiocb *req)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -605,24 +605,6 @@ static void io_complete_rw_iopoll(struct kiocb *kiocb, long res)
|
|||
smp_store_release(&req->iopoll_completed, 1);
|
||||
}
|
||||
|
||||
static inline ssize_t io_fixup_restart_res(ssize_t ret)
|
||||
{
|
||||
switch (ret) {
|
||||
case -ERESTARTSYS:
|
||||
case -ERESTARTNOINTR:
|
||||
case -ERESTARTNOHAND:
|
||||
case -ERESTART_RESTARTBLOCK:
|
||||
/*
|
||||
* We can't just restart the syscall, since previously
|
||||
* submitted sqes may already be in progress. Just fail
|
||||
* this IO with EINTR.
|
||||
*/
|
||||
return -EINTR;
|
||||
default:
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void io_rw_done(struct io_kiocb *req, ssize_t ret)
|
||||
{
|
||||
struct io_rw *rw = io_kiocb_to_cmd(req, struct io_rw);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user