open/accept directly into io_uring fixed file table
Subsystems (5)
v4 cover open/accept directly into io_uring fixed file table
Add an optional feature to open/accept directly into io_uring's fixed file table bypassing the normal file table. Same behaviour if as the snippet below, but in one operation:
sqe = io_uring_[open|accept]_prep(); io_uring_submit(sqe);
// ... once we get a CQE back io_uring_register_files_update(uring_idx, (fd = cqe->res)); close((fd = cqe->res));
The idea is old, and was brough up and implemented a year ago by Josh Triplett, though haven't sought the light.
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.
note: IOSQE_FIXED_FILE can't be used as a mode switch, because accept takes a file, and it already uses the flag with a different meaning.
v2, since RFC:
- added attribution
- updated descriptions
- rebased
v3:
- EBADF if slot is already used (Josh Triplett)
- alias index with splice_fd_in (Josh Triplett)
- fix a bound check bug
v4:
- separate u32 fields to internally store indexes (Jens, Josh)
Pavel Begunkov (4):
net: add accept helper not installing fd
io_uring: openat directly into fixed fd table
io_uring: hand code io_accept() fd installing
io_uring: accept directly into fixed file table
fs/io_uring.c | 115 +++++++++++++++++++++++++++++----- include/linux/socket.h | 3 + include/uapi/linux/io_uring.h | 5 +- net/socket.c | 71 +++++++++++---------- 4 files changed, 143 insertions(+), 51 deletions(-)
Signature · 1 line
2.32.0
PATCH v4 1/4 net: add accept helper not installing fd
Introduce and reuse a helper that acts similarly to __sys_accept4_file() but returns struct file instead of installing file descriptor. Will be used by io_uring.
--- include/linux/socket.h | 3 ++ net/socket.c | 71 ++++++++++++++++++++++-------------------- 2 files changed, 40 insertions(+), 34 deletions(-)
include/linux/socket.h +3 -0
@@ -421,6 +421,9 @@ extern int __sys_accept4_file(struct file *file, unsigned file_flags,struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen, int flags, unsigned long nofile); +extern struct file *do_accept (struct file *file,unsigned file_flags, +struct sockaddr __user *upeer_sockaddr, +int __user *upeer_addrlen,int flags);extern int __sys_accept4 (int fd,struct sockaddr __user *upeer_sockaddr,int __user *upeer_addrlen,int flags);extern int __sys_socket (int family,int type,int protocol);
net/socket.c +37 -34
103 lines not on this page · raw message
Signature · 1 line
2.32.0
Re: [PATCH v4 1/4] net: add accept helper not installing fd
From: Pavel Begunkov <[email protected]>
Date: Wed, 25 Aug 2021 12:25:44 +0100
Quoted 7 lines
Introduce and reuse a helper that acts similarly to
__sys_accept4_file()but returns struct file instead of installing file descriptor. Will be used byio_uring.Signed-off-by Pavel Begunkov <[email protected]>Acked-by Jakub Kicinski <[email protected]>Signed-off-by Jens Axboe <[email protected]>
PATCH v4 2/4 io_uring: openat directly into fixed fd table
In reply to Pavel BegunkovInstead 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.
--- fs/io_uring.c | 74 +++++++++++++++++++++++++++++++---- include/uapi/linux/io_uring.h | 5 ++- 2 files changed, 70 insertions(+), 9 deletions(-)
fs/io_uring.c +66 -8
@@ -579,6 +579,7 @@ struct io_sr_msg {struct io_open {struct file *file;int dfd; +u32 file_slot;struct filename *filename;struct open_how how;unsigned long nofile;
@@ -1062,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,u32 slot_index); +static struct kmem_cache *req_cachep;static const struct file_operations io_uring_fops;
@@ -3863,7 +3867,7 @@ static int __io_openat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqeif (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 ;
@@ -3880,6 +3884,11 @@ static int __io_openat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe req->open.filename =NULL ;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->flags |=REQ_F_NEED_CLEANUP ;return 0 ;
@@ -3917,8 +3926,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->open.file_slot;int ret; ret =build_open_flags (&req->open.how, &op);
@@ -3937,9 +3946,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)) {
@@ -3948,7 +3959,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 */
@@ -3961,7 +3973,12 @@ 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, + req->open.file_slot -1 ); err:putname (req->open.filename); req->flags &= ~REQ_F_NEED_CLEANUP ;
@@ -7896,6 +7913,46 @@ 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,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,struct io_rsrc_node *node,void *rsrc) {
@@ -10363,6 +10420,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 ));
include/uapi/linux/io_uring.h +4 -1
@@ -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 ]; };
Signature · 1 line
2.32.0
PATCH v4 3/4 io_uring: hand code io_accept() fd installing
In reply to Pavel BegunkovMake io_accept() to handle file descriptor allocations and installation. A preparation patch for bypassing file tables.
--- fs/io_uring.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-)
fs/io_uring.c +20 -7
@@ -4809,6 +4809,11 @@ static int io_accept_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) accept->addr_len =u64_to_user_ptr (READ_ONCE (sqe->addr2)); accept->flags =READ_ONCE (sqe->accept_flags); accept->nofile =rlimit (RLIMIT_NOFILE ); + +if (accept->flags & ~(SOCK_CLOEXEC |SOCK_NONBLOCK )) +return -EINVAL ; +if (SOCK_NONBLOCK !=O_NONBLOCK && (accept->flags &SOCK_NONBLOCK )) + accept->flags = (accept->flags & ~SOCK_NONBLOCK ) |O_NONBLOCK ;return 0 ; }
@@ -4817,20 +4822,28 @@ static int io_accept(struct io_kiocb *req, unsigned int issue_flags)struct io_accept *accept = &req->accept;bool force_nonblock = issue_flags &IO_URING_F_NONBLOCK ;unsigned int file_flags = force_nonblock ?O_NONBLOCK :0 ; -int ret; +struct file *file; +int ret, fd;if (req->file->f_flags &O_NONBLOCK ) req->flags |=REQ_F_NOWAIT ; - ret =__sys_accept4_file (req->file, file_flags, accept->addr, - accept->addr_len, accept->flags, - accept->nofile); -if (ret == -EAGAIN && force_nonblock) -return -EAGAIN ; -if (ret <0 ) { + fd =__get_unused_fd_flags (accept->flags, accept->nofile); +if (unlikely (fd <0 )) +return fd; + + file =do_accept (req->file, file_flags, accept->addr, accept->addr_len, + accept->flags); +if (IS_ERR (file)) { + ret =PTR_ERR (file); +if (ret == -EAGAIN && force_nonblock) +return -EAGAIN ;if (ret == -ERESTARTSYS ) ret = -EINTR ;req_set_fail (req); + }else { +fd_install (fd, file); + ret = fd; }__io_req_complete (req, issue_flags, ret,0 );return 0 ;
Signature · 1 line
2.32.0
PATCH v4 4/4 io_uring: accept directly into fixed file table
In reply to Pavel BegunkovAs done with open opcodes, allow accept to skip installing fd into processes' file tables and put it directly into io_uring's fixed file table. Same restrictions and design as for open.
--- fs/io_uring.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-)
fs/io_uring.c +18 -6
@@ -514,6 +514,7 @@ struct io_accept {struct sockaddr __user *addr;int __user *addr_len;int flags; +u32 file_slot;unsigned long nofile; };
@@ -4802,7 +4803,7 @@ static int io_accept_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)if (unlikely (req->ctx->flags &IORING_SETUP_IOPOLL ))return -EINVAL ; -if (sqe->ioprio || sqe->len || sqe->buf_index || sqe->splice_fd_in) +if (sqe->ioprio || sqe->len || sqe->buf_index)return -EINVAL ; accept->addr =u64_to_user_ptr (READ_ONCE (sqe->addr));
@@ -4810,6 +4811,10 @@ static int io_accept_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) accept->flags =READ_ONCE (sqe->accept_flags); accept->nofile =rlimit (RLIMIT_NOFILE ); + accept->file_slot =READ_ONCE (sqe->file_index); +if (accept->file_slot && ((req->open.how.flags &O_CLOEXEC ) || + (accept->flags &SOCK_CLOEXEC ))) +return -EINVAL ;if (accept->flags & ~(SOCK_CLOEXEC |SOCK_NONBLOCK ))return -EINVAL ;if (SOCK_NONBLOCK !=O_NONBLOCK && (accept->flags &SOCK_NONBLOCK ))
@@ -4822,28 +4827,35 @@ static int io_accept(struct io_kiocb *req, unsigned int issue_flags)struct io_accept *accept = &req->accept;bool force_nonblock = issue_flags &IO_URING_F_NONBLOCK ;unsigned int file_flags = force_nonblock ?O_NONBLOCK :0 ; +bool fixed = !!accept->file_slot;struct file *file;int ret, fd;if (req->file->f_flags &O_NONBLOCK ) req->flags |=REQ_F_NOWAIT ; - fd =__get_unused_fd_flags (accept->flags, accept->nofile); -if (unlikely (fd <0 )) -return fd; - +if (!fixed) { + fd =__get_unused_fd_flags (accept->flags, accept->nofile); +if (unlikely (fd <0 )) +return fd; + } file =do_accept (req->file, file_flags, accept->addr, accept->addr_len, accept->flags);if (IS_ERR (file)) { +if (!fixed) +put_unused_fd (fd); ret =PTR_ERR (file);if (ret == -EAGAIN && force_nonblock)return -EAGAIN ;if (ret == -ERESTARTSYS ) ret = -EINTR ;req_set_fail (req); - }else { + }else if (!fixed) {fd_install (fd, file); ret = fd; + }else { + ret =io_install_fixed_file (req, file, issue_flags, + accept->file_slot -1 ); }__io_req_complete (req, issue_flags, ret,0 );return 0 ;
Signature · 1 line
2.32.0
Jens Axboe
maintainerQuoted 20 lines · Pavel Begunkov
Add an optional feature to open/accept directly into io_uring's fixed file table bypassing the normal file table. Same behaviour if as the snippet below, but in one operation:
sqe = io_uring_[open|accept]_prep();
io_uring_submit(sqe);
// ... once we get a CQE backio_uring_register_files_update(uring_idx, (fd = cqe->res));close((fd = cqe->res));The idea is old, and was brough up and implemented a year ago by Josh Triplett, though haven't sought the light.
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 slotsqe->file_index- 1. A file table should be already created, the slot should be valid and empty, otherwise the operation will fail.
Quoted 2 lines
note:
IOSQE_FIXED_FILEcan't be used as a mode switch, because accept takes a file, and it already uses the flag with a different meaning.
Updated the tree and picked you davem's ack as well.
Signature · 1 line
Jens Axboe