This adds support for async msgsnd through io_uring. The message is stored in msg pointer in io_msgsnd and is saved in io_setup_async_msgq if we need to punt to async context. Signed-off-by: Usama Arif <usama.arif@xxxxxxxxxxxxx> --- fs/io_uring.c | 107 ++++++++++++++++++++++++++++++++++ include/uapi/linux/io_uring.h | 1 + 2 files changed, 108 insertions(+) diff --git a/fs/io_uring.c b/fs/io_uring.c index 3aab4182fd89..5949fcadb380 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -81,6 +81,7 @@ #include <linux/audit.h> #include <linux/security.h> #include <linux/xattr.h> +#include <linux/msg.h> #define CREATE_TRACE_POINTS #include <trace/events/io_uring.h> @@ -669,6 +670,15 @@ struct io_sr_msg { unsigned int flags; }; +struct io_msg_sr { + struct file *file; + int msq_id; + struct msgbuf __user *msg_p; + size_t msg_sz; + long msg_type; + int msg_flags; +}; + struct io_open { struct file *file; int dfd; @@ -803,6 +813,10 @@ struct io_async_msghdr { struct sockaddr_storage addr; }; +struct io_async_msg_msg { + struct msg_msg *msg; +}; + struct io_rw_state { struct iov_iter iter; struct iov_iter_state iter_state; @@ -996,6 +1010,7 @@ struct io_kiocb { struct io_socket sock; struct io_nop nop; struct io_uring_cmd uring_cmd; + struct io_msg_sr msg_sr; }; u8 opcode; @@ -1199,6 +1214,9 @@ static const struct io_op_def io_op_defs[] = { .needs_async_setup = 1, .async_size = sizeof(struct io_async_msghdr), }, + [IORING_OP_MSGSND] = { + .async_size = sizeof(struct io_async_msg_msg), + }, [IORING_OP_TIMEOUT] = { .audit_skip = 1, .async_size = sizeof(struct io_timeout_data), @@ -1404,6 +1422,8 @@ const char *io_uring_get_opcode(u8 opcode) return "SENDMSG"; case IORING_OP_RECVMSG: return "RECVMSG"; + case IORING_OP_MSGSND: + return "MSGSND"; case IORING_OP_TIMEOUT: return "TIMEOUT"; case IORING_OP_TIMEOUT_REMOVE: @@ -6180,6 +6200,81 @@ static int io_sendmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) return 0; } +static int io_setup_async_msg_msg(struct io_kiocb *req, struct msg_msg *msg) +{ + struct io_async_msg_msg *async_msg_msg = req->async_data; + + if (async_msg_msg) + return -EAGAIN; + if (io_alloc_async_data(req)) + return -ENOMEM; + async_msg_msg = req->async_data; + + req->flags |= REQ_F_NEED_CLEANUP; + async_msg_msg->msg = msg; + + return -EAGAIN; +} + +static int io_msgsnd_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) +{ + struct io_msg_sr *msg_sr = &req->msg_sr; + struct msgbuf __user *msg_p; + long mtype; + + if (unlikely(sqe->addr2 || sqe->file_index)) + return -EINVAL; + + msg_sr->msq_id = READ_ONCE(sqe->fd); + msg_p = u64_to_user_ptr(READ_ONCE(sqe->addr)); + msg_sr->msg_p = msg_p; + if (get_user(mtype, &msg_p->mtype)) + return -EFAULT; + msg_sr->msg_type = mtype; + msg_sr->msg_sz = READ_ONCE(sqe->len); + msg_sr->msg_flags = READ_ONCE(sqe->msg_flags); + if (msg_sr->msg_flags & IPC_NOWAIT) + req->flags |= REQ_F_NOWAIT; + + return 0; +} + +static int io_msgsnd(struct io_kiocb *req, unsigned int issue_flags) +{ + struct io_async_msg_msg *async_msg_msg; + struct io_msg_sr *msg_sr = &req->msg_sr; + int ret; + int flags; + struct msg_msg *msg; + bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK; + + if (req_has_async_data(req)) { + async_msg_msg = req->async_data; + msg = async_msg_msg->msg; + } else { + ret = check_and_load_msgsnd(msg_sr->msq_id, msg_sr->msg_type, + msg_sr->msg_p->mtext, + &msg, msg_sr->msg_sz); + if (ret) + return ret; + } + + if (force_nonblock) + flags = msg_sr->msg_flags | IPC_NOWAIT; + + ret = __do_msgsnd(msg_sr->msq_id, msg_sr->msg_type, &msg, + msg_sr->msg_sz, flags); + + if (ret == -EAGAIN && (issue_flags & IO_URING_F_NONBLOCK)) + return io_setup_async_msg_msg(req, msg); + + if (msg != NULL) + free_msg(msg); + req->flags &= ~REQ_F_NEED_CLEANUP; + + io_req_complete(req, ret); + return ret; +} static int io_sendmsg(struct io_kiocb *req, unsigned int issue_flags) { struct io_async_msghdr iomsg, *kmsg; @@ -8192,6 +8287,8 @@ static int io_req_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) return io_socket_prep(req, sqe); case IORING_OP_URING_CMD: return io_uring_cmd_prep(req, sqe); + case IORING_OP_MSGSND: + return io_msgsnd_prep(req, sqe); } printk_once(KERN_WARNING "io_uring: unhandled opcode %d\n", @@ -8316,6 +8413,13 @@ static void io_clean_op(struct io_kiocb *req) kfree(io->free_iov); break; } + case IORING_OP_MSGSND: { + struct io_async_msg_msg *io = req->async_data; + + if (io->msg != NULL) + free_msg(io->msg); + break; + } case IORING_OP_OPENAT: case IORING_OP_OPENAT2: if (req->open.filename) @@ -8529,6 +8633,9 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags) case IORING_OP_URING_CMD: ret = io_uring_cmd(req, issue_flags); break; + case IORING_OP_MSGSND: + ret = io_msgsnd(req, issue_flags); + break; default: ret = -EINVAL; break; diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h index 776e0278f9dd..fa29bd96207d 100644 --- a/include/uapi/linux/io_uring.h +++ b/include/uapi/linux/io_uring.h @@ -190,6 +190,7 @@ enum io_uring_op { IORING_OP_GETXATTR, IORING_OP_SOCKET, IORING_OP_URING_CMD, + IORING_OP_MSGSND, /* this goes last, obviously */ IORING_OP_LAST, -- 2.25.1