172 lines · 4.1 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#include <linux/kernel.h>#include <linux/errno.h>#include <linux/file.h>#include <linux/slab.h>#include <linux/nospec.h>#include <linux/io_uring.h> #include <uapi/linux/io_uring.h> #include "io_uring.h"#include "rsrc.h"#include "filetable.h"#include "msg_ring.h" struct io_msg {	struct file			*file;	u64 user_data;	u32 len;	u32 cmd;	u32 src_fd;	u32 dst_fd;	u32 flags;}; static int io_msg_ring_data(struct io_kiocb *req){	struct io_ring_ctx *target_ctx = req->file->private_data;	struct io_msg *msg = io_kiocb_to_cmd(req, struct io_msg); 	if (msg->src_fd || msg->dst_fd || msg->flags)		return -EINVAL; 	if (io_post_aux_cqe(target_ctx, msg->user_data, msg->len, 0, true))		return 0; 	return -EOVERFLOW;} static void io_double_unlock_ctx(struct io_ring_ctx *ctx,				 struct io_ring_ctx *octx,				 unsigned int issue_flags){	if (issue_flags & IO_URING_F_UNLOCKED)		mutex_unlock(&ctx->uring_lock);	mutex_unlock(&octx->uring_lock);} static int io_double_lock_ctx(struct io_ring_ctx *ctx,			      struct io_ring_ctx *octx,			      unsigned int issue_flags){	/*	 * To ensure proper ordering between the two ctxs, we can only	 * attempt a trylock on the target. If that fails and we already have	 * the source ctx lock, punt to io-wq.	 */	if (!(issue_flags & IO_URING_F_UNLOCKED)) {		if (!mutex_trylock(&octx->uring_lock))			return -EAGAIN;		return 0;	} 	/* Always grab smallest value ctx first. We know ctx != octx. */	if (ctx < octx) {		mutex_lock(&ctx->uring_lock);		mutex_lock(&octx->uring_lock);	} else {		mutex_lock(&octx->uring_lock);		mutex_lock(&ctx->uring_lock);	} 	return 0;} static int io_msg_send_fd(struct io_kiocb *req, unsigned int issue_flags){	struct io_ring_ctx *target_ctx = req->file->private_data;	struct io_msg *msg = io_kiocb_to_cmd(req, struct io_msg);	struct io_ring_ctx *ctx = req->ctx;	unsigned long file_ptr;	struct file *src_file;	int ret; 	if (target_ctx == ctx)		return -EINVAL; 	ret = io_double_lock_ctx(ctx, target_ctx, issue_flags);	if (unlikely(ret))		return ret; 	ret = -EBADF;	if (unlikely(msg->src_fd >= ctx->nr_user_files))		goto out_unlock; 	msg->src_fd = array_index_nospec(msg->src_fd, ctx->nr_user_files);	file_ptr = io_fixed_file_slot(&ctx->file_table, msg->src_fd)->file_ptr;	src_file = (struct file *) (file_ptr & FFS_MASK);	get_file(src_file); 	ret = __io_fixed_fd_install(target_ctx, src_file, msg->dst_fd);	if (ret < 0) {		fput(src_file);		goto out_unlock;	} 	if (msg->flags & IORING_MSG_RING_CQE_SKIP)		goto out_unlock; 	/*	 * If this fails, the target still received the file descriptor but	 * wasn't notified of the fact. This means that if this request	 * completes with -EOVERFLOW, then the sender must ensure that a	 * later IORING_OP_MSG_RING delivers the message.	 */	if (!io_post_aux_cqe(target_ctx, msg->user_data, msg->len, 0, true))		ret = -EOVERFLOW;out_unlock:	io_double_unlock_ctx(ctx, target_ctx, issue_flags);	return ret;} int io_msg_ring_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe){	struct io_msg *msg = io_kiocb_to_cmd(req, struct io_msg); 	if (unlikely(sqe->buf_index || sqe->personality))		return -EINVAL; 	msg->user_data = READ_ONCE(sqe->off);	msg->len = READ_ONCE(sqe->len);	msg->cmd = READ_ONCE(sqe->addr);	msg->src_fd = READ_ONCE(sqe->addr3);	msg->dst_fd = READ_ONCE(sqe->file_index);	msg->flags = READ_ONCE(sqe->msg_ring_flags);	if (msg->flags & ~IORING_MSG_RING_CQE_SKIP)		return -EINVAL; 	return 0;} int io_msg_ring(struct io_kiocb *req, unsigned int issue_flags){	struct io_msg *msg = io_kiocb_to_cmd(req, struct io_msg);	int ret; 	ret = -EBADFD;	if (!io_is_uring_fops(req->file))		goto done; 	switch (msg->cmd) {	case IORING_MSG_DATA:		ret = io_msg_ring_data(req);		break;	case IORING_MSG_SEND_FD:		ret = io_msg_send_fd(req, issue_flags);		break;	default:		ret = -EINVAL;		break;	} done:	if (ret < 0)		req_set_fail(req);	io_req_set_res(req, ret, 0);	/* put file to avoid an attempt to IOPOLL the req */	if (!(req->flags & REQ_F_FIXED_FILE))		io_put_file(req->file);	req->file = NULL;	return IOU_OK;}