1498 lines · 37.2 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/net.h>#include <linux/compat.h>#include <net/compat.h>#include <linux/io_uring.h> #include <uapi/linux/io_uring.h> #include "io_uring.h"#include "kbuf.h"#include "alloc_cache.h"#include "net.h"#include "notif.h"#include "rsrc.h" #if defined(CONFIG_NET)struct io_shutdown {	struct file			*file;	int				how;}; struct io_accept {	struct file			*file;	struct sockaddr __user		*addr;	int __user			*addr_len;	int				flags;	u32				file_slot;	unsigned long			nofile;}; struct io_socket {	struct file			*file;	int				domain;	int				type;	int				protocol;	int				flags;	u32				file_slot;	unsigned long			nofile;}; struct io_connect {	struct file			*file;	struct sockaddr __user		*addr;	int				addr_len;	bool				in_progress;}; struct io_sr_msg {	struct file			*file;	union {		struct compat_msghdr __user	*umsg_compat;		struct user_msghdr __user	*umsg;		void __user			*buf;	};	unsigned			len;	unsigned			done_io;	unsigned			msg_flags;	u16				flags;	/* initialised and used only by !msg send variants */	u16				addr_len;	u16				buf_group;	void __user			*addr;	/* used only for send zerocopy */	struct io_kiocb 		*notif;}; static inline bool io_check_multishot(struct io_kiocb *req,				      unsigned int issue_flags){	/*	 * When ->locked_cq is set we only allow to post CQEs from the original	 * task context. Usual request completions will be handled in other	 * generic paths but multipoll may decide to post extra cqes.	 */	return !(issue_flags & IO_URING_F_IOWQ) ||		!(issue_flags & IO_URING_F_MULTISHOT) ||		!req->ctx->task_complete;} int io_shutdown_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe){	struct io_shutdown *shutdown = io_kiocb_to_cmd(req, struct io_shutdown); 	if (unlikely(sqe->off || sqe->addr || sqe->rw_flags ||		     sqe->buf_index || sqe->splice_fd_in))		return -EINVAL; 	shutdown->how = READ_ONCE(sqe->len);	return 0;} int io_shutdown(struct io_kiocb *req, unsigned int issue_flags){	struct io_shutdown *shutdown = io_kiocb_to_cmd(req, struct io_shutdown);	struct socket *sock;	int ret; 	if (issue_flags & IO_URING_F_NONBLOCK)		return -EAGAIN; 	sock = sock_from_file(req->file);	if (unlikely(!sock))		return -ENOTSOCK; 	ret = __sys_shutdown_sock(sock, shutdown->how);	io_req_set_res(req, ret, 0);	return IOU_OK;} static bool io_net_retry(struct socket *sock, int flags){	if (!(flags & MSG_WAITALL))		return false;	return sock->type == SOCK_STREAM || sock->type == SOCK_SEQPACKET;} static void io_netmsg_recycle(struct io_kiocb *req, unsigned int issue_flags){	struct io_async_msghdr *hdr = req->async_data; 	if (!req_has_async_data(req) || issue_flags & IO_URING_F_UNLOCKED)		return; 	/* Let normal cleanup path reap it if we fail adding to the cache */	if (io_alloc_cache_put(&req->ctx->netmsg_cache, &hdr->cache)) {		req->async_data = NULL;		req->flags &= ~REQ_F_ASYNC_DATA;	}} static struct io_async_msghdr *io_msg_alloc_async(struct io_kiocb *req,						  unsigned int issue_flags){	struct io_ring_ctx *ctx = req->ctx;	struct io_cache_entry *entry;	struct io_async_msghdr *hdr; 	if (!(issue_flags & IO_URING_F_UNLOCKED)) {		entry = io_alloc_cache_get(&ctx->netmsg_cache);		if (entry) {			hdr = container_of(entry, struct io_async_msghdr, cache);			hdr->free_iov = NULL;			req->flags |= REQ_F_ASYNC_DATA;			req->async_data = hdr;			return hdr;		}	} 	if (!io_alloc_async_data(req)) {		hdr = req->async_data;		hdr->free_iov = NULL;		return hdr;	}	return NULL;} static inline struct io_async_msghdr *io_msg_alloc_async_prep(struct io_kiocb *req){	/* ->prep_async is always called from the submission context */	return io_msg_alloc_async(req, 0);} static int io_setup_async_msg(struct io_kiocb *req,			      struct io_async_msghdr *kmsg,			      unsigned int issue_flags){	struct io_async_msghdr *async_msg; 	if (req_has_async_data(req))		return -EAGAIN;	async_msg = io_msg_alloc_async(req, issue_flags);	if (!async_msg) {		kfree(kmsg->free_iov);		return -ENOMEM;	}	req->flags |= REQ_F_NEED_CLEANUP;	memcpy(async_msg, kmsg, sizeof(*kmsg));	if (async_msg->msg.msg_name)		async_msg->msg.msg_name = &async_msg->addr;	/* if were using fast_iov, set it to the new one */	if (!kmsg->free_iov) {		size_t fast_idx = kmsg->msg.msg_iter.iov - kmsg->fast_iov;		async_msg->msg.msg_iter.iov = &async_msg->fast_iov[fast_idx];	} 	return -EAGAIN;} static int io_sendmsg_copy_hdr(struct io_kiocb *req,			       struct io_async_msghdr *iomsg){	struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg); 	iomsg->msg.msg_name = &iomsg->addr;	iomsg->free_iov = iomsg->fast_iov;	return sendmsg_copy_msghdr(&iomsg->msg, sr->umsg, sr->msg_flags,					&iomsg->free_iov);} int io_send_prep_async(struct io_kiocb *req){	struct io_sr_msg *zc = io_kiocb_to_cmd(req, struct io_sr_msg);	struct io_async_msghdr *io;	int ret; 	if (!zc->addr || req_has_async_data(req))		return 0;	io = io_msg_alloc_async_prep(req);	if (!io)		return -ENOMEM;	ret = move_addr_to_kernel(zc->addr, zc->addr_len, &io->addr);	return ret;} static int io_setup_async_addr(struct io_kiocb *req,			      struct sockaddr_storage *addr_storage,			      unsigned int issue_flags){	struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg);	struct io_async_msghdr *io; 	if (!sr->addr || req_has_async_data(req))		return -EAGAIN;	io = io_msg_alloc_async(req, issue_flags);	if (!io)		return -ENOMEM;	memcpy(&io->addr, addr_storage, sizeof(io->addr));	return -EAGAIN;} int io_sendmsg_prep_async(struct io_kiocb *req){	int ret; 	if (!io_msg_alloc_async_prep(req))		return -ENOMEM;	ret = io_sendmsg_copy_hdr(req, req->async_data);	if (!ret)		req->flags |= REQ_F_NEED_CLEANUP;	return ret;} void io_sendmsg_recvmsg_cleanup(struct io_kiocb *req){	struct io_async_msghdr *io = req->async_data; 	kfree(io->free_iov);} int io_sendmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe){	struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg); 	if (req->opcode == IORING_OP_SEND) {		if (READ_ONCE(sqe->__pad3[0]))			return -EINVAL;		sr->addr = u64_to_user_ptr(READ_ONCE(sqe->addr2));		sr->addr_len = READ_ONCE(sqe->addr_len);	} else if (sqe->addr2 || sqe->file_index) {		return -EINVAL;	} 	sr->umsg = u64_to_user_ptr(READ_ONCE(sqe->addr));	sr->len = READ_ONCE(sqe->len);	sr->flags = READ_ONCE(sqe->ioprio);	if (sr->flags & ~IORING_RECVSEND_POLL_FIRST)		return -EINVAL;	sr->msg_flags = READ_ONCE(sqe->msg_flags) | MSG_NOSIGNAL;	if (sr->msg_flags & MSG_DONTWAIT)		req->flags |= REQ_F_NOWAIT; #ifdef CONFIG_COMPAT	if (req->ctx->compat)		sr->msg_flags |= MSG_CMSG_COMPAT;#endif	sr->done_io = 0;	return 0;} int io_sendmsg(struct io_kiocb *req, unsigned int issue_flags){	struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg);	struct io_async_msghdr iomsg, *kmsg;	struct socket *sock;	unsigned flags;	int min_ret = 0;	int ret; 	sock = sock_from_file(req->file);	if (unlikely(!sock))		return -ENOTSOCK; 	if (req_has_async_data(req)) {		kmsg = req->async_data;	} else {		ret = io_sendmsg_copy_hdr(req, &iomsg);		if (ret)			return ret;		kmsg = &iomsg;	} 	if (!(req->flags & REQ_F_POLLED) &&	    (sr->flags & IORING_RECVSEND_POLL_FIRST))		return io_setup_async_msg(req, kmsg, issue_flags); 	flags = sr->msg_flags;	if (issue_flags & IO_URING_F_NONBLOCK)		flags |= MSG_DONTWAIT;	if (flags & MSG_WAITALL)		min_ret = iov_iter_count(&kmsg->msg.msg_iter); 	ret = __sys_sendmsg_sock(sock, &kmsg->msg, flags); 	if (ret < min_ret) {		if (ret == -EAGAIN && (issue_flags & IO_URING_F_NONBLOCK))			return io_setup_async_msg(req, kmsg, issue_flags);		if (ret > 0 && io_net_retry(sock, flags)) {			sr->done_io += ret;			req->flags |= REQ_F_PARTIAL_IO;			return io_setup_async_msg(req, kmsg, issue_flags);		}		if (ret == -ERESTARTSYS)			ret = -EINTR;		req_set_fail(req);	}	/* fast path, check for non-NULL to avoid function call */	if (kmsg->free_iov)		kfree(kmsg->free_iov);	req->flags &= ~REQ_F_NEED_CLEANUP;	io_netmsg_recycle(req, issue_flags);	if (ret >= 0)		ret += sr->done_io;	else if (sr->done_io)		ret = sr->done_io;	io_req_set_res(req, ret, 0);	return IOU_OK;} int io_send(struct io_kiocb *req, unsigned int issue_flags){	struct sockaddr_storage __address;	struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg);	struct msghdr msg;	struct iovec iov;	struct socket *sock;	unsigned flags;	int min_ret = 0;	int ret; 	msg.msg_name = NULL;	msg.msg_control = NULL;	msg.msg_controllen = 0;	msg.msg_namelen = 0;	msg.msg_ubuf = NULL; 	if (sr->addr) {		if (req_has_async_data(req)) {			struct io_async_msghdr *io = req->async_data; 			msg.msg_name = &io->addr;		} else {			ret = move_addr_to_kernel(sr->addr, sr->addr_len, &__address);			if (unlikely(ret < 0))				return ret;			msg.msg_name = (struct sockaddr *)&__address;		}		msg.msg_namelen = sr->addr_len;	} 	if (!(req->flags & REQ_F_POLLED) &&	    (sr->flags & IORING_RECVSEND_POLL_FIRST))		return io_setup_async_addr(req, &__address, issue_flags); 	sock = sock_from_file(req->file);	if (unlikely(!sock))		return -ENOTSOCK; 	ret = import_single_range(ITER_SOURCE, sr->buf, sr->len, &iov, &msg.msg_iter);	if (unlikely(ret))		return ret; 	flags = sr->msg_flags;	if (issue_flags & IO_URING_F_NONBLOCK)		flags |= MSG_DONTWAIT;	if (flags & MSG_WAITALL)		min_ret = iov_iter_count(&msg.msg_iter); 	msg.msg_flags = flags;	ret = sock_sendmsg(sock, &msg);	if (ret < min_ret) {		if (ret == -EAGAIN && (issue_flags & IO_URING_F_NONBLOCK))			return io_setup_async_addr(req, &__address, issue_flags); 		if (ret > 0 && io_net_retry(sock, flags)) {			sr->len -= ret;			sr->buf += ret;			sr->done_io += ret;			req->flags |= REQ_F_PARTIAL_IO;			return io_setup_async_addr(req, &__address, issue_flags);		}		if (ret == -ERESTARTSYS)			ret = -EINTR;		req_set_fail(req);	}	if (ret >= 0)		ret += sr->done_io;	else if (sr->done_io)		ret = sr->done_io;	io_req_set_res(req, ret, 0);	return IOU_OK;} static bool io_recvmsg_multishot_overflow(struct io_async_msghdr *iomsg){	int hdr; 	if (iomsg->namelen < 0)		return true;	if (check_add_overflow((int)sizeof(struct io_uring_recvmsg_out),			       iomsg->namelen, &hdr))		return true;	if (check_add_overflow(hdr, (int)iomsg->controllen, &hdr))		return true; 	return false;} static int __io_recvmsg_copy_hdr(struct io_kiocb *req,				 struct io_async_msghdr *iomsg){	struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg);	struct user_msghdr msg;	int ret; 	if (copy_from_user(&msg, sr->umsg, sizeof(*sr->umsg)))		return -EFAULT; 	ret = __copy_msghdr(&iomsg->msg, &msg, &iomsg->uaddr);	if (ret)		return ret; 	if (req->flags & REQ_F_BUFFER_SELECT) {		if (msg.msg_iovlen == 0) {			sr->len = iomsg->fast_iov[0].iov_len = 0;			iomsg->fast_iov[0].iov_base = NULL;			iomsg->free_iov = NULL;		} else if (msg.msg_iovlen > 1) {			return -EINVAL;		} else {			if (copy_from_user(iomsg->fast_iov, msg.msg_iov, sizeof(*msg.msg_iov)))				return -EFAULT;			sr->len = iomsg->fast_iov[0].iov_len;			iomsg->free_iov = NULL;		} 		if (req->flags & REQ_F_APOLL_MULTISHOT) {			iomsg->namelen = msg.msg_namelen;			iomsg->controllen = msg.msg_controllen;			if (io_recvmsg_multishot_overflow(iomsg))				return -EOVERFLOW;		}	} else {		iomsg->free_iov = iomsg->fast_iov;		ret = __import_iovec(ITER_DEST, msg.msg_iov, msg.msg_iovlen, UIO_FASTIOV,				     &iomsg->free_iov, &iomsg->msg.msg_iter,				     false);		if (ret > 0)			ret = 0;	} 	return ret;} #ifdef CONFIG_COMPATstatic int __io_compat_recvmsg_copy_hdr(struct io_kiocb *req,					struct io_async_msghdr *iomsg){	struct io_sr_msg *sr = io_kiocb_to_cmd(req, struct io_sr_msg);	struct compat_msghdr msg;	struct compat_iovec __user *uiov;	int ret; 	if (copy_from_user(&msg, sr->umsg_compat, sizeof(msg)))		return -EFAULT; 	ret = __get_compat_msghdr(&iomsg->msg, &msg, &iomsg->uaddr);	if (ret)		return ret; 	uiov = compat_ptr(msg.msg_iov);	if (req->flags & REQ_F_BUFFER_SELECT) {		compat_ssize_t clen; 		iomsg->free_iov = NULL;		if (msg.msg_iovlen == 0) {			sr->len = 0;

Lines 1–500 of 1498.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t