On 02/11/2020 07:44, Hao Xu wrote: > Async ioctl is necessary for some scenarios like nonblocking > single-threaded model Once I fell for it myself, see Jann explained why that's a bad idea. https://lore.kernel.org/io-uring/CAG48ez0N_b+kjbddhHe+BUvSnOSvpm1vdfQ9cv+cgTLuCMXqug@xxxxxxxxxxxxxx/ > > Signed-off-by: Hao Xu <haoxu@xxxxxxxxxxxxxxxxx> > --- > I've written corresponding liburing tests for this feature. Currently > just a simple test for BLKGETSIZE operation. I'll release it later soon > when it gets better. > > fs/io_uring.c | 56 +++++++++++++++++++++++++++++++++++++++++++ > fs/ioctl.c | 4 ++-- > include/linux/fs.h | 3 ++- > include/uapi/linux/io_uring.h | 1 + > 4 files changed, 61 insertions(+), 3 deletions(-) > > diff --git a/fs/io_uring.c b/fs/io_uring.c > index b42dfa0243bf..c8ab6b6d2d70 100644 > --- a/fs/io_uring.c > +++ b/fs/io_uring.c > @@ -539,6 +539,13 @@ struct io_statx { > struct statx __user *buffer; > }; > > +struct io_ioctl { > + struct file *file; > + unsigned int fd; > + unsigned int cmd; > + unsigned long arg; > +}; > + > struct io_completion { > struct file *file; > struct list_head list; > @@ -665,6 +672,7 @@ struct io_kiocb { > struct io_splice splice; > struct io_provide_buf pbuf; > struct io_statx statx; > + struct io_ioctl ioctl; > /* use only after cleaning per-op data, see io_clean_op() */ > struct io_completion compl; > }; > @@ -932,6 +940,10 @@ struct io_op_def { > .hash_reg_file = 1, > .unbound_nonreg_file = 1, > }, > + [IORING_OP_IOCTL] = { > + .needs_file = 1, > + .work_flags = IO_WQ_WORK_MM | IO_WQ_WORK_FILES > + }, > }; > > enum io_mem_account { > @@ -4819,6 +4831,45 @@ static int io_connect(struct io_kiocb *req, bool force_nonblock, > } > #endif /* CONFIG_NET */ > > +static int io_ioctl_prep(struct io_kiocb *req, > + const struct io_uring_sqe *sqe) > +{ > + if (sqe->ioprio || sqe->buf_index || sqe->rw_flags) > + return -EINVAL; > + if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) > + return -EINVAL; > + > + req->ioctl.fd = READ_ONCE(sqe->fd); > + req->ioctl.cmd = READ_ONCE(sqe->len); > + req->ioctl.arg = READ_ONCE(sqe->addr); > + return 0; > +} > + > +static int io_ioctl(struct io_kiocb *req, bool force_nonblock) > +{ > + int ret; > + > + if (force_nonblock) > + return -EAGAIN; > + > + if (!req->file) > + return -EBADF; > + > + ret = security_file_ioctl(req->file, req->ioctl.cmd, req->ioctl.arg); > + if (ret) > + goto out; > + > + ret = do_vfs_ioctl(req->file, req->ioctl.fd, req->ioctl.cmd, req->ioctl.arg); > + if (ret == -ENOIOCTLCMD) > + ret = vfs_ioctl(req->file, req->ioctl.cmd, req->ioctl.arg); > + > +out: > + if (ret) > + req_set_fail_links(req); > + io_req_complete(req, ret); > + return 0; > +} > + > struct io_poll_table { > struct poll_table_struct pt; > struct io_kiocb *req; > @@ -5742,6 +5793,8 @@ static int io_req_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) > return io_remove_buffers_prep(req, sqe); > case IORING_OP_TEE: > return io_tee_prep(req, sqe); > + case IORING_OP_IOCTL: > + return io_ioctl_prep(req, sqe); > } > > printk_once(KERN_WARNING "io_uring: unhandled opcode %d\n", > @@ -5985,6 +6038,9 @@ static int io_issue_sqe(struct io_kiocb *req, bool force_nonblock, > case IORING_OP_TEE: > ret = io_tee(req, force_nonblock); > break; > + case IORING_OP_IOCTL: > + ret = io_ioctl(req, force_nonblock); > + break; > default: > ret = -EINVAL; > break; > diff --git a/fs/ioctl.c b/fs/ioctl.c > index 4e6cc0a7d69c..4ff2eb0d8ee0 100644 > --- a/fs/ioctl.c > +++ b/fs/ioctl.c > @@ -664,8 +664,8 @@ static int ioctl_file_dedupe_range(struct file *file, > * When you add any new common ioctls to the switches above and below, > * please ensure they have compatible arguments in compat mode. > */ > -static int do_vfs_ioctl(struct file *filp, unsigned int fd, > - unsigned int cmd, unsigned long arg) > +int do_vfs_ioctl(struct file *filp, unsigned int fd, > + unsigned int cmd, unsigned long arg) > { > void __user *argp = (void __user *)arg; > struct inode *inode = file_inode(filp); > diff --git a/include/linux/fs.h b/include/linux/fs.h > index 0bd126418bb6..ad62aa6f6136 100644 > --- a/include/linux/fs.h > +++ b/include/linux/fs.h > @@ -1732,7 +1732,8 @@ int vfs_mkobj(struct dentry *, umode_t, > int vfs_utimes(const struct path *path, struct timespec64 *times); > > extern long vfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg); > - > +extern int do_vfs_ioctl(struct file *filp, unsigned int fd, > + unsigned int cmd, unsigned long arg); > #ifdef CONFIG_COMPAT > extern long compat_ptr_ioctl(struct file *file, unsigned int cmd, > unsigned long arg); > diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h > index 98d8e06dea22..4919b4e94c12 100644 > --- a/include/uapi/linux/io_uring.h > +++ b/include/uapi/linux/io_uring.h > @@ -132,6 +132,7 @@ enum { > IORING_OP_PROVIDE_BUFFERS, > IORING_OP_REMOVE_BUFFERS, > IORING_OP_TEE, > + IORING_OP_IOCTL, > > /* this goes last, obviously */ > IORING_OP_LAST, > -- Pavel Begunkov