Commit b9445598 authored by Pavel Begunkov's avatar Pavel Begunkov Committed by Jens Axboe

io_uring: openat directly into fixed fd table

Instead of opening a file into a process's file table as usual and then
registering the fd within io_uring, some users may want to skip the
first step and place it directly into io_uring's fixed file table.
This patch adds such a capability for IORING_OP_OPENAT and
IORING_OP_OPENAT2.

The behaviour is controlled by setting sqe->file_index, where 0 implies
the old behaviour using normal file tables. If non-zero value is
specified, then it will behave as described and place the file into a
fixed file slot sqe->file_index - 1. A file table should be already
created, the slot should be valid and empty, otherwise the operation
will fail.

Keep the error codes consistent with IORING_OP_FILES_UPDATE, ENXIO and
EINVAL on inappropriate fixed tables, and return EBADF on collision with
already registered file.

Note: IOSQE_FIXED_FILE can't be used to switch between modes, because
accept takes a file, and it already uses the flag with a different
meaning.
Suggested-by: default avatarJosh Triplett <josh@joshtriplett.org>
Signed-off-by: default avatarPavel Begunkov <asml.silence@gmail.com>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
Link: https://lore.kernel.org/r/e9b33d1163286f51ea707f87d95bd596dada1e65.1629888991.git.asml.silence@gmail.comSigned-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent d32f89da
...@@ -580,6 +580,7 @@ struct io_sr_msg { ...@@ -580,6 +580,7 @@ struct io_sr_msg {
struct io_open { struct io_open {
struct file *file; struct file *file;
int dfd; int dfd;
u32 file_slot;
struct filename *filename; struct filename *filename;
struct open_how how; struct open_how how;
unsigned long nofile; unsigned long nofile;
...@@ -1063,6 +1064,9 @@ static void io_req_task_queue(struct io_kiocb *req); ...@@ -1063,6 +1064,9 @@ static void io_req_task_queue(struct io_kiocb *req);
static void io_submit_flush_completions(struct io_ring_ctx *ctx); static void io_submit_flush_completions(struct io_ring_ctx *ctx);
static int io_req_prep_async(struct io_kiocb *req); static int io_req_prep_async(struct io_kiocb *req);
static int io_install_fixed_file(struct io_kiocb *req, struct file *file,
unsigned int issue_flags, u32 slot_index);
static struct kmem_cache *req_cachep; static struct kmem_cache *req_cachep;
static const struct file_operations io_uring_fops; static const struct file_operations io_uring_fops;
...@@ -3864,7 +3868,7 @@ static int __io_openat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe ...@@ -3864,7 +3868,7 @@ static int __io_openat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe
if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL))
return -EINVAL; return -EINVAL;
if (unlikely(sqe->ioprio || sqe->buf_index || sqe->splice_fd_in)) if (unlikely(sqe->ioprio || sqe->buf_index))
return -EINVAL; return -EINVAL;
if (unlikely(req->flags & REQ_F_FIXED_FILE)) if (unlikely(req->flags & REQ_F_FIXED_FILE))
return -EBADF; return -EBADF;
...@@ -3881,6 +3885,11 @@ static int __io_openat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe ...@@ -3881,6 +3885,11 @@ static int __io_openat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe
req->open.filename = NULL; req->open.filename = NULL;
return ret; return ret;
} }
req->open.file_slot = READ_ONCE(sqe->file_index);
if (req->open.file_slot && (req->open.how.flags & O_CLOEXEC))
return -EINVAL;
req->open.nofile = rlimit(RLIMIT_NOFILE); req->open.nofile = rlimit(RLIMIT_NOFILE);
req->flags |= REQ_F_NEED_CLEANUP; req->flags |= REQ_F_NEED_CLEANUP;
return 0; return 0;
...@@ -3918,8 +3927,8 @@ static int io_openat2(struct io_kiocb *req, unsigned int issue_flags) ...@@ -3918,8 +3927,8 @@ static int io_openat2(struct io_kiocb *req, unsigned int issue_flags)
{ {
struct open_flags op; struct open_flags op;
struct file *file; struct file *file;
bool nonblock_set; bool resolve_nonblock, nonblock_set;
bool resolve_nonblock; bool fixed = !!req->open.file_slot;
int ret; int ret;
ret = build_open_flags(&req->open.how, &op); ret = build_open_flags(&req->open.how, &op);
...@@ -3938,9 +3947,11 @@ static int io_openat2(struct io_kiocb *req, unsigned int issue_flags) ...@@ -3938,9 +3947,11 @@ static int io_openat2(struct io_kiocb *req, unsigned int issue_flags)
op.open_flag |= O_NONBLOCK; op.open_flag |= O_NONBLOCK;
} }
ret = __get_unused_fd_flags(req->open.how.flags, req->open.nofile); if (!fixed) {
if (ret < 0) ret = __get_unused_fd_flags(req->open.how.flags, req->open.nofile);
goto err; if (ret < 0)
goto err;
}
file = do_filp_open(req->open.dfd, req->open.filename, &op); file = do_filp_open(req->open.dfd, req->open.filename, &op);
if (IS_ERR(file)) { if (IS_ERR(file)) {
...@@ -3949,7 +3960,8 @@ static int io_openat2(struct io_kiocb *req, unsigned int issue_flags) ...@@ -3949,7 +3960,8 @@ static int io_openat2(struct io_kiocb *req, unsigned int issue_flags)
* marginal gain for something that is now known to be a slower * marginal gain for something that is now known to be a slower
* path. So just put it, and we'll get a new one when we retry. * path. So just put it, and we'll get a new one when we retry.
*/ */
put_unused_fd(ret); if (!fixed)
put_unused_fd(ret);
ret = PTR_ERR(file); ret = PTR_ERR(file);
/* only retry if RESOLVE_CACHED wasn't already set by application */ /* only retry if RESOLVE_CACHED wasn't already set by application */
...@@ -3962,7 +3974,12 @@ static int io_openat2(struct io_kiocb *req, unsigned int issue_flags) ...@@ -3962,7 +3974,12 @@ static int io_openat2(struct io_kiocb *req, unsigned int issue_flags)
if ((issue_flags & IO_URING_F_NONBLOCK) && !nonblock_set) if ((issue_flags & IO_URING_F_NONBLOCK) && !nonblock_set)
file->f_flags &= ~O_NONBLOCK; file->f_flags &= ~O_NONBLOCK;
fsnotify_open(file); fsnotify_open(file);
fd_install(ret, file);
if (!fixed)
fd_install(ret, file);
else
ret = io_install_fixed_file(req, file, issue_flags,
req->open.file_slot - 1);
err: err:
putname(req->open.filename); putname(req->open.filename);
req->flags &= ~REQ_F_NEED_CLEANUP; req->flags &= ~REQ_F_NEED_CLEANUP;
...@@ -7899,6 +7916,46 @@ static int io_sqe_file_register(struct io_ring_ctx *ctx, struct file *file, ...@@ -7899,6 +7916,46 @@ static int io_sqe_file_register(struct io_ring_ctx *ctx, struct file *file,
#endif #endif
} }
static int io_install_fixed_file(struct io_kiocb *req, struct file *file,
unsigned int issue_flags, u32 slot_index)
{
struct io_ring_ctx *ctx = req->ctx;
bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK;
struct io_fixed_file *file_slot;
int ret = -EBADF;
io_ring_submit_lock(ctx, !force_nonblock);
if (file->f_op == &io_uring_fops)
goto err;
ret = -ENXIO;
if (!ctx->file_data)
goto err;
ret = -EINVAL;
if (slot_index >= ctx->nr_user_files)
goto err;
slot_index = array_index_nospec(slot_index, ctx->nr_user_files);
file_slot = io_fixed_file_slot(&ctx->file_table, slot_index);
ret = -EBADF;
if (file_slot->file_ptr)
goto err;
*io_get_tag_slot(ctx->file_data, slot_index) = 0;
io_fixed_file_set(file_slot, file);
ret = io_sqe_file_register(ctx, file, slot_index);
if (ret) {
file_slot->file_ptr = 0;
goto err;
}
ret = 0;
err:
io_ring_submit_unlock(ctx, !force_nonblock);
if (ret)
fput(file);
return ret;
}
static int io_queue_rsrc_removal(struct io_rsrc_data *data, unsigned idx, static int io_queue_rsrc_removal(struct io_rsrc_data *data, unsigned idx,
struct io_rsrc_node *node, void *rsrc) struct io_rsrc_node *node, void *rsrc)
{ {
...@@ -10366,6 +10423,7 @@ static int __init io_uring_init(void) ...@@ -10366,6 +10423,7 @@ static int __init io_uring_init(void)
BUILD_BUG_SQE_ELEM(40, __u16, buf_group); BUILD_BUG_SQE_ELEM(40, __u16, buf_group);
BUILD_BUG_SQE_ELEM(42, __u16, personality); BUILD_BUG_SQE_ELEM(42, __u16, personality);
BUILD_BUG_SQE_ELEM(44, __s32, splice_fd_in); BUILD_BUG_SQE_ELEM(44, __s32, splice_fd_in);
BUILD_BUG_SQE_ELEM(44, __u32, file_index);
BUILD_BUG_ON(sizeof(struct io_uring_files_update) != BUILD_BUG_ON(sizeof(struct io_uring_files_update) !=
sizeof(struct io_uring_rsrc_update)); sizeof(struct io_uring_rsrc_update));
......
...@@ -55,7 +55,10 @@ struct io_uring_sqe { ...@@ -55,7 +55,10 @@ struct io_uring_sqe {
} __attribute__((packed)); } __attribute__((packed));
/* personality to use, if used */ /* personality to use, if used */
__u16 personality; __u16 personality;
__s32 splice_fd_in; union {
__s32 splice_fd_in;
__u32 file_index;
};
__u64 __pad2[2]; __u64 __pad2[2];
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment