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
9static 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 optval: USER_SOCKPTR(p: optval),
28 optlen: KERNEL_SOCKPTR(p: &optlen));
29 if (err)
30 return err;
31
32 /* On success, return optlen */
33 return optlen;
34}
35
36static 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(p: optval);
51
52 return do_sock_setsockopt(sock, compat, level, optname, optval: optval_s,
53 optlen);
54}
55
56static 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: &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(ctx: cmd_to_io_kiocb(ptr: 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
88static 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(list: q))
105 return -EAGAIN;
106 __skb_queue_head_init(list: &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, list: q);
114 __skb_queue_tail(list: &list, newsk: skb);
115 }
116 }
117
118 while (1) {
119 skb = skb_peek(list_: &list);
120 if (!skb)
121 break;
122 if (!io_process_timestamp_skb(cmd, sk, skb, issue_flags))
123 break;
124 __skb_dequeue(list: &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(list: q, head: &list);
131 }
132 return -EAGAIN;
133}
134
135int 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}
166EXPORT_SYMBOL_GPL(io_uring_cmd_sock);
167