166 lines
4.1 KiB
C
166 lines
4.1 KiB
C
#include <asm/ioctls.h>
|
|
#include <linux/io_uring/net.h>
|
|
#include <linux/errqueue.h>
|
|
#include <net/sock.h>
|
|
|
|
#include "uring_cmd.h"
|
|
|
|
static inline int io_uring_cmd_getsockopt(struct socket *sock,
|
|
struct io_uring_cmd *cmd,
|
|
unsigned int issue_flags)
|
|
{
|
|
const struct io_uring_sqe *sqe = cmd->sqe;
|
|
bool compat = !!(issue_flags & IO_URING_F_COMPAT);
|
|
int optlen, optname, level, err;
|
|
void __user *optval;
|
|
|
|
level = READ_ONCE(sqe->level);
|
|
if (level != SOL_SOCKET)
|
|
return -EOPNOTSUPP;
|
|
|
|
optval = u64_to_user_ptr(READ_ONCE(sqe->optval));
|
|
optname = READ_ONCE(sqe->optname);
|
|
optlen = READ_ONCE(sqe->optlen);
|
|
|
|
err = do_sock_getsockopt(sock, compat, level, optname,
|
|
USER_SOCKPTR(optval),
|
|
KERNEL_SOCKPTR(&optlen));
|
|
if (err)
|
|
return err;
|
|
|
|
/* On success, return optlen */
|
|
return optlen;
|
|
}
|
|
|
|
static inline int io_uring_cmd_setsockopt(struct socket *sock,
|
|
struct io_uring_cmd *cmd,
|
|
unsigned int issue_flags)
|
|
{
|
|
const struct io_uring_sqe *sqe = cmd->sqe;
|
|
bool compat = !!(issue_flags & IO_URING_F_COMPAT);
|
|
int optname, optlen, level;
|
|
void __user *optval;
|
|
sockptr_t optval_s;
|
|
|
|
optval = u64_to_user_ptr(READ_ONCE(sqe->optval));
|
|
optname = READ_ONCE(sqe->optname);
|
|
optlen = READ_ONCE(sqe->optlen);
|
|
level = READ_ONCE(sqe->level);
|
|
optval_s = USER_SOCKPTR(optval);
|
|
|
|
return do_sock_setsockopt(sock, compat, level, optname, optval_s,
|
|
optlen);
|
|
}
|
|
|
|
static bool io_process_timestamp_skb(struct io_uring_cmd *cmd, struct sock *sk,
|
|
struct sk_buff *skb, unsigned issue_flags)
|
|
{
|
|
struct sock_exterr_skb *serr = SKB_EXT_ERR(skb);
|
|
struct io_uring_cqe cqe[2];
|
|
struct io_timespec *iots;
|
|
struct timespec64 ts;
|
|
u32 tstype, tskey;
|
|
int ret;
|
|
|
|
BUILD_BUG_ON(sizeof(struct io_uring_cqe) != sizeof(struct io_timespec));
|
|
|
|
ret = skb_get_tx_timestamp(skb, sk, &ts);
|
|
if (ret < 0)
|
|
return false;
|
|
|
|
tskey = serr->ee.ee_data;
|
|
tstype = serr->ee.ee_info;
|
|
|
|
cqe->user_data = 0;
|
|
cqe->res = tskey;
|
|
cqe->flags = IORING_CQE_F_MORE;
|
|
cqe->flags |= tstype << IORING_TIMESTAMP_TYPE_SHIFT;
|
|
if (ret == SOF_TIMESTAMPING_TX_HARDWARE)
|
|
cqe->flags |= IORING_CQE_F_TSTAMP_HW;
|
|
|
|
iots = (struct io_timespec *)&cqe[1];
|
|
iots->tv_sec = ts.tv_sec;
|
|
iots->tv_nsec = ts.tv_nsec;
|
|
return io_uring_cmd_post_mshot_cqe32(cmd, issue_flags, cqe);
|
|
}
|
|
|
|
static int io_uring_cmd_timestamp(struct socket *sock,
|
|
struct io_uring_cmd *cmd,
|
|
unsigned int issue_flags)
|
|
{
|
|
struct sock *sk = sock->sk;
|
|
struct sk_buff_head *q = &sk->sk_error_queue;
|
|
struct sk_buff *skb, *tmp;
|
|
struct sk_buff_head list;
|
|
int ret;
|
|
|
|
if (!(issue_flags & IO_URING_F_CQE32))
|
|
return -EINVAL;
|
|
ret = io_cmd_poll_multishot(cmd, issue_flags, EPOLLERR);
|
|
if (unlikely(ret))
|
|
return ret;
|
|
|
|
if (skb_queue_empty_lockless(q))
|
|
return -EAGAIN;
|
|
__skb_queue_head_init(&list);
|
|
|
|
scoped_guard(spinlock_irq, &q->lock) {
|
|
skb_queue_walk_safe(q, skb, tmp) {
|
|
/* don't support skbs with payload */
|
|
if (!skb_has_tx_timestamp(skb, sk) || skb->len)
|
|
continue;
|
|
__skb_unlink(skb, q);
|
|
__skb_queue_tail(&list, skb);
|
|
}
|
|
}
|
|
|
|
while (1) {
|
|
skb = skb_peek(&list);
|
|
if (!skb)
|
|
break;
|
|
if (!io_process_timestamp_skb(cmd, sk, skb, issue_flags))
|
|
break;
|
|
__skb_dequeue(&list);
|
|
consume_skb(skb);
|
|
}
|
|
|
|
if (!unlikely(skb_queue_empty(&list))) {
|
|
scoped_guard(spinlock_irqsave, &q->lock)
|
|
skb_queue_splice(q, &list);
|
|
}
|
|
return -EAGAIN;
|
|
}
|
|
|
|
int io_uring_cmd_sock(struct io_uring_cmd *cmd, unsigned int issue_flags)
|
|
{
|
|
struct socket *sock = cmd->file->private_data;
|
|
struct sock *sk = sock->sk;
|
|
struct proto *prot = READ_ONCE(sk->sk_prot);
|
|
int ret, arg = 0;
|
|
|
|
if (!prot || !prot->ioctl)
|
|
return -EOPNOTSUPP;
|
|
|
|
switch (cmd->cmd_op) {
|
|
case SOCKET_URING_OP_SIOCINQ:
|
|
ret = prot->ioctl(sk, SIOCINQ, &arg);
|
|
if (ret)
|
|
return ret;
|
|
return arg;
|
|
case SOCKET_URING_OP_SIOCOUTQ:
|
|
ret = prot->ioctl(sk, SIOCOUTQ, &arg);
|
|
if (ret)
|
|
return ret;
|
|
return arg;
|
|
case SOCKET_URING_OP_GETSOCKOPT:
|
|
return io_uring_cmd_getsockopt(sock, cmd, issue_flags);
|
|
case SOCKET_URING_OP_SETSOCKOPT:
|
|
return io_uring_cmd_setsockopt(sock, cmd, issue_flags);
|
|
case SOCKET_URING_OP_TX_TIMESTAMP:
|
|
return io_uring_cmd_timestamp(sock, cmd, issue_flags);
|
|
default:
|
|
return -EOPNOTSUPP;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(io_uring_cmd_sock);
|