162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* XDP sockets monitoring support 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright(c) 2019 Intel Corporation. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Author: Björn Töpel <bjorn.topel@intel.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <net/xdp_sock.h> 1162306a36Sopenharmony_ci#include <linux/xdp_diag.h> 1262306a36Sopenharmony_ci#include <linux/sock_diag.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "xsk_queue.h" 1562306a36Sopenharmony_ci#include "xsk.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic int xsk_diag_put_info(const struct xdp_sock *xs, struct sk_buff *nlskb) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci struct xdp_diag_info di = {}; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci di.ifindex = xs->dev ? xs->dev->ifindex : 0; 2262306a36Sopenharmony_ci di.queue_id = xs->queue_id; 2362306a36Sopenharmony_ci return nla_put(nlskb, XDP_DIAG_INFO, sizeof(di), &di); 2462306a36Sopenharmony_ci} 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic int xsk_diag_put_ring(const struct xsk_queue *queue, int nl_type, 2762306a36Sopenharmony_ci struct sk_buff *nlskb) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct xdp_diag_ring dr = {}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci dr.entries = queue->nentries; 3262306a36Sopenharmony_ci return nla_put(nlskb, nl_type, sizeof(dr), &dr); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int xsk_diag_put_rings_cfg(const struct xdp_sock *xs, 3662306a36Sopenharmony_ci struct sk_buff *nlskb) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci int err = 0; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (xs->rx) 4162306a36Sopenharmony_ci err = xsk_diag_put_ring(xs->rx, XDP_DIAG_RX_RING, nlskb); 4262306a36Sopenharmony_ci if (!err && xs->tx) 4362306a36Sopenharmony_ci err = xsk_diag_put_ring(xs->tx, XDP_DIAG_TX_RING, nlskb); 4462306a36Sopenharmony_ci return err; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int xsk_diag_put_umem(const struct xdp_sock *xs, struct sk_buff *nlskb) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct xsk_buff_pool *pool = xs->pool; 5062306a36Sopenharmony_ci struct xdp_umem *umem = xs->umem; 5162306a36Sopenharmony_ci struct xdp_diag_umem du = {}; 5262306a36Sopenharmony_ci int err; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (!umem) 5562306a36Sopenharmony_ci return 0; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci du.id = umem->id; 5862306a36Sopenharmony_ci du.size = umem->size; 5962306a36Sopenharmony_ci du.num_pages = umem->npgs; 6062306a36Sopenharmony_ci du.chunk_size = umem->chunk_size; 6162306a36Sopenharmony_ci du.headroom = umem->headroom; 6262306a36Sopenharmony_ci du.ifindex = (pool && pool->netdev) ? pool->netdev->ifindex : 0; 6362306a36Sopenharmony_ci du.queue_id = pool ? pool->queue_id : 0; 6462306a36Sopenharmony_ci du.flags = 0; 6562306a36Sopenharmony_ci if (umem->zc) 6662306a36Sopenharmony_ci du.flags |= XDP_DU_F_ZEROCOPY; 6762306a36Sopenharmony_ci du.refs = refcount_read(&umem->users); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci err = nla_put(nlskb, XDP_DIAG_UMEM, sizeof(du), &du); 7062306a36Sopenharmony_ci if (!err && pool && pool->fq) 7162306a36Sopenharmony_ci err = xsk_diag_put_ring(pool->fq, 7262306a36Sopenharmony_ci XDP_DIAG_UMEM_FILL_RING, nlskb); 7362306a36Sopenharmony_ci if (!err && pool && pool->cq) 7462306a36Sopenharmony_ci err = xsk_diag_put_ring(pool->cq, 7562306a36Sopenharmony_ci XDP_DIAG_UMEM_COMPLETION_RING, nlskb); 7662306a36Sopenharmony_ci return err; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic int xsk_diag_put_stats(const struct xdp_sock *xs, struct sk_buff *nlskb) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct xdp_diag_stats du = {}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci du.n_rx_dropped = xs->rx_dropped; 8462306a36Sopenharmony_ci du.n_rx_invalid = xskq_nb_invalid_descs(xs->rx); 8562306a36Sopenharmony_ci du.n_rx_full = xs->rx_queue_full; 8662306a36Sopenharmony_ci du.n_fill_ring_empty = xs->pool ? xskq_nb_queue_empty_descs(xs->pool->fq) : 0; 8762306a36Sopenharmony_ci du.n_tx_invalid = xskq_nb_invalid_descs(xs->tx); 8862306a36Sopenharmony_ci du.n_tx_ring_empty = xskq_nb_queue_empty_descs(xs->tx); 8962306a36Sopenharmony_ci return nla_put(nlskb, XDP_DIAG_STATS, sizeof(du), &du); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int xsk_diag_fill(struct sock *sk, struct sk_buff *nlskb, 9362306a36Sopenharmony_ci struct xdp_diag_req *req, 9462306a36Sopenharmony_ci struct user_namespace *user_ns, 9562306a36Sopenharmony_ci u32 portid, u32 seq, u32 flags, int sk_ino) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct xdp_sock *xs = xdp_sk(sk); 9862306a36Sopenharmony_ci struct xdp_diag_msg *msg; 9962306a36Sopenharmony_ci struct nlmsghdr *nlh; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci nlh = nlmsg_put(nlskb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*msg), 10262306a36Sopenharmony_ci flags); 10362306a36Sopenharmony_ci if (!nlh) 10462306a36Sopenharmony_ci return -EMSGSIZE; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci msg = nlmsg_data(nlh); 10762306a36Sopenharmony_ci memset(msg, 0, sizeof(*msg)); 10862306a36Sopenharmony_ci msg->xdiag_family = AF_XDP; 10962306a36Sopenharmony_ci msg->xdiag_type = sk->sk_type; 11062306a36Sopenharmony_ci msg->xdiag_ino = sk_ino; 11162306a36Sopenharmony_ci sock_diag_save_cookie(sk, msg->xdiag_cookie); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci mutex_lock(&xs->mutex); 11462306a36Sopenharmony_ci if (READ_ONCE(xs->state) == XSK_UNBOUND) 11562306a36Sopenharmony_ci goto out_nlmsg_trim; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if ((req->xdiag_show & XDP_SHOW_INFO) && xsk_diag_put_info(xs, nlskb)) 11862306a36Sopenharmony_ci goto out_nlmsg_trim; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if ((req->xdiag_show & XDP_SHOW_INFO) && 12162306a36Sopenharmony_ci nla_put_u32(nlskb, XDP_DIAG_UID, 12262306a36Sopenharmony_ci from_kuid_munged(user_ns, sock_i_uid(sk)))) 12362306a36Sopenharmony_ci goto out_nlmsg_trim; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if ((req->xdiag_show & XDP_SHOW_RING_CFG) && 12662306a36Sopenharmony_ci xsk_diag_put_rings_cfg(xs, nlskb)) 12762306a36Sopenharmony_ci goto out_nlmsg_trim; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if ((req->xdiag_show & XDP_SHOW_UMEM) && 13062306a36Sopenharmony_ci xsk_diag_put_umem(xs, nlskb)) 13162306a36Sopenharmony_ci goto out_nlmsg_trim; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if ((req->xdiag_show & XDP_SHOW_MEMINFO) && 13462306a36Sopenharmony_ci sock_diag_put_meminfo(sk, nlskb, XDP_DIAG_MEMINFO)) 13562306a36Sopenharmony_ci goto out_nlmsg_trim; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if ((req->xdiag_show & XDP_SHOW_STATS) && 13862306a36Sopenharmony_ci xsk_diag_put_stats(xs, nlskb)) 13962306a36Sopenharmony_ci goto out_nlmsg_trim; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci mutex_unlock(&xs->mutex); 14262306a36Sopenharmony_ci nlmsg_end(nlskb, nlh); 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ciout_nlmsg_trim: 14662306a36Sopenharmony_ci mutex_unlock(&xs->mutex); 14762306a36Sopenharmony_ci nlmsg_cancel(nlskb, nlh); 14862306a36Sopenharmony_ci return -EMSGSIZE; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic int xsk_diag_dump(struct sk_buff *nlskb, struct netlink_callback *cb) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct xdp_diag_req *req = nlmsg_data(cb->nlh); 15462306a36Sopenharmony_ci struct net *net = sock_net(nlskb->sk); 15562306a36Sopenharmony_ci int num = 0, s_num = cb->args[0]; 15662306a36Sopenharmony_ci struct sock *sk; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci mutex_lock(&net->xdp.lock); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci sk_for_each(sk, &net->xdp.list) { 16162306a36Sopenharmony_ci if (!net_eq(sock_net(sk), net)) 16262306a36Sopenharmony_ci continue; 16362306a36Sopenharmony_ci if (num++ < s_num) 16462306a36Sopenharmony_ci continue; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (xsk_diag_fill(sk, nlskb, req, 16762306a36Sopenharmony_ci sk_user_ns(NETLINK_CB(cb->skb).sk), 16862306a36Sopenharmony_ci NETLINK_CB(cb->skb).portid, 16962306a36Sopenharmony_ci cb->nlh->nlmsg_seq, NLM_F_MULTI, 17062306a36Sopenharmony_ci sock_i_ino(sk)) < 0) { 17162306a36Sopenharmony_ci num--; 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci mutex_unlock(&net->xdp.lock); 17762306a36Sopenharmony_ci cb->args[0] = num; 17862306a36Sopenharmony_ci return nlskb->len; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int xsk_diag_handler_dump(struct sk_buff *nlskb, struct nlmsghdr *hdr) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct netlink_dump_control c = { .dump = xsk_diag_dump }; 18462306a36Sopenharmony_ci int hdrlen = sizeof(struct xdp_diag_req); 18562306a36Sopenharmony_ci struct net *net = sock_net(nlskb->sk); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (nlmsg_len(hdr) < hdrlen) 18862306a36Sopenharmony_ci return -EINVAL; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (!(hdr->nlmsg_flags & NLM_F_DUMP)) 19162306a36Sopenharmony_ci return -EOPNOTSUPP; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return netlink_dump_start(net->diag_nlsk, nlskb, hdr, &c); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic const struct sock_diag_handler xsk_diag_handler = { 19762306a36Sopenharmony_ci .family = AF_XDP, 19862306a36Sopenharmony_ci .dump = xsk_diag_handler_dump, 19962306a36Sopenharmony_ci}; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic int __init xsk_diag_init(void) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci return sock_diag_register(&xsk_diag_handler); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void __exit xsk_diag_exit(void) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci sock_diag_unregister(&xsk_diag_handler); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cimodule_init(xsk_diag_init); 21262306a36Sopenharmony_cimodule_exit(xsk_diag_exit); 21362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 21462306a36Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, AF_XDP); 215