Skip to content

Commit 6bf9c47

Browse files
committed
io_uring: defer file assignment
If an application uses direct open or accept, it knows in advance what direct descriptor value it will get as it picks it itself. This allows combined requests such as: sqe = io_uring_get_sqe(ring); io_uring_prep_openat_direct(sqe, ..., file_slot); sqe->flags |= IOSQE_IO_LINK | IOSQE_CQE_SKIP_SUCCESS; sqe = io_uring_get_sqe(ring); io_uring_prep_read(sqe,file_slot, buf, buf_size, 0); sqe->flags |= IOSQE_FIXED_FILE; io_uring_submit(ring); where we prepare both a file open and read, and only get a completion event for the read when both have completed successfully. Currently links are fully prepared before the head is issued, but that fails if the dependent link needs a file assigned that isn't valid until the head has completed. Conversely, if the same chain is performed but the fixed file slot is already valid, then we would be unexpectedly returning data from the old file slot rather than the newly opened one. Make sure we're consistent here. Allow deferral of file setup, which makes this documented case work. Cc: [email protected] # v5.15+ Signed-off-by: Jens Axboe <[email protected]>
1 parent 5106dd6 commit 6bf9c47

File tree

2 files changed

+30
-10
lines changed

2 files changed

+30
-10
lines changed

fs/io-wq.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ struct io_wq_work_node *wq_stack_extract(struct io_wq_work_node *stack)
155155
struct io_wq_work {
156156
struct io_wq_work_node list;
157157
unsigned flags;
158+
int fd;
158159
};
159160

160161
static inline struct io_wq_work *wq_next_work(struct io_wq_work *work)

fs/io_uring.c

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7240,6 +7240,23 @@ static void io_clean_op(struct io_kiocb *req)
72407240
req->flags &= ~IO_REQ_CLEAN_FLAGS;
72417241
}
72427242

7243+
static bool io_assign_file(struct io_kiocb *req, unsigned int issue_flags)
7244+
{
7245+
if (req->file || !io_op_defs[req->opcode].needs_file)
7246+
return true;
7247+
7248+
if (req->flags & REQ_F_FIXED_FILE)
7249+
req->file = io_file_get_fixed(req, req->work.fd, issue_flags);
7250+
else
7251+
req->file = io_file_get_normal(req, req->work.fd);
7252+
if (req->file)
7253+
return true;
7254+
7255+
req_set_fail(req);
7256+
req->result = -EBADF;
7257+
return false;
7258+
}
7259+
72437260
static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags)
72447261
{
72457262
const struct cred *creds = NULL;
@@ -7250,6 +7267,8 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags)
72507267

72517268
if (!io_op_defs[req->opcode].audit_skip)
72527269
audit_uring_entry(req->opcode);
7270+
if (unlikely(!io_assign_file(req, issue_flags)))
7271+
return -EBADF;
72537272

72547273
switch (req->opcode) {
72557274
case IORING_OP_NOP:
@@ -7394,10 +7413,11 @@ static struct io_wq_work *io_wq_free_work(struct io_wq_work *work)
73947413
static void io_wq_submit_work(struct io_wq_work *work)
73957414
{
73967415
struct io_kiocb *req = container_of(work, struct io_kiocb, work);
7416+
const struct io_op_def *def = &io_op_defs[req->opcode];
73977417
unsigned int issue_flags = IO_URING_F_UNLOCKED;
73987418
bool needs_poll = false;
73997419
struct io_kiocb *timeout;
7400-
int ret = 0;
7420+
int ret = 0, err = -ECANCELED;
74017421

74027422
/* one will be dropped by ->io_free_work() after returning to io-wq */
74037423
if (!(req->flags & REQ_F_REFCOUNT))
@@ -7409,14 +7429,18 @@ static void io_wq_submit_work(struct io_wq_work *work)
74097429
if (timeout)
74107430
io_queue_linked_timeout(timeout);
74117431

7432+
if (!io_assign_file(req, issue_flags)) {
7433+
err = -EBADF;
7434+
work->flags |= IO_WQ_WORK_CANCEL;
7435+
}
7436+
74127437
/* either cancelled or io-wq is dying, so don't touch tctx->iowq */
74137438
if (work->flags & IO_WQ_WORK_CANCEL) {
7414-
io_req_task_queue_fail(req, -ECANCELED);
7439+
io_req_task_queue_fail(req, err);
74157440
return;
74167441
}
74177442

74187443
if (req->flags & REQ_F_FORCE_ASYNC) {
7419-
const struct io_op_def *def = &io_op_defs[req->opcode];
74207444
bool opcode_poll = def->pollin || def->pollout;
74217445

74227446
if (opcode_poll && file_can_poll(req->file)) {
@@ -7749,6 +7773,8 @@ static int io_init_req(struct io_ring_ctx *ctx, struct io_kiocb *req,
77497773
if (io_op_defs[opcode].needs_file) {
77507774
struct io_submit_state *state = &ctx->submit_state;
77517775

7776+
req->work.fd = READ_ONCE(sqe->fd);
7777+
77527778
/*
77537779
* Plug now if we have more than 2 IO left after this, and the
77547780
* target is potentially a read/write to block based storage.
@@ -7758,13 +7784,6 @@ static int io_init_req(struct io_ring_ctx *ctx, struct io_kiocb *req,
77587784
state->need_plug = false;
77597785
blk_start_plug_nr_ios(&state->plug, state->submit_nr);
77607786
}
7761-
7762-
if (req->flags & REQ_F_FIXED_FILE)
7763-
req->file = io_file_get_fixed(req, READ_ONCE(sqe->fd), 0);
7764-
else
7765-
req->file = io_file_get_normal(req, READ_ONCE(sqe->fd));
7766-
if (unlikely(!req->file))
7767-
return -EBADF;
77687787
}
77697788

77707789
personality = READ_ONCE(sqe->personality);

0 commit comments

Comments
 (0)