File Index
Files and directories
Sign in
FILE

cmd_net.c

File preview with syntax highlighting.

linux-6.19.14/io_uring/cmd_net.c C
  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				 USER_SOCKPTR(optval),
 28				 KERNEL_SOCKPTR(&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(optval);
 51
 52	return do_sock_setsockopt(sock, compat, level, optname, 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);
 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
 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(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(&list, q);
131	}
132	return -EAGAIN;
133}
134
135static int io_uring_cmd_getsockname(struct socket *sock,
136				    struct io_uring_cmd *cmd,
137				    unsigned int issue_flags)
138{
139	const struct io_uring_sqe *sqe = cmd->sqe;
140	struct sockaddr __user *uaddr;
141	unsigned int peer;
142	int __user *ulen;
143
144	if (sqe->ioprio || sqe->__pad1 || sqe->len || sqe->rw_flags)
145		return -EINVAL;
146
147	uaddr = u64_to_user_ptr(READ_ONCE(sqe->addr));
148	ulen = u64_to_user_ptr(READ_ONCE(sqe->addr3));
149	peer = READ_ONCE(sqe->optlen);
150	if (peer > 1)
151		return -EINVAL;
152	return do_getsockname(sock, peer, uaddr, ulen);
153}
154
155int io_uring_cmd_sock(struct io_uring_cmd *cmd, unsigned int issue_flags)
156{
157	struct socket *sock = cmd->file->private_data;
158	struct sock *sk = sock->sk;
159	struct proto *prot = READ_ONCE(sk->sk_prot);
160	int ret, arg = 0;
161
162	switch (cmd->cmd_op) {
163	case SOCKET_URING_OP_SIOCINQ:
164		if (!prot || !prot->ioctl)
165			return -EOPNOTSUPP;
166
167		ret = prot->ioctl(sk, SIOCINQ, &arg);
168		if (ret)
169			return ret;
170		return arg;
171	case SOCKET_URING_OP_SIOCOUTQ:
172		if (!prot || !prot->ioctl)
173			return -EOPNOTSUPP;
174
175		ret = prot->ioctl(sk, SIOCOUTQ, &arg);
176		if (ret)
177			return ret;
178		return arg;
179	case SOCKET_URING_OP_GETSOCKOPT:
180		return io_uring_cmd_getsockopt(sock, cmd, issue_flags);
181	case SOCKET_URING_OP_SETSOCKOPT:
182		return io_uring_cmd_setsockopt(sock, cmd, issue_flags);
183	case SOCKET_URING_OP_TX_TIMESTAMP:
184		return io_uring_cmd_timestamp(sock, cmd, issue_flags);
185	case SOCKET_URING_OP_GETSOCKNAME:
186		return io_uring_cmd_getsockname(sock, cmd, issue_flags);
187	default:
188		return -EOPNOTSUPP;
189	}
190}
191EXPORT_SYMBOL_GPL(io_uring_cmd_sock);
~/ Read-only file preview.