1*ccd18ce2STim Bird // SPDX-License-Identifier: GPL-2.0 291db6edcSPavel Begunkov #include <asm/ioctls.h> 391db6edcSPavel Begunkov #include <linux/io_uring/net.h> 49e4ed359SPavel Begunkov #include <linux/errqueue.h> 591db6edcSPavel Begunkov #include <net/sock.h> 691db6edcSPavel Begunkov 791db6edcSPavel Begunkov #include "uring_cmd.h" 81e81bf14SJens Axboe #include "io_uring.h" 991db6edcSPavel Begunkov 1091db6edcSPavel Begunkov static inline int io_uring_cmd_getsockopt(struct socket *sock, 1191db6edcSPavel Begunkov struct io_uring_cmd *cmd, 1291db6edcSPavel Begunkov unsigned int issue_flags) 1391db6edcSPavel Begunkov { 1491db6edcSPavel Begunkov const struct io_uring_sqe *sqe = cmd->sqe; 1591db6edcSPavel Begunkov bool compat = !!(issue_flags & IO_URING_F_COMPAT); 1691db6edcSPavel Begunkov int optlen, optname, level, err; 1791db6edcSPavel Begunkov void __user *optval; 1891db6edcSPavel Begunkov 1991db6edcSPavel Begunkov level = READ_ONCE(sqe->level); 2091db6edcSPavel Begunkov if (level != SOL_SOCKET) 2191db6edcSPavel Begunkov return -EOPNOTSUPP; 2291db6edcSPavel Begunkov 2391db6edcSPavel Begunkov optval = u64_to_user_ptr(READ_ONCE(sqe->optval)); 2491db6edcSPavel Begunkov optname = READ_ONCE(sqe->optname); 2591db6edcSPavel Begunkov optlen = READ_ONCE(sqe->optlen); 2691db6edcSPavel Begunkov 2791db6edcSPavel Begunkov err = do_sock_getsockopt(sock, compat, level, optname, 2891db6edcSPavel Begunkov USER_SOCKPTR(optval), 2991db6edcSPavel Begunkov KERNEL_SOCKPTR(&optlen)); 3091db6edcSPavel Begunkov if (err) 3191db6edcSPavel Begunkov return err; 3291db6edcSPavel Begunkov 3391db6edcSPavel Begunkov /* On success, return optlen */ 3491db6edcSPavel Begunkov return optlen; 3591db6edcSPavel Begunkov } 3691db6edcSPavel Begunkov 3791db6edcSPavel Begunkov static inline int io_uring_cmd_setsockopt(struct socket *sock, 3891db6edcSPavel Begunkov struct io_uring_cmd *cmd, 3991db6edcSPavel Begunkov unsigned int issue_flags) 4091db6edcSPavel Begunkov { 4191db6edcSPavel Begunkov const struct io_uring_sqe *sqe = cmd->sqe; 4291db6edcSPavel Begunkov bool compat = !!(issue_flags & IO_URING_F_COMPAT); 4391db6edcSPavel Begunkov int optname, optlen, level; 4491db6edcSPavel Begunkov void __user *optval; 4591db6edcSPavel Begunkov sockptr_t optval_s; 4691db6edcSPavel Begunkov 4791db6edcSPavel Begunkov optval = u64_to_user_ptr(READ_ONCE(sqe->optval)); 4891db6edcSPavel Begunkov optname = READ_ONCE(sqe->optname); 4991db6edcSPavel Begunkov optlen = READ_ONCE(sqe->optlen); 5091db6edcSPavel Begunkov level = READ_ONCE(sqe->level); 5191db6edcSPavel Begunkov optval_s = USER_SOCKPTR(optval); 5291db6edcSPavel Begunkov 5391db6edcSPavel Begunkov return do_sock_setsockopt(sock, compat, level, optname, optval_s, 5491db6edcSPavel Begunkov optlen); 5591db6edcSPavel Begunkov } 5691db6edcSPavel Begunkov 579e4ed359SPavel Begunkov static bool io_process_timestamp_skb(struct io_uring_cmd *cmd, struct sock *sk, 589e4ed359SPavel Begunkov struct sk_buff *skb, unsigned issue_flags) 599e4ed359SPavel Begunkov { 609e4ed359SPavel Begunkov struct sock_exterr_skb *serr = SKB_EXT_ERR(skb); 619e4ed359SPavel Begunkov struct io_uring_cqe cqe[2]; 629e4ed359SPavel Begunkov struct io_timespec *iots; 639e4ed359SPavel Begunkov struct timespec64 ts; 649e4ed359SPavel Begunkov u32 tstype, tskey; 659e4ed359SPavel Begunkov int ret; 669e4ed359SPavel Begunkov 679e4ed359SPavel Begunkov BUILD_BUG_ON(sizeof(struct io_uring_cqe) != sizeof(struct io_timespec)); 689e4ed359SPavel Begunkov 699e4ed359SPavel Begunkov ret = skb_get_tx_timestamp(skb, sk, &ts); 709e4ed359SPavel Begunkov if (ret < 0) 719e4ed359SPavel Begunkov return false; 729e4ed359SPavel Begunkov 739e4ed359SPavel Begunkov tskey = serr->ee.ee_data; 749e4ed359SPavel Begunkov tstype = serr->ee.ee_info; 759e4ed359SPavel Begunkov 769e4ed359SPavel Begunkov cqe->user_data = 0; 779e4ed359SPavel Begunkov cqe->res = tskey; 781e81bf14SJens Axboe cqe->flags = IORING_CQE_F_MORE | ctx_cqe32_flags(cmd_to_io_kiocb(cmd)->ctx); 799e4ed359SPavel Begunkov cqe->flags |= tstype << IORING_TIMESTAMP_TYPE_SHIFT; 809e4ed359SPavel Begunkov if (ret == SOF_TIMESTAMPING_TX_HARDWARE) 819e4ed359SPavel Begunkov cqe->flags |= IORING_CQE_F_TSTAMP_HW; 829e4ed359SPavel Begunkov 839e4ed359SPavel Begunkov iots = (struct io_timespec *)&cqe[1]; 849e4ed359SPavel Begunkov iots->tv_sec = ts.tv_sec; 859e4ed359SPavel Begunkov iots->tv_nsec = ts.tv_nsec; 869e4ed359SPavel Begunkov return io_uring_cmd_post_mshot_cqe32(cmd, issue_flags, cqe); 879e4ed359SPavel Begunkov } 889e4ed359SPavel Begunkov 899e4ed359SPavel Begunkov static int io_uring_cmd_timestamp(struct socket *sock, 909e4ed359SPavel Begunkov struct io_uring_cmd *cmd, 919e4ed359SPavel Begunkov unsigned int issue_flags) 929e4ed359SPavel Begunkov { 939e4ed359SPavel Begunkov struct sock *sk = sock->sk; 949e4ed359SPavel Begunkov struct sk_buff_head *q = &sk->sk_error_queue; 959e4ed359SPavel Begunkov struct sk_buff *skb, *tmp; 969e4ed359SPavel Begunkov struct sk_buff_head list; 979e4ed359SPavel Begunkov int ret; 989e4ed359SPavel Begunkov 999e4ed359SPavel Begunkov if (!(issue_flags & IO_URING_F_CQE32)) 1009e4ed359SPavel Begunkov return -EINVAL; 1019e4ed359SPavel Begunkov ret = io_cmd_poll_multishot(cmd, issue_flags, EPOLLERR); 1029e4ed359SPavel Begunkov if (unlikely(ret)) 1039e4ed359SPavel Begunkov return ret; 1049e4ed359SPavel Begunkov 1059e4ed359SPavel Begunkov if (skb_queue_empty_lockless(q)) 1069e4ed359SPavel Begunkov return -EAGAIN; 1079e4ed359SPavel Begunkov __skb_queue_head_init(&list); 1089e4ed359SPavel Begunkov 1099e4ed359SPavel Begunkov scoped_guard(spinlock_irq, &q->lock) { 1109e4ed359SPavel Begunkov skb_queue_walk_safe(q, skb, tmp) { 1119e4ed359SPavel Begunkov /* don't support skbs with payload */ 1129e4ed359SPavel Begunkov if (!skb_has_tx_timestamp(skb, sk) || skb->len) 1139e4ed359SPavel Begunkov continue; 1149e4ed359SPavel Begunkov __skb_unlink(skb, q); 1159e4ed359SPavel Begunkov __skb_queue_tail(&list, skb); 1169e4ed359SPavel Begunkov } 1179e4ed359SPavel Begunkov } 1189e4ed359SPavel Begunkov 1199e4ed359SPavel Begunkov while (1) { 1209e4ed359SPavel Begunkov skb = skb_peek(&list); 1219e4ed359SPavel Begunkov if (!skb) 1229e4ed359SPavel Begunkov break; 1239e4ed359SPavel Begunkov if (!io_process_timestamp_skb(cmd, sk, skb, issue_flags)) 1249e4ed359SPavel Begunkov break; 1259e4ed359SPavel Begunkov __skb_dequeue(&list); 1269e4ed359SPavel Begunkov consume_skb(skb); 1279e4ed359SPavel Begunkov } 1289e4ed359SPavel Begunkov 1299e4ed359SPavel Begunkov if (!unlikely(skb_queue_empty(&list))) { 1309e4ed359SPavel Begunkov scoped_guard(spinlock_irqsave, &q->lock) 13146447367SJens Axboe skb_queue_splice(&list, q); 1329e4ed359SPavel Begunkov } 1339e4ed359SPavel Begunkov return -EAGAIN; 1349e4ed359SPavel Begunkov } 1359e4ed359SPavel Begunkov 1365d24321eSGabriel Krisman Bertazi static int io_uring_cmd_getsockname(struct socket *sock, 1375d24321eSGabriel Krisman Bertazi struct io_uring_cmd *cmd, 1385d24321eSGabriel Krisman Bertazi unsigned int issue_flags) 1395d24321eSGabriel Krisman Bertazi { 1405d24321eSGabriel Krisman Bertazi const struct io_uring_sqe *sqe = cmd->sqe; 1415d24321eSGabriel Krisman Bertazi struct sockaddr __user *uaddr; 1425d24321eSGabriel Krisman Bertazi unsigned int peer; 1435d24321eSGabriel Krisman Bertazi int __user *ulen; 1445d24321eSGabriel Krisman Bertazi 1455d24321eSGabriel Krisman Bertazi if (sqe->ioprio || sqe->__pad1 || sqe->len || sqe->rw_flags) 1465d24321eSGabriel Krisman Bertazi return -EINVAL; 1475d24321eSGabriel Krisman Bertazi 1485d24321eSGabriel Krisman Bertazi uaddr = u64_to_user_ptr(READ_ONCE(sqe->addr)); 1495d24321eSGabriel Krisman Bertazi ulen = u64_to_user_ptr(sqe->addr3); 1505d24321eSGabriel Krisman Bertazi peer = READ_ONCE(sqe->optlen); 1515d24321eSGabriel Krisman Bertazi if (peer > 1) 1525d24321eSGabriel Krisman Bertazi return -EINVAL; 1535d24321eSGabriel Krisman Bertazi return do_getsockname(sock, peer, uaddr, ulen); 1545d24321eSGabriel Krisman Bertazi } 1555d24321eSGabriel Krisman Bertazi 15691db6edcSPavel Begunkov int io_uring_cmd_sock(struct io_uring_cmd *cmd, unsigned int issue_flags) 15791db6edcSPavel Begunkov { 15891db6edcSPavel Begunkov struct socket *sock = cmd->file->private_data; 15991db6edcSPavel Begunkov struct sock *sk = sock->sk; 16091db6edcSPavel Begunkov struct proto *prot = READ_ONCE(sk->sk_prot); 16191db6edcSPavel Begunkov int ret, arg = 0; 16291db6edcSPavel Begunkov 16391db6edcSPavel Begunkov if (!prot || !prot->ioctl) 16491db6edcSPavel Begunkov return -EOPNOTSUPP; 16591db6edcSPavel Begunkov 16691db6edcSPavel Begunkov switch (cmd->cmd_op) { 16791db6edcSPavel Begunkov case SOCKET_URING_OP_SIOCINQ: 16891db6edcSPavel Begunkov ret = prot->ioctl(sk, SIOCINQ, &arg); 16991db6edcSPavel Begunkov if (ret) 17091db6edcSPavel Begunkov return ret; 17191db6edcSPavel Begunkov return arg; 17291db6edcSPavel Begunkov case SOCKET_URING_OP_SIOCOUTQ: 17391db6edcSPavel Begunkov ret = prot->ioctl(sk, SIOCOUTQ, &arg); 17491db6edcSPavel Begunkov if (ret) 17591db6edcSPavel Begunkov return ret; 17691db6edcSPavel Begunkov return arg; 17791db6edcSPavel Begunkov case SOCKET_URING_OP_GETSOCKOPT: 17891db6edcSPavel Begunkov return io_uring_cmd_getsockopt(sock, cmd, issue_flags); 17991db6edcSPavel Begunkov case SOCKET_URING_OP_SETSOCKOPT: 18091db6edcSPavel Begunkov return io_uring_cmd_setsockopt(sock, cmd, issue_flags); 1819e4ed359SPavel Begunkov case SOCKET_URING_OP_TX_TIMESTAMP: 1829e4ed359SPavel Begunkov return io_uring_cmd_timestamp(sock, cmd, issue_flags); 1835d24321eSGabriel Krisman Bertazi case SOCKET_URING_OP_GETSOCKNAME: 1845d24321eSGabriel Krisman Bertazi return io_uring_cmd_getsockname(sock, cmd, issue_flags); 18591db6edcSPavel Begunkov default: 18691db6edcSPavel Begunkov return -EOPNOTSUPP; 18791db6edcSPavel Begunkov } 18891db6edcSPavel Begunkov } 18991db6edcSPavel Begunkov EXPORT_SYMBOL_GPL(io_uring_cmd_sock); 190