191db6edcSPavel Begunkov #include <asm/ioctls.h> 291db6edcSPavel Begunkov #include <linux/io_uring/net.h> 39e4ed359SPavel Begunkov #include <linux/errqueue.h> 491db6edcSPavel Begunkov #include <net/sock.h> 591db6edcSPavel Begunkov 691db6edcSPavel Begunkov #include "uring_cmd.h" 71e81bf14SJens Axboe #include "io_uring.h" 891db6edcSPavel Begunkov 991db6edcSPavel Begunkov static inline int io_uring_cmd_getsockopt(struct socket *sock, 1091db6edcSPavel Begunkov struct io_uring_cmd *cmd, 1191db6edcSPavel Begunkov unsigned int issue_flags) 1291db6edcSPavel Begunkov { 1391db6edcSPavel Begunkov const struct io_uring_sqe *sqe = cmd->sqe; 1491db6edcSPavel Begunkov bool compat = !!(issue_flags & IO_URING_F_COMPAT); 1591db6edcSPavel Begunkov int optlen, optname, level, err; 1691db6edcSPavel Begunkov void __user *optval; 1791db6edcSPavel Begunkov 1891db6edcSPavel Begunkov level = READ_ONCE(sqe->level); 1991db6edcSPavel Begunkov if (level != SOL_SOCKET) 2091db6edcSPavel Begunkov return -EOPNOTSUPP; 2191db6edcSPavel Begunkov 2291db6edcSPavel Begunkov optval = u64_to_user_ptr(READ_ONCE(sqe->optval)); 2391db6edcSPavel Begunkov optname = READ_ONCE(sqe->optname); 2491db6edcSPavel Begunkov optlen = READ_ONCE(sqe->optlen); 2591db6edcSPavel Begunkov 2691db6edcSPavel Begunkov err = do_sock_getsockopt(sock, compat, level, optname, 2791db6edcSPavel Begunkov USER_SOCKPTR(optval), 2891db6edcSPavel Begunkov KERNEL_SOCKPTR(&optlen)); 2991db6edcSPavel Begunkov if (err) 3091db6edcSPavel Begunkov return err; 3191db6edcSPavel Begunkov 3291db6edcSPavel Begunkov /* On success, return optlen */ 3391db6edcSPavel Begunkov return optlen; 3491db6edcSPavel Begunkov } 3591db6edcSPavel Begunkov 3691db6edcSPavel Begunkov static inline int io_uring_cmd_setsockopt(struct socket *sock, 3791db6edcSPavel Begunkov struct io_uring_cmd *cmd, 3891db6edcSPavel Begunkov unsigned int issue_flags) 3991db6edcSPavel Begunkov { 4091db6edcSPavel Begunkov const struct io_uring_sqe *sqe = cmd->sqe; 4191db6edcSPavel Begunkov bool compat = !!(issue_flags & IO_URING_F_COMPAT); 4291db6edcSPavel Begunkov int optname, optlen, level; 4391db6edcSPavel Begunkov void __user *optval; 4491db6edcSPavel Begunkov sockptr_t optval_s; 4591db6edcSPavel Begunkov 4691db6edcSPavel Begunkov optval = u64_to_user_ptr(READ_ONCE(sqe->optval)); 4791db6edcSPavel Begunkov optname = READ_ONCE(sqe->optname); 4891db6edcSPavel Begunkov optlen = READ_ONCE(sqe->optlen); 4991db6edcSPavel Begunkov level = READ_ONCE(sqe->level); 5091db6edcSPavel Begunkov optval_s = USER_SOCKPTR(optval); 5191db6edcSPavel Begunkov 5291db6edcSPavel Begunkov return do_sock_setsockopt(sock, compat, level, optname, optval_s, 5391db6edcSPavel Begunkov optlen); 5491db6edcSPavel Begunkov } 5591db6edcSPavel Begunkov 569e4ed359SPavel Begunkov static bool io_process_timestamp_skb(struct io_uring_cmd *cmd, struct sock *sk, 579e4ed359SPavel Begunkov struct sk_buff *skb, unsigned issue_flags) 589e4ed359SPavel Begunkov { 599e4ed359SPavel Begunkov struct sock_exterr_skb *serr = SKB_EXT_ERR(skb); 609e4ed359SPavel Begunkov struct io_uring_cqe cqe[2]; 619e4ed359SPavel Begunkov struct io_timespec *iots; 629e4ed359SPavel Begunkov struct timespec64 ts; 639e4ed359SPavel Begunkov u32 tstype, tskey; 649e4ed359SPavel Begunkov int ret; 659e4ed359SPavel Begunkov 669e4ed359SPavel Begunkov BUILD_BUG_ON(sizeof(struct io_uring_cqe) != sizeof(struct io_timespec)); 679e4ed359SPavel Begunkov 689e4ed359SPavel Begunkov ret = skb_get_tx_timestamp(skb, sk, &ts); 699e4ed359SPavel Begunkov if (ret < 0) 709e4ed359SPavel Begunkov return false; 719e4ed359SPavel Begunkov 729e4ed359SPavel Begunkov tskey = serr->ee.ee_data; 739e4ed359SPavel Begunkov tstype = serr->ee.ee_info; 749e4ed359SPavel Begunkov 759e4ed359SPavel Begunkov cqe->user_data = 0; 769e4ed359SPavel Begunkov cqe->res = tskey; 771e81bf14SJens Axboe cqe->flags = IORING_CQE_F_MORE | ctx_cqe32_flags(cmd_to_io_kiocb(cmd)->ctx); 789e4ed359SPavel Begunkov cqe->flags |= tstype << IORING_TIMESTAMP_TYPE_SHIFT; 799e4ed359SPavel Begunkov if (ret == SOF_TIMESTAMPING_TX_HARDWARE) 809e4ed359SPavel Begunkov cqe->flags |= IORING_CQE_F_TSTAMP_HW; 819e4ed359SPavel Begunkov 829e4ed359SPavel Begunkov iots = (struct io_timespec *)&cqe[1]; 839e4ed359SPavel Begunkov iots->tv_sec = ts.tv_sec; 849e4ed359SPavel Begunkov iots->tv_nsec = ts.tv_nsec; 859e4ed359SPavel Begunkov return io_uring_cmd_post_mshot_cqe32(cmd, issue_flags, cqe); 869e4ed359SPavel Begunkov } 879e4ed359SPavel Begunkov 889e4ed359SPavel Begunkov static int io_uring_cmd_timestamp(struct socket *sock, 899e4ed359SPavel Begunkov struct io_uring_cmd *cmd, 909e4ed359SPavel Begunkov unsigned int issue_flags) 919e4ed359SPavel Begunkov { 929e4ed359SPavel Begunkov struct sock *sk = sock->sk; 939e4ed359SPavel Begunkov struct sk_buff_head *q = &sk->sk_error_queue; 949e4ed359SPavel Begunkov struct sk_buff *skb, *tmp; 959e4ed359SPavel Begunkov struct sk_buff_head list; 969e4ed359SPavel Begunkov int ret; 979e4ed359SPavel Begunkov 989e4ed359SPavel Begunkov if (!(issue_flags & IO_URING_F_CQE32)) 999e4ed359SPavel Begunkov return -EINVAL; 1009e4ed359SPavel Begunkov ret = io_cmd_poll_multishot(cmd, issue_flags, EPOLLERR); 1019e4ed359SPavel Begunkov if (unlikely(ret)) 1029e4ed359SPavel Begunkov return ret; 1039e4ed359SPavel Begunkov 1049e4ed359SPavel Begunkov if (skb_queue_empty_lockless(q)) 1059e4ed359SPavel Begunkov return -EAGAIN; 1069e4ed359SPavel Begunkov __skb_queue_head_init(&list); 1079e4ed359SPavel Begunkov 1089e4ed359SPavel Begunkov scoped_guard(spinlock_irq, &q->lock) { 1099e4ed359SPavel Begunkov skb_queue_walk_safe(q, skb, tmp) { 1109e4ed359SPavel Begunkov /* don't support skbs with payload */ 1119e4ed359SPavel Begunkov if (!skb_has_tx_timestamp(skb, sk) || skb->len) 1129e4ed359SPavel Begunkov continue; 1139e4ed359SPavel Begunkov __skb_unlink(skb, q); 1149e4ed359SPavel Begunkov __skb_queue_tail(&list, skb); 1159e4ed359SPavel Begunkov } 1169e4ed359SPavel Begunkov } 1179e4ed359SPavel Begunkov 1189e4ed359SPavel Begunkov while (1) { 1199e4ed359SPavel Begunkov skb = skb_peek(&list); 1209e4ed359SPavel Begunkov if (!skb) 1219e4ed359SPavel Begunkov break; 1229e4ed359SPavel Begunkov if (!io_process_timestamp_skb(cmd, sk, skb, issue_flags)) 1239e4ed359SPavel Begunkov break; 1249e4ed359SPavel Begunkov __skb_dequeue(&list); 1259e4ed359SPavel Begunkov consume_skb(skb); 1269e4ed359SPavel Begunkov } 1279e4ed359SPavel Begunkov 1289e4ed359SPavel Begunkov if (!unlikely(skb_queue_empty(&list))) { 1299e4ed359SPavel Begunkov scoped_guard(spinlock_irqsave, &q->lock) 130*46447367SJens Axboe skb_queue_splice(&list, q); 1319e4ed359SPavel Begunkov } 1329e4ed359SPavel Begunkov return -EAGAIN; 1339e4ed359SPavel Begunkov } 1349e4ed359SPavel Begunkov 13591db6edcSPavel Begunkov int io_uring_cmd_sock(struct io_uring_cmd *cmd, unsigned int issue_flags) 13691db6edcSPavel Begunkov { 13791db6edcSPavel Begunkov struct socket *sock = cmd->file->private_data; 13891db6edcSPavel Begunkov struct sock *sk = sock->sk; 13991db6edcSPavel Begunkov struct proto *prot = READ_ONCE(sk->sk_prot); 14091db6edcSPavel Begunkov int ret, arg = 0; 14191db6edcSPavel Begunkov 14291db6edcSPavel Begunkov if (!prot || !prot->ioctl) 14391db6edcSPavel Begunkov return -EOPNOTSUPP; 14491db6edcSPavel Begunkov 14591db6edcSPavel Begunkov switch (cmd->cmd_op) { 14691db6edcSPavel Begunkov case SOCKET_URING_OP_SIOCINQ: 14791db6edcSPavel Begunkov ret = prot->ioctl(sk, SIOCINQ, &arg); 14891db6edcSPavel Begunkov if (ret) 14991db6edcSPavel Begunkov return ret; 15091db6edcSPavel Begunkov return arg; 15191db6edcSPavel Begunkov case SOCKET_URING_OP_SIOCOUTQ: 15291db6edcSPavel Begunkov ret = prot->ioctl(sk, SIOCOUTQ, &arg); 15391db6edcSPavel Begunkov if (ret) 15491db6edcSPavel Begunkov return ret; 15591db6edcSPavel Begunkov return arg; 15691db6edcSPavel Begunkov case SOCKET_URING_OP_GETSOCKOPT: 15791db6edcSPavel Begunkov return io_uring_cmd_getsockopt(sock, cmd, issue_flags); 15891db6edcSPavel Begunkov case SOCKET_URING_OP_SETSOCKOPT: 15991db6edcSPavel Begunkov return io_uring_cmd_setsockopt(sock, cmd, issue_flags); 1609e4ed359SPavel Begunkov case SOCKET_URING_OP_TX_TIMESTAMP: 1619e4ed359SPavel Begunkov return io_uring_cmd_timestamp(sock, cmd, issue_flags); 16291db6edcSPavel Begunkov default: 16391db6edcSPavel Begunkov return -EOPNOTSUPP; 16491db6edcSPavel Begunkov } 16591db6edcSPavel Begunkov } 16691db6edcSPavel Begunkov EXPORT_SYMBOL_GPL(io_uring_cmd_sock); 167