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