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