xref: /linux/io_uring/cmd_net.c (revision 8457669db968c98edb781892d73fa559e1efcbd4)
1ccd18ce2STim Bird // SPDX-License-Identifier: GPL-2.0
291db6edcSPavel Begunkov #include <asm/ioctls.h>
391db6edcSPavel Begunkov #include <linux/io_uring/net.h>
49e4ed359SPavel Begunkov #include <linux/errqueue.h>
591db6edcSPavel Begunkov #include <net/sock.h>
691db6edcSPavel Begunkov 
791db6edcSPavel Begunkov #include "uring_cmd.h"
81e81bf14SJens Axboe #include "io_uring.h"
991db6edcSPavel Begunkov 
io_uring_cmd_getsockopt(struct socket * sock,struct io_uring_cmd * cmd,unsigned int issue_flags)1091db6edcSPavel Begunkov static inline int io_uring_cmd_getsockopt(struct socket *sock,
1191db6edcSPavel Begunkov 					  struct io_uring_cmd *cmd,
1291db6edcSPavel Begunkov 					  unsigned int issue_flags)
1391db6edcSPavel Begunkov {
1491db6edcSPavel Begunkov 	const struct io_uring_sqe *sqe = cmd->sqe;
1591db6edcSPavel Begunkov 	bool compat = !!(issue_flags & IO_URING_F_COMPAT);
1691db6edcSPavel Begunkov 	int optlen, optname, level, err;
1791db6edcSPavel Begunkov 	void __user *optval;
1891db6edcSPavel Begunkov 
1991db6edcSPavel Begunkov 	level = READ_ONCE(sqe->level);
2091db6edcSPavel Begunkov 	if (level != SOL_SOCKET)
2191db6edcSPavel Begunkov 		return -EOPNOTSUPP;
2291db6edcSPavel Begunkov 
2391db6edcSPavel Begunkov 	optval = u64_to_user_ptr(READ_ONCE(sqe->optval));
2491db6edcSPavel Begunkov 	optname = READ_ONCE(sqe->optname);
2591db6edcSPavel Begunkov 	optlen = READ_ONCE(sqe->optlen);
2691db6edcSPavel Begunkov 
2791db6edcSPavel Begunkov 	err = do_sock_getsockopt(sock, compat, level, optname,
2891db6edcSPavel Begunkov 				 USER_SOCKPTR(optval),
2991db6edcSPavel Begunkov 				 KERNEL_SOCKPTR(&optlen));
3091db6edcSPavel Begunkov 	if (err)
3191db6edcSPavel Begunkov 		return err;
3291db6edcSPavel Begunkov 
3391db6edcSPavel Begunkov 	/* On success, return optlen */
3491db6edcSPavel Begunkov 	return optlen;
3591db6edcSPavel Begunkov }
3691db6edcSPavel Begunkov 
io_uring_cmd_setsockopt(struct socket * sock,struct io_uring_cmd * cmd,unsigned int issue_flags)3791db6edcSPavel Begunkov static inline int io_uring_cmd_setsockopt(struct socket *sock,
3891db6edcSPavel Begunkov 					  struct io_uring_cmd *cmd,
3991db6edcSPavel Begunkov 					  unsigned int issue_flags)
4091db6edcSPavel Begunkov {
4191db6edcSPavel Begunkov 	const struct io_uring_sqe *sqe = cmd->sqe;
4291db6edcSPavel Begunkov 	bool compat = !!(issue_flags & IO_URING_F_COMPAT);
4391db6edcSPavel Begunkov 	int optname, optlen, level;
4491db6edcSPavel Begunkov 	void __user *optval;
4591db6edcSPavel Begunkov 	sockptr_t optval_s;
4691db6edcSPavel Begunkov 
4791db6edcSPavel Begunkov 	optval = u64_to_user_ptr(READ_ONCE(sqe->optval));
4891db6edcSPavel Begunkov 	optname = READ_ONCE(sqe->optname);
4991db6edcSPavel Begunkov 	optlen = READ_ONCE(sqe->optlen);
5091db6edcSPavel Begunkov 	level = READ_ONCE(sqe->level);
5191db6edcSPavel Begunkov 	optval_s = USER_SOCKPTR(optval);
5291db6edcSPavel Begunkov 
5391db6edcSPavel Begunkov 	return do_sock_setsockopt(sock, compat, level, optname, optval_s,
5491db6edcSPavel Begunkov 				  optlen);
5591db6edcSPavel Begunkov }
5691db6edcSPavel Begunkov 
io_process_timestamp_skb(struct io_uring_cmd * cmd,struct sock * sk,struct sk_buff * skb,unsigned issue_flags)579e4ed359SPavel Begunkov static bool io_process_timestamp_skb(struct io_uring_cmd *cmd, struct sock *sk,
589e4ed359SPavel Begunkov 				     struct sk_buff *skb, unsigned issue_flags)
599e4ed359SPavel Begunkov {
609e4ed359SPavel Begunkov 	struct sock_exterr_skb *serr = SKB_EXT_ERR(skb);
619e4ed359SPavel Begunkov 	struct io_uring_cqe cqe[2];
629e4ed359SPavel Begunkov 	struct io_timespec *iots;
639e4ed359SPavel Begunkov 	struct timespec64 ts;
649e4ed359SPavel Begunkov 	u32 tstype, tskey;
659e4ed359SPavel Begunkov 	int ret;
669e4ed359SPavel Begunkov 
679e4ed359SPavel Begunkov 	BUILD_BUG_ON(sizeof(struct io_uring_cqe) != sizeof(struct io_timespec));
689e4ed359SPavel Begunkov 
699e4ed359SPavel Begunkov 	ret = skb_get_tx_timestamp(skb, sk, &ts);
709e4ed359SPavel Begunkov 	if (ret < 0)
719e4ed359SPavel Begunkov 		return false;
729e4ed359SPavel Begunkov 
739e4ed359SPavel Begunkov 	tskey = serr->ee.ee_data;
749e4ed359SPavel Begunkov 	tstype = serr->ee.ee_info;
759e4ed359SPavel Begunkov 
769e4ed359SPavel Begunkov 	cqe->user_data = 0;
779e4ed359SPavel Begunkov 	cqe->res = tskey;
781e81bf14SJens Axboe 	cqe->flags = IORING_CQE_F_MORE | ctx_cqe32_flags(cmd_to_io_kiocb(cmd)->ctx);
799e4ed359SPavel Begunkov 	cqe->flags |= tstype << IORING_TIMESTAMP_TYPE_SHIFT;
809e4ed359SPavel Begunkov 	if (ret == SOF_TIMESTAMPING_TX_HARDWARE)
819e4ed359SPavel Begunkov 		cqe->flags |= IORING_CQE_F_TSTAMP_HW;
829e4ed359SPavel Begunkov 
839e4ed359SPavel Begunkov 	iots = (struct io_timespec *)&cqe[1];
849e4ed359SPavel Begunkov 	iots->tv_sec = ts.tv_sec;
859e4ed359SPavel Begunkov 	iots->tv_nsec = ts.tv_nsec;
869e4ed359SPavel Begunkov 	return io_uring_cmd_post_mshot_cqe32(cmd, issue_flags, cqe);
879e4ed359SPavel Begunkov }
889e4ed359SPavel Begunkov 
io_uring_cmd_timestamp(struct socket * sock,struct io_uring_cmd * cmd,unsigned int issue_flags)899e4ed359SPavel Begunkov static int io_uring_cmd_timestamp(struct socket *sock,
909e4ed359SPavel Begunkov 				  struct io_uring_cmd *cmd,
919e4ed359SPavel Begunkov 				  unsigned int issue_flags)
929e4ed359SPavel Begunkov {
939e4ed359SPavel Begunkov 	struct sock *sk = sock->sk;
949e4ed359SPavel Begunkov 	struct sk_buff_head *q = &sk->sk_error_queue;
959e4ed359SPavel Begunkov 	struct sk_buff *skb, *tmp;
969e4ed359SPavel Begunkov 	struct sk_buff_head list;
979e4ed359SPavel Begunkov 	int ret;
989e4ed359SPavel Begunkov 
999e4ed359SPavel Begunkov 	if (!(issue_flags & IO_URING_F_CQE32))
1009e4ed359SPavel Begunkov 		return -EINVAL;
1019e4ed359SPavel Begunkov 	ret = io_cmd_poll_multishot(cmd, issue_flags, EPOLLERR);
1029e4ed359SPavel Begunkov 	if (unlikely(ret))
1039e4ed359SPavel Begunkov 		return ret;
1049e4ed359SPavel Begunkov 
1059e4ed359SPavel Begunkov 	if (skb_queue_empty_lockless(q))
1069e4ed359SPavel Begunkov 		return -EAGAIN;
1079e4ed359SPavel Begunkov 	__skb_queue_head_init(&list);
1089e4ed359SPavel Begunkov 
1099e4ed359SPavel Begunkov 	scoped_guard(spinlock_irq, &q->lock) {
1109e4ed359SPavel Begunkov 		skb_queue_walk_safe(q, skb, tmp) {
1119e4ed359SPavel Begunkov 			/* don't support skbs with payload */
1129e4ed359SPavel Begunkov 			if (!skb_has_tx_timestamp(skb, sk) || skb->len)
1139e4ed359SPavel Begunkov 				continue;
1149e4ed359SPavel Begunkov 			__skb_unlink(skb, q);
1159e4ed359SPavel Begunkov 			__skb_queue_tail(&list, skb);
1169e4ed359SPavel Begunkov 		}
1179e4ed359SPavel Begunkov 	}
1189e4ed359SPavel Begunkov 
1199e4ed359SPavel Begunkov 	while (1) {
1209e4ed359SPavel Begunkov 		skb = skb_peek(&list);
1219e4ed359SPavel Begunkov 		if (!skb)
1229e4ed359SPavel Begunkov 			break;
1239e4ed359SPavel Begunkov 		if (!io_process_timestamp_skb(cmd, sk, skb, issue_flags))
1249e4ed359SPavel Begunkov 			break;
1259e4ed359SPavel Begunkov 		__skb_dequeue(&list);
1269e4ed359SPavel Begunkov 		consume_skb(skb);
1279e4ed359SPavel Begunkov 	}
1289e4ed359SPavel Begunkov 
1299e4ed359SPavel Begunkov 	if (!unlikely(skb_queue_empty(&list))) {
1309e4ed359SPavel Begunkov 		scoped_guard(spinlock_irqsave, &q->lock)
13146447367SJens Axboe 			skb_queue_splice(&list, q);
1329e4ed359SPavel Begunkov 	}
1339e4ed359SPavel Begunkov 	return -EAGAIN;
1349e4ed359SPavel Begunkov }
1359e4ed359SPavel Begunkov 
io_uring_cmd_getsockname(struct socket * sock,struct io_uring_cmd * cmd,unsigned int issue_flags)1365d24321eSGabriel Krisman Bertazi static int io_uring_cmd_getsockname(struct socket *sock,
1375d24321eSGabriel Krisman Bertazi 				    struct io_uring_cmd *cmd,
1385d24321eSGabriel Krisman Bertazi 				    unsigned int issue_flags)
1395d24321eSGabriel Krisman Bertazi {
1405d24321eSGabriel Krisman Bertazi 	const struct io_uring_sqe *sqe = cmd->sqe;
1415d24321eSGabriel Krisman Bertazi 	struct sockaddr __user *uaddr;
1425d24321eSGabriel Krisman Bertazi 	unsigned int peer;
1435d24321eSGabriel Krisman Bertazi 	int __user *ulen;
1445d24321eSGabriel Krisman Bertazi 
1455d24321eSGabriel Krisman Bertazi 	if (sqe->ioprio || sqe->__pad1 || sqe->len || sqe->rw_flags)
1465d24321eSGabriel Krisman Bertazi 		return -EINVAL;
1475d24321eSGabriel Krisman Bertazi 
1485d24321eSGabriel Krisman Bertazi 	uaddr = u64_to_user_ptr(READ_ONCE(sqe->addr));
149*a4643553SJens Axboe 	ulen = u64_to_user_ptr(READ_ONCE(sqe->addr3));
1505d24321eSGabriel Krisman Bertazi 	peer = READ_ONCE(sqe->optlen);
1515d24321eSGabriel Krisman Bertazi 	if (peer > 1)
1525d24321eSGabriel Krisman Bertazi 		return -EINVAL;
1535d24321eSGabriel Krisman Bertazi 	return do_getsockname(sock, peer, uaddr, ulen);
1545d24321eSGabriel Krisman Bertazi }
1555d24321eSGabriel Krisman Bertazi 
io_uring_cmd_sock(struct io_uring_cmd * cmd,unsigned int issue_flags)15691db6edcSPavel Begunkov int io_uring_cmd_sock(struct io_uring_cmd *cmd, unsigned int issue_flags)
15791db6edcSPavel Begunkov {
15891db6edcSPavel Begunkov 	struct socket *sock = cmd->file->private_data;
15991db6edcSPavel Begunkov 	struct sock *sk = sock->sk;
16091db6edcSPavel Begunkov 	struct proto *prot = READ_ONCE(sk->sk_prot);
16191db6edcSPavel Begunkov 	int ret, arg = 0;
16291db6edcSPavel Begunkov 
163600b665bSAsbjørn Sloth Tønnesen 	switch (cmd->cmd_op) {
164600b665bSAsbjørn Sloth Tønnesen 	case SOCKET_URING_OP_SIOCINQ:
16591db6edcSPavel Begunkov 		if (!prot || !prot->ioctl)
16691db6edcSPavel Begunkov 			return -EOPNOTSUPP;
16791db6edcSPavel Begunkov 
16891db6edcSPavel Begunkov 		ret = prot->ioctl(sk, SIOCINQ, &arg);
16991db6edcSPavel Begunkov 		if (ret)
17091db6edcSPavel Begunkov 			return ret;
17191db6edcSPavel Begunkov 		return arg;
17291db6edcSPavel Begunkov 	case SOCKET_URING_OP_SIOCOUTQ:
173600b665bSAsbjørn Sloth Tønnesen 		if (!prot || !prot->ioctl)
174600b665bSAsbjørn Sloth Tønnesen 			return -EOPNOTSUPP;
175600b665bSAsbjørn Sloth Tønnesen 
17691db6edcSPavel Begunkov 		ret = prot->ioctl(sk, SIOCOUTQ, &arg);
17791db6edcSPavel Begunkov 		if (ret)
17891db6edcSPavel Begunkov 			return ret;
17991db6edcSPavel Begunkov 		return arg;
18091db6edcSPavel Begunkov 	case SOCKET_URING_OP_GETSOCKOPT:
18191db6edcSPavel Begunkov 		return io_uring_cmd_getsockopt(sock, cmd, issue_flags);
18291db6edcSPavel Begunkov 	case SOCKET_URING_OP_SETSOCKOPT:
18391db6edcSPavel Begunkov 		return io_uring_cmd_setsockopt(sock, cmd, issue_flags);
1849e4ed359SPavel Begunkov 	case SOCKET_URING_OP_TX_TIMESTAMP:
1859e4ed359SPavel Begunkov 		return io_uring_cmd_timestamp(sock, cmd, issue_flags);
1865d24321eSGabriel Krisman Bertazi 	case SOCKET_URING_OP_GETSOCKNAME:
1875d24321eSGabriel Krisman Bertazi 		return io_uring_cmd_getsockname(sock, cmd, issue_flags);
18891db6edcSPavel Begunkov 	default:
18991db6edcSPavel Begunkov 		return -EOPNOTSUPP;
19091db6edcSPavel Begunkov 	}
19191db6edcSPavel Begunkov }
19291db6edcSPavel Begunkov EXPORT_SYMBOL_GPL(io_uring_cmd_sock);
193