Files
linux/io_uring/cmd_net.c
Linus Torvalds 0abcfd8983 Merge tag 'for-6.19/io_uring-20251201' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux
Pull io_uring updates from Jens Axboe:

 - Unify how task_work cancelations are detected, placing it in the
   task_work running state rather than needing to check the task state

 - Series cleaning up and moving the cancelation code to where it
   belongs, in cancel.c

 - Cleanup of waitid and futex argument handling

 - Add support for mixed sized SQEs. 6.18 added support for mixed sized
   CQEs, improving flexibility and efficiency of workloads that need big
   CQEs. This adds similar support for SQEs, where the occasional need
   for a 128b SQE doesn't necessitate having all SQEs be 128b in size

 - Introduce zcrx and SQ/CQ layout queries. The former returns what zcrx
   features are available. And both return the ring size information to
   help with allocation size calculation for user provided rings like
   IORING_SETUP_NO_MMAP and IORING_MEM_REGION_TYPE_USER

 - Zcrx updates for 6.19. It includes a bunch of small patches,
   IORING_REGISTER_ZCRX_CTRL and RQ flushing and David's work on sharing
   zcrx b/w multiple io_uring instances

 - Series cleaning up ring initializations, notable deduplicating ring
   size and offset calculations. It also moves most of the checking
   before doing any allocations, making the code simpler

 - Add support for getsockname and getpeername, which is mostly a
   trivial hookup after a bit of refactoring on the networking side

 - Various fixes and cleanups

* tag 'for-6.19/io_uring-20251201' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux: (68 commits)
  io_uring: Introduce getsockname io_uring cmd
  socket: Split out a getsockname helper for io_uring
  socket: Unify getsockname and getpeername implementation
  io_uring/query: drop unused io_handle_query_entry() ctx arg
  io_uring/kbuf: remove obsolete buf_nr_pages and update comments
  io_uring/register: use correct location for io_rings_layout
  io_uring/zcrx: share an ifq between rings
  io_uring/zcrx: add io_fill_zcrx_offsets()
  io_uring/zcrx: export zcrx via a file
  io_uring/zcrx: move io_zcrx_scrub() and dependencies up
  io_uring/zcrx: count zcrx users
  io_uring/zcrx: add sync refill queue flushing
  io_uring/zcrx: introduce IORING_REGISTER_ZCRX_CTRL
  io_uring/zcrx: elide passing msg flags
  io_uring/zcrx: use folio_nr_pages() instead of shift operation
  io_uring/zcrx: convert to use netmem_desc
  io_uring/query: introduce rings info query
  io_uring/query: introduce zcrx query
  io_uring: move cq/sq user offset init around
  io_uring: pre-calculate scq layout
  ...
2025-12-03 18:58:57 -08:00

189 lines
4.8 KiB
C

#include <asm/ioctls.h>
#include <linux/io_uring/net.h>
#include <linux/errqueue.h>
#include <net/sock.h>
#include "uring_cmd.h"
#include "io_uring.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 | ctx_cqe32_flags(cmd_to_io_kiocb(cmd)->ctx);
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(&list, q);
}
return -EAGAIN;
}
static int io_uring_cmd_getsockname(struct socket *sock,
struct io_uring_cmd *cmd,
unsigned int issue_flags)
{
const struct io_uring_sqe *sqe = cmd->sqe;
struct sockaddr __user *uaddr;
unsigned int peer;
int __user *ulen;
if (sqe->ioprio || sqe->__pad1 || sqe->len || sqe->rw_flags)
return -EINVAL;
uaddr = u64_to_user_ptr(READ_ONCE(sqe->addr));
ulen = u64_to_user_ptr(sqe->addr3);
peer = READ_ONCE(sqe->optlen);
if (peer > 1)
return -EINVAL;
return do_getsockname(sock, peer, uaddr, ulen);
}
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);
case SOCKET_URING_OP_GETSOCKNAME:
return io_uring_cmd_getsockname(sock, cmd, issue_flags);
default:
return -EOPNOTSUPP;
}
}
EXPORT_SYMBOL_GPL(io_uring_cmd_sock);