]> www.infradead.org Git - users/hch/block.git/commitdiff
io_uring: openat directly into fixed fd table
authorPavel Begunkov <asml.silence@gmail.com>
Sat, 21 Aug 2021 15:52:38 +0000 (16:52 +0100)
committerJens Axboe <axboe@kernel.dk>
Mon, 23 Aug 2021 19:41:56 +0000 (13:41 -0600)
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. 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: we can't use IOSQE_FIXED_FILE to switch between modes, because
accept takes a file, and it already uses the flag with a different
meaning.

Suggested-by: Josh Triplett <josh@joshtriplett.org>
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
Link: https://lore.kernel.org/r/fe6429fbbf964463049496a2d24a649d728561a0.1629559905.git.asml.silence@gmail.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
fs/io_uring.c
include/uapi/linux/io_uring.h

index 6859438c4e09cb16c87f1e6c765b6ec8b3276572..d6375bf598236aabac8f3b6b4b3d976222b3b32a 100644 (file)
@@ -1063,6 +1063,9 @@ static void io_req_task_queue(struct io_kiocb *req);
 static void io_submit_flush_completions(struct io_ring_ctx *ctx);
 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);
+
 static struct kmem_cache *req_cachep;
 
 static const struct file_operations io_uring_fops;
@@ -3860,11 +3863,12 @@ static int io_fallocate(struct io_kiocb *req, unsigned int issue_flags)
 static int __io_openat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 {
        const char __user *fname;
+       unsigned index;
        int ret;
 
        if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL))
                return -EINVAL;
-       if (unlikely(sqe->ioprio || sqe->buf_index || sqe->splice_fd_in))
+       if (unlikely(sqe->ioprio || sqe->buf_index))
                return -EINVAL;
        if (unlikely(req->flags & REQ_F_FIXED_FILE))
                return -EBADF;
@@ -3881,6 +3885,16 @@ static int __io_openat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe
                req->open.filename = NULL;
                return ret;
        }
+
+       index = READ_ONCE(sqe->file_index);
+       req->buf_index = index;
+       if (index) {
+               if (req->open.how.flags & O_CLOEXEC)
+                       return -EINVAL;
+               if (req->buf_index != index)
+                       return -EINVAL;
+       }
+
        req->open.nofile = rlimit(RLIMIT_NOFILE);
        req->flags |= REQ_F_NEED_CLEANUP;
        return 0;
@@ -3918,8 +3932,8 @@ static int io_openat2(struct io_kiocb *req, unsigned int issue_flags)
 {
        struct open_flags op;
        struct file *file;
-       bool nonblock_set;
-       bool resolve_nonblock;
+       bool resolve_nonblock, nonblock_set;
+       bool fixed = !!req->buf_index;
        int ret;
 
        ret = build_open_flags(&req->open.how, &op);
@@ -3938,9 +3952,11 @@ static int io_openat2(struct io_kiocb *req, unsigned int issue_flags)
                op.open_flag |= O_NONBLOCK;
        }
 
-       ret = __get_unused_fd_flags(req->open.how.flags, req->open.nofile);
-       if (ret < 0)
-               goto err;
+       if (!fixed) {
+               ret = __get_unused_fd_flags(req->open.how.flags, req->open.nofile);
+               if (ret < 0)
+                       goto err;
+       }
 
        file = do_filp_open(req->open.dfd, req->open.filename, &op);
        if (IS_ERR(file)) {
@@ -3949,7 +3965,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
                 * 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);
                /* only retry if RESOLVE_CACHED wasn't already set by application */
@@ -3962,7 +3979,11 @@ static int io_openat2(struct io_kiocb *req, unsigned int issue_flags)
        if ((issue_flags & IO_URING_F_NONBLOCK) && !nonblock_set)
                file->f_flags &= ~O_NONBLOCK;
        fsnotify_open(file);
-       fd_install(ret, file);
+
+       if (!fixed)
+               fd_install(ret, file);
+       else
+               ret = io_install_fixed_file(req, file, issue_flags);
 err:
        putname(req->open.filename);
        req->flags &= ~REQ_F_NEED_CLEANUP;
@@ -7899,6 +7920,50 @@ static int io_sqe_file_register(struct io_ring_ctx *ctx, struct file *file,
 #endif
 }
 
+static int io_install_fixed_file(struct io_kiocb *req, struct file *file,
+                                unsigned int issue_flags)
+{
+       struct io_ring_ctx *ctx = req->ctx;
+       int i = req->buf_index - 1;
+       bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK;
+       struct io_fixed_file *file_slot;
+       int ret = -EBADF;
+
+       if (WARN_ON_ONCE(req->buf_index == 0))
+               goto err;
+
+       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 (i >= ctx->nr_user_files)
+               goto err;
+
+       i = array_index_nospec(i, ctx->nr_user_files);
+       file_slot = io_fixed_file_slot(&ctx->file_table, i);
+       ret = -EBADF;
+       if (file_slot->file_ptr)
+               goto err;
+
+       *io_get_tag_slot(ctx->file_data, i) = 0;
+       io_fixed_file_set(file_slot, file);
+       ret = io_sqe_file_register(ctx, file, i);
+       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,
                                 struct io_rsrc_node *node, void *rsrc)
 {
@@ -10366,6 +10431,7 @@ static int __init io_uring_init(void)
        BUILD_BUG_SQE_ELEM(40, __u16,  buf_group);
        BUILD_BUG_SQE_ELEM(42, __u16,  personality);
        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) !=
                     sizeof(struct io_uring_rsrc_update));
index 79126d5cd289d8bf2a2b5d4e49623b554236a270..45a4f2373694a13a14641b159335e3cafaa0d5ff 100644 (file)
@@ -55,7 +55,10 @@ struct io_uring_sqe {
        } __attribute__((packed));
        /* personality to use, if used */
        __u16   personality;
-       __s32   splice_fd_in;
+       union {
+               __s32   splice_fd_in;
+               __u32   file_index;
+       };
        __u64   __pad2[2];
 };