xref: /linux/io_uring/cmd_net.c (revision 46447367a52965e9d35f112f5b26fc8ff8ec443d)
191db6edcSPavel Begunkov #include <asm/ioctls.h>
291db6edcSPavel Begunkov #include <linux/io_uring/net.h>
39e4ed359SPavel Begunkov #include <linux/errqueue.h>
491db6edcSPavel Begunkov #include <net/sock.h>
591db6edcSPavel Begunkov 
691db6edcSPavel Begunkov #include "uring_cmd.h"
71e81bf14SJens Axboe #include "io_uring.h"
891db6edcSPavel Begunkov 
991db6edcSPavel Begunkov static inline int io_uring_cmd_getsockopt(struct socket *sock,
1091db6edcSPavel Begunkov 					  struct io_uring_cmd *cmd,
1191db6edcSPavel Begunkov 					  unsigned int issue_flags)
1291db6edcSPavel Begunkov {
1391db6edcSPavel Begunkov 	const struct io_uring_sqe *sqe = cmd->sqe;
1491db6edcSPavel Begunkov 	bool compat = !!(issue_flags & IO_URING_F_COMPAT);
1591db6edcSPavel Begunkov 	int optlen, optname, level, err;
1691db6edcSPavel Begunkov 	void __user *optval;
1791db6edcSPavel Begunkov 
1891db6edcSPavel Begunkov 	level = READ_ONCE(sqe->level);
1991db6edcSPavel Begunkov 	if (level != SOL_SOCKET)
2091db6edcSPavel Begunkov 		return -EOPNOTSUPP;
2191db6edcSPavel Begunkov 
2291db6edcSPavel Begunkov 	optval = u64_to_user_ptr(READ_ONCE(sqe->optval));
2391db6edcSPavel Begunkov 	optname = READ_ONCE(sqe->optname);
2491db6edcSPavel Begunkov 	optlen = READ_ONCE(sqe->optlen);
2591db6edcSPavel Begunkov 
2691db6edcSPavel Begunkov 	err = do_sock_getsockopt(sock, compat, level, optname,
2791db6edcSPavel Begunkov 				 USER_SOCKPTR(optval),
2891db6edcSPavel Begunkov 				 KERNEL_SOCKPTR(&optlen));
2991db6edcSPavel Begunkov 	if (err)
3091db6edcSPavel Begunkov 		return err;
3191db6edcSPavel Begunkov 
3291db6edcSPavel Begunkov 	/* On success, return optlen */
3391db6edcSPavel Begunkov 	return optlen;
3491db6edcSPavel Begunkov }
3591db6edcSPavel Begunkov 
3691db6edcSPavel Begunkov static inline int io_uring_cmd_setsockopt(struct socket *sock,
3791db6edcSPavel Begunkov 					  struct io_uring_cmd *cmd,
3891db6edcSPavel Begunkov 					  unsigned int issue_flags)
3991db6edcSPavel Begunkov {
4091db6edcSPavel Begunkov 	const struct io_uring_sqe *sqe = cmd->sqe;
4191db6edcSPavel Begunkov 	bool compat = !!(issue_flags & IO_URING_F_COMPAT);
4291db6edcSPavel Begunkov 	int optname, optlen, level;
4391db6edcSPavel Begunkov 	void __user *optval;
4491db6edcSPavel Begunkov 	sockptr_t optval_s;
4591db6edcSPavel Begunkov 
4691db6edcSPavel Begunkov 	optval = u64_to_user_ptr(READ_ONCE(sqe->optval));
4791db6edcSPavel Begunkov 	optname = READ_ONCE(sqe->optname);
4891db6edcSPavel Begunkov 	optlen = READ_ONCE(sqe->optlen);
4991db6edcSPavel Begunkov 	level = READ_ONCE(sqe->level);
5091db6edcSPavel Begunkov 	optval_s = USER_SOCKPTR(optval);
5191db6edcSPavel Begunkov 
5291db6edcSPavel Begunkov 	return do_sock_setsockopt(sock, compat, level, optname, optval_s,
5391db6edcSPavel Begunkov 				  optlen);
5491db6edcSPavel Begunkov }
5591db6edcSPavel Begunkov 
569e4ed359SPavel Begunkov static bool io_process_timestamp_skb(struct io_uring_cmd *cmd, struct sock *sk,
579e4ed359SPavel Begunkov 				     struct sk_buff *skb, unsigned issue_flags)
589e4ed359SPavel Begunkov {
599e4ed359SPavel Begunkov 	struct sock_exterr_skb *serr = SKB_EXT_ERR(skb);
609e4ed359SPavel Begunkov 	struct io_uring_cqe cqe[2];
619e4ed359SPavel Begunkov 	struct io_timespec *iots;
629e4ed359SPavel Begunkov 	struct timespec64 ts;
639e4ed359SPavel Begunkov 	u32 tstype, tskey;
649e4ed359SPavel Begunkov 	int ret;
659e4ed359SPavel Begunkov 
669e4ed359SPavel Begunkov 	BUILD_BUG_ON(sizeof(struct io_uring_cqe) != sizeof(struct io_timespec));
679e4ed359SPavel Begunkov 
689e4ed359SPavel Begunkov 	ret = skb_get_tx_timestamp(skb, sk, &ts);
699e4ed359SPavel Begunkov 	if (ret < 0)
709e4ed359SPavel Begunkov 		return false;
719e4ed359SPavel Begunkov 
729e4ed359SPavel Begunkov 	tskey = serr->ee.ee_data;
739e4ed359SPavel Begunkov 	tstype = serr->ee.ee_info;
749e4ed359SPavel Begunkov 
759e4ed359SPavel Begunkov 	cqe->user_data = 0;
769e4ed359SPavel Begunkov 	cqe->res = tskey;
771e81bf14SJens Axboe 	cqe->flags = IORING_CQE_F_MORE | ctx_cqe32_flags(cmd_to_io_kiocb(cmd)->ctx);
789e4ed359SPavel Begunkov 	cqe->flags |= tstype << IORING_TIMESTAMP_TYPE_SHIFT;
799e4ed359SPavel Begunkov 	if (ret == SOF_TIMESTAMPING_TX_HARDWARE)
809e4ed359SPavel Begunkov 		cqe->flags |= IORING_CQE_F_TSTAMP_HW;
819e4ed359SPavel Begunkov 
829e4ed359SPavel Begunkov 	iots = (struct io_timespec *)&cqe[1];
839e4ed359SPavel Begunkov 	iots->tv_sec = ts.tv_sec;
849e4ed359SPavel Begunkov 	iots->tv_nsec = ts.tv_nsec;
859e4ed359SPavel Begunkov 	return io_uring_cmd_post_mshot_cqe32(cmd, issue_flags, cqe);
869e4ed359SPavel Begunkov }
879e4ed359SPavel Begunkov 
889e4ed359SPavel Begunkov static int io_uring_cmd_timestamp(struct socket *sock,
899e4ed359SPavel Begunkov 				  struct io_uring_cmd *cmd,
909e4ed359SPavel Begunkov 				  unsigned int issue_flags)
919e4ed359SPavel Begunkov {
929e4ed359SPavel Begunkov 	struct sock *sk = sock->sk;
939e4ed359SPavel Begunkov 	struct sk_buff_head *q = &sk->sk_error_queue;
949e4ed359SPavel Begunkov 	struct sk_buff *skb, *tmp;
959e4ed359SPavel Begunkov 	struct sk_buff_head list;
969e4ed359SPavel Begunkov 	int ret;
979e4ed359SPavel Begunkov 
989e4ed359SPavel Begunkov 	if (!(issue_flags & IO_URING_F_CQE32))
999e4ed359SPavel Begunkov 		return -EINVAL;
1009e4ed359SPavel Begunkov 	ret = io_cmd_poll_multishot(cmd, issue_flags, EPOLLERR);
1019e4ed359SPavel Begunkov 	if (unlikely(ret))
1029e4ed359SPavel Begunkov 		return ret;
1039e4ed359SPavel Begunkov 
1049e4ed359SPavel Begunkov 	if (skb_queue_empty_lockless(q))
1059e4ed359SPavel Begunkov 		return -EAGAIN;
1069e4ed359SPavel Begunkov 	__skb_queue_head_init(&list);
1079e4ed359SPavel Begunkov 
1089e4ed359SPavel Begunkov 	scoped_guard(spinlock_irq, &q->lock) {
1099e4ed359SPavel Begunkov 		skb_queue_walk_safe(q, skb, tmp) {
1109e4ed359SPavel Begunkov 			/* don't support skbs with payload */
1119e4ed359SPavel Begunkov 			if (!skb_has_tx_timestamp(skb, sk) || skb->len)
1129e4ed359SPavel Begunkov 				continue;
1139e4ed359SPavel Begunkov 			__skb_unlink(skb, q);
1149e4ed359SPavel Begunkov 			__skb_queue_tail(&list, skb);
1159e4ed359SPavel Begunkov 		}
1169e4ed359SPavel Begunkov 	}
1179e4ed359SPavel Begunkov 
1189e4ed359SPavel Begunkov 	while (1) {
1199e4ed359SPavel Begunkov 		skb = skb_peek(&list);
1209e4ed359SPavel Begunkov 		if (!skb)
1219e4ed359SPavel Begunkov 			break;
1229e4ed359SPavel Begunkov 		if (!io_process_timestamp_skb(cmd, sk, skb, issue_flags))
1239e4ed359SPavel Begunkov 			break;
1249e4ed359SPavel Begunkov 		__skb_dequeue(&list);
1259e4ed359SPavel Begunkov 		consume_skb(skb);
1269e4ed359SPavel Begunkov 	}
1279e4ed359SPavel Begunkov 
1289e4ed359SPavel Begunkov 	if (!unlikely(skb_queue_empty(&list))) {
1299e4ed359SPavel Begunkov 		scoped_guard(spinlock_irqsave, &q->lock)
130*46447367SJens Axboe 			skb_queue_splice(&list, q);
1319e4ed359SPavel Begunkov 	}
1329e4ed359SPavel Begunkov 	return -EAGAIN;
1339e4ed359SPavel Begunkov }
1349e4ed359SPavel Begunkov 
13591db6edcSPavel Begunkov int io_uring_cmd_sock(struct io_uring_cmd *cmd, unsigned int issue_flags)
13691db6edcSPavel Begunkov {
13791db6edcSPavel Begunkov 	struct socket *sock = cmd->file->private_data;
13891db6edcSPavel Begunkov 	struct sock *sk = sock->sk;
13991db6edcSPavel Begunkov 	struct proto *prot = READ_ONCE(sk->sk_prot);
14091db6edcSPavel Begunkov 	int ret, arg = 0;
14191db6edcSPavel Begunkov 
14291db6edcSPavel Begunkov 	if (!prot || !prot->ioctl)
14391db6edcSPavel Begunkov 		return -EOPNOTSUPP;
14491db6edcSPavel Begunkov 
14591db6edcSPavel Begunkov 	switch (cmd->cmd_op) {
14691db6edcSPavel Begunkov 	case SOCKET_URING_OP_SIOCINQ:
14791db6edcSPavel Begunkov 		ret = prot->ioctl(sk, SIOCINQ, &arg);
14891db6edcSPavel Begunkov 		if (ret)
14991db6edcSPavel Begunkov 			return ret;
15091db6edcSPavel Begunkov 		return arg;
15191db6edcSPavel Begunkov 	case SOCKET_URING_OP_SIOCOUTQ:
15291db6edcSPavel Begunkov 		ret = prot->ioctl(sk, SIOCOUTQ, &arg);
15391db6edcSPavel Begunkov 		if (ret)
15491db6edcSPavel Begunkov 			return ret;
15591db6edcSPavel Begunkov 		return arg;
15691db6edcSPavel Begunkov 	case SOCKET_URING_OP_GETSOCKOPT:
15791db6edcSPavel Begunkov 		return io_uring_cmd_getsockopt(sock, cmd, issue_flags);
15891db6edcSPavel Begunkov 	case SOCKET_URING_OP_SETSOCKOPT:
15991db6edcSPavel Begunkov 		return io_uring_cmd_setsockopt(sock, cmd, issue_flags);
1609e4ed359SPavel Begunkov 	case SOCKET_URING_OP_TX_TIMESTAMP:
1619e4ed359SPavel Begunkov 		return io_uring_cmd_timestamp(sock, cmd, issue_flags);
16291db6edcSPavel Begunkov 	default:
16391db6edcSPavel Begunkov 		return -EOPNOTSUPP;
16491db6edcSPavel Begunkov 	}
16591db6edcSPavel Begunkov }
16691db6edcSPavel Begunkov EXPORT_SYMBOL_GPL(io_uring_cmd_sock);
167