1 #include <asm/ioctls.h>
2 #include <linux/io_uring/net.h>
3 #include <linux/errqueue.h>
4 #include <net/sock.h>
5 
6 #include "uring_cmd.h"
7 
io_uring_cmd_getsockopt(struct socket * sock,struct io_uring_cmd * cmd,unsigned int issue_flags)8 static inline int io_uring_cmd_getsockopt(struct socket *sock,
9 					  struct io_uring_cmd *cmd,
10 					  unsigned int issue_flags)
11 {
12 	const struct io_uring_sqe *sqe = cmd->sqe;
13 	bool compat = !!(issue_flags & IO_URING_F_COMPAT);
14 	int optlen, optname, level, err;
15 	void __user *optval;
16 
17 	level = READ_ONCE(sqe->level);
18 	if (level != SOL_SOCKET)
19 		return -EOPNOTSUPP;
20 
21 	optval = u64_to_user_ptr(READ_ONCE(sqe->optval));
22 	optname = READ_ONCE(sqe->optname);
23 	optlen = READ_ONCE(sqe->optlen);
24 
25 	err = do_sock_getsockopt(sock, compat, level, optname,
26 				 USER_SOCKPTR(optval),
27 				 KERNEL_SOCKPTR(&optlen));
28 	if (err)
29 		return err;
30 
31 	/* On success, return optlen */
32 	return optlen;
33 }
34 
io_uring_cmd_setsockopt(struct socket * sock,struct io_uring_cmd * cmd,unsigned int issue_flags)35 static inline int io_uring_cmd_setsockopt(struct socket *sock,
36 					  struct io_uring_cmd *cmd,
37 					  unsigned int issue_flags)
38 {
39 	const struct io_uring_sqe *sqe = cmd->sqe;
40 	bool compat = !!(issue_flags & IO_URING_F_COMPAT);
41 	int optname, optlen, level;
42 	void __user *optval;
43 	sockptr_t optval_s;
44 
45 	optval = u64_to_user_ptr(READ_ONCE(sqe->optval));
46 	optname = READ_ONCE(sqe->optname);
47 	optlen = READ_ONCE(sqe->optlen);
48 	level = READ_ONCE(sqe->level);
49 	optval_s = USER_SOCKPTR(optval);
50 
51 	return do_sock_setsockopt(sock, compat, level, optname, optval_s,
52 				  optlen);
53 }
54 
io_process_timestamp_skb(struct io_uring_cmd * cmd,struct sock * sk,struct sk_buff * skb,unsigned issue_flags)55 static bool io_process_timestamp_skb(struct io_uring_cmd *cmd, struct sock *sk,
56 				     struct sk_buff *skb, unsigned issue_flags)
57 {
58 	struct sock_exterr_skb *serr = SKB_EXT_ERR(skb);
59 	struct io_uring_cqe cqe[2];
60 	struct io_timespec *iots;
61 	struct timespec64 ts;
62 	u32 tstype, tskey;
63 	int ret;
64 
65 	BUILD_BUG_ON(sizeof(struct io_uring_cqe) != sizeof(struct io_timespec));
66 
67 	ret = skb_get_tx_timestamp(skb, sk, &ts);
68 	if (ret < 0)
69 		return false;
70 
71 	tskey = serr->ee.ee_data;
72 	tstype = serr->ee.ee_info;
73 
74 	cqe->user_data = 0;
75 	cqe->res = tskey;
76 	cqe->flags = IORING_CQE_F_MORE;
77 	cqe->flags |= tstype << IORING_TIMESTAMP_TYPE_SHIFT;
78 	if (ret == SOF_TIMESTAMPING_TX_HARDWARE)
79 		cqe->flags |= IORING_CQE_F_TSTAMP_HW;
80 
81 	iots = (struct io_timespec *)&cqe[1];
82 	iots->tv_sec = ts.tv_sec;
83 	iots->tv_nsec = ts.tv_nsec;
84 	return io_uring_cmd_post_mshot_cqe32(cmd, issue_flags, cqe);
85 }
86 
io_uring_cmd_timestamp(struct socket * sock,struct io_uring_cmd * cmd,unsigned int issue_flags)87 static int io_uring_cmd_timestamp(struct socket *sock,
88 				  struct io_uring_cmd *cmd,
89 				  unsigned int issue_flags)
90 {
91 	struct sock *sk = sock->sk;
92 	struct sk_buff_head *q = &sk->sk_error_queue;
93 	struct sk_buff *skb, *tmp;
94 	struct sk_buff_head list;
95 	int ret;
96 
97 	if (!(issue_flags & IO_URING_F_CQE32))
98 		return -EINVAL;
99 	ret = io_cmd_poll_multishot(cmd, issue_flags, EPOLLERR);
100 	if (unlikely(ret))
101 		return ret;
102 
103 	if (skb_queue_empty_lockless(q))
104 		return -EAGAIN;
105 	__skb_queue_head_init(&list);
106 
107 	scoped_guard(spinlock_irq, &q->lock) {
108 		skb_queue_walk_safe(q, skb, tmp) {
109 			/* don't support skbs with payload */
110 			if (!skb_has_tx_timestamp(skb, sk) || skb->len)
111 				continue;
112 			__skb_unlink(skb, q);
113 			__skb_queue_tail(&list, skb);
114 		}
115 	}
116 
117 	while (1) {
118 		skb = skb_peek(&list);
119 		if (!skb)
120 			break;
121 		if (!io_process_timestamp_skb(cmd, sk, skb, issue_flags))
122 			break;
123 		__skb_dequeue(&list);
124 		consume_skb(skb);
125 	}
126 
127 	if (!unlikely(skb_queue_empty(&list))) {
128 		scoped_guard(spinlock_irqsave, &q->lock)
129 			skb_queue_splice(q, &list);
130 	}
131 	return -EAGAIN;
132 }
133 
io_uring_cmd_sock(struct io_uring_cmd * cmd,unsigned int issue_flags)134 int io_uring_cmd_sock(struct io_uring_cmd *cmd, unsigned int issue_flags)
135 {
136 	struct socket *sock = cmd->file->private_data;
137 	struct sock *sk = sock->sk;
138 	struct proto *prot = READ_ONCE(sk->sk_prot);
139 	int ret, arg = 0;
140 
141 	if (!prot || !prot->ioctl)
142 		return -EOPNOTSUPP;
143 
144 	switch (cmd->cmd_op) {
145 	case SOCKET_URING_OP_SIOCINQ:
146 		ret = prot->ioctl(sk, SIOCINQ, &arg);
147 		if (ret)
148 			return ret;
149 		return arg;
150 	case SOCKET_URING_OP_SIOCOUTQ:
151 		ret = prot->ioctl(sk, SIOCOUTQ, &arg);
152 		if (ret)
153 			return ret;
154 		return arg;
155 	case SOCKET_URING_OP_GETSOCKOPT:
156 		return io_uring_cmd_getsockopt(sock, cmd, issue_flags);
157 	case SOCKET_URING_OP_SETSOCKOPT:
158 		return io_uring_cmd_setsockopt(sock, cmd, issue_flags);
159 	case SOCKET_URING_OP_TX_TIMESTAMP:
160 		return io_uring_cmd_timestamp(sock, cmd, issue_flags);
161 	default:
162 		return -EOPNOTSUPP;
163 	}
164 }
165 EXPORT_SYMBOL_GPL(io_uring_cmd_sock);
166