2019-01-25 02:59:39 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/* XDP sockets monitoring support
|
|
|
|
*
|
|
|
|
* Copyright(c) 2019 Intel Corporation.
|
|
|
|
*
|
|
|
|
* Author: Björn Töpel <bjorn.topel@intel.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <net/xdp_sock.h>
|
|
|
|
#include <linux/xdp_diag.h>
|
|
|
|
#include <linux/sock_diag.h>
|
|
|
|
|
|
|
|
#include "xsk_queue.h"
|
|
|
|
#include "xsk.h"
|
|
|
|
|
|
|
|
static int xsk_diag_put_info(const struct xdp_sock *xs, struct sk_buff *nlskb)
|
|
|
|
{
|
|
|
|
struct xdp_diag_info di = {};
|
|
|
|
|
|
|
|
di.ifindex = xs->dev ? xs->dev->ifindex : 0;
|
|
|
|
di.queue_id = xs->queue_id;
|
|
|
|
return nla_put(nlskb, XDP_DIAG_INFO, sizeof(di), &di);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int xsk_diag_put_ring(const struct xsk_queue *queue, int nl_type,
|
|
|
|
struct sk_buff *nlskb)
|
|
|
|
{
|
|
|
|
struct xdp_diag_ring dr = {};
|
|
|
|
|
|
|
|
dr.entries = queue->nentries;
|
|
|
|
return nla_put(nlskb, nl_type, sizeof(dr), &dr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int xsk_diag_put_rings_cfg(const struct xdp_sock *xs,
|
|
|
|
struct sk_buff *nlskb)
|
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
if (xs->rx)
|
|
|
|
err = xsk_diag_put_ring(xs->rx, XDP_DIAG_RX_RING, nlskb);
|
|
|
|
if (!err && xs->tx)
|
|
|
|
err = xsk_diag_put_ring(xs->tx, XDP_DIAG_TX_RING, nlskb);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int xsk_diag_put_umem(const struct xdp_sock *xs, struct sk_buff *nlskb)
|
|
|
|
{
|
|
|
|
struct xdp_umem *umem = xs->umem;
|
|
|
|
struct xdp_diag_umem du = {};
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!umem)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
du.id = umem->id;
|
|
|
|
du.size = umem->size;
|
|
|
|
du.num_pages = umem->npgs;
|
xsk: add support to allow unaligned chunk placement
Currently, addresses are chunk size aligned. This means, we are very
restricted in terms of where we can place chunk within the umem. For
example, if we have a chunk size of 2k, then our chunks can only be placed
at 0,2k,4k,6k,8k... and so on (ie. every 2k starting from 0).
This patch introduces the ability to use unaligned chunks. With these
changes, we are no longer bound to having to place chunks at a 2k (or
whatever your chunk size is) interval. Since we are no longer dealing with
aligned chunks, they can now cross page boundaries. Checks for page
contiguity have been added in order to keep track of which pages are
followed by a physically contiguous page.
Signed-off-by: Kevin Laatz <kevin.laatz@intel.com>
Signed-off-by: Ciara Loftus <ciara.loftus@intel.com>
Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
Acked-by: Jonathan Lemon <jonathan.lemon@gmail.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
2019-08-27 10:25:22 +08:00
|
|
|
du.chunk_size = umem->chunk_size_nohr + umem->headroom;
|
2019-01-25 02:59:39 +08:00
|
|
|
du.headroom = umem->headroom;
|
|
|
|
du.ifindex = umem->dev ? umem->dev->ifindex : 0;
|
|
|
|
du.queue_id = umem->queue_id;
|
|
|
|
du.flags = 0;
|
|
|
|
if (umem->zc)
|
|
|
|
du.flags |= XDP_DU_F_ZEROCOPY;
|
|
|
|
du.refs = refcount_read(&umem->users);
|
|
|
|
|
|
|
|
err = nla_put(nlskb, XDP_DIAG_UMEM, sizeof(du), &du);
|
|
|
|
|
|
|
|
if (!err && umem->fq)
|
2019-03-06 01:31:26 +08:00
|
|
|
err = xsk_diag_put_ring(umem->fq, XDP_DIAG_UMEM_FILL_RING, nlskb);
|
2019-01-25 02:59:39 +08:00
|
|
|
if (!err && umem->cq) {
|
2019-03-06 01:31:26 +08:00
|
|
|
err = xsk_diag_put_ring(umem->cq, XDP_DIAG_UMEM_COMPLETION_RING,
|
2019-01-25 02:59:39 +08:00
|
|
|
nlskb);
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int xsk_diag_fill(struct sock *sk, struct sk_buff *nlskb,
|
|
|
|
struct xdp_diag_req *req,
|
|
|
|
struct user_namespace *user_ns,
|
|
|
|
u32 portid, u32 seq, u32 flags, int sk_ino)
|
|
|
|
{
|
|
|
|
struct xdp_sock *xs = xdp_sk(sk);
|
|
|
|
struct xdp_diag_msg *msg;
|
|
|
|
struct nlmsghdr *nlh;
|
|
|
|
|
|
|
|
nlh = nlmsg_put(nlskb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*msg),
|
|
|
|
flags);
|
|
|
|
if (!nlh)
|
|
|
|
return -EMSGSIZE;
|
|
|
|
|
|
|
|
msg = nlmsg_data(nlh);
|
|
|
|
memset(msg, 0, sizeof(*msg));
|
|
|
|
msg->xdiag_family = AF_XDP;
|
|
|
|
msg->xdiag_type = sk->sk_type;
|
|
|
|
msg->xdiag_ino = sk_ino;
|
|
|
|
sock_diag_save_cookie(sk, msg->xdiag_cookie);
|
|
|
|
|
2019-09-04 19:49:13 +08:00
|
|
|
mutex_lock(&xs->mutex);
|
2019-01-25 02:59:39 +08:00
|
|
|
if ((req->xdiag_show & XDP_SHOW_INFO) && xsk_diag_put_info(xs, nlskb))
|
|
|
|
goto out_nlmsg_trim;
|
|
|
|
|
|
|
|
if ((req->xdiag_show & XDP_SHOW_INFO) &&
|
|
|
|
nla_put_u32(nlskb, XDP_DIAG_UID,
|
|
|
|
from_kuid_munged(user_ns, sock_i_uid(sk))))
|
|
|
|
goto out_nlmsg_trim;
|
|
|
|
|
|
|
|
if ((req->xdiag_show & XDP_SHOW_RING_CFG) &&
|
|
|
|
xsk_diag_put_rings_cfg(xs, nlskb))
|
|
|
|
goto out_nlmsg_trim;
|
|
|
|
|
|
|
|
if ((req->xdiag_show & XDP_SHOW_UMEM) &&
|
|
|
|
xsk_diag_put_umem(xs, nlskb))
|
|
|
|
goto out_nlmsg_trim;
|
|
|
|
|
|
|
|
if ((req->xdiag_show & XDP_SHOW_MEMINFO) &&
|
|
|
|
sock_diag_put_meminfo(sk, nlskb, XDP_DIAG_MEMINFO))
|
|
|
|
goto out_nlmsg_trim;
|
|
|
|
|
2019-09-04 19:49:13 +08:00
|
|
|
mutex_unlock(&xs->mutex);
|
2019-01-25 02:59:39 +08:00
|
|
|
nlmsg_end(nlskb, nlh);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
out_nlmsg_trim:
|
2019-09-04 19:49:13 +08:00
|
|
|
mutex_unlock(&xs->mutex);
|
2019-01-25 02:59:39 +08:00
|
|
|
nlmsg_cancel(nlskb, nlh);
|
|
|
|
return -EMSGSIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int xsk_diag_dump(struct sk_buff *nlskb, struct netlink_callback *cb)
|
|
|
|
{
|
|
|
|
struct xdp_diag_req *req = nlmsg_data(cb->nlh);
|
|
|
|
struct net *net = sock_net(nlskb->sk);
|
|
|
|
int num = 0, s_num = cb->args[0];
|
|
|
|
struct sock *sk;
|
|
|
|
|
|
|
|
mutex_lock(&net->xdp.lock);
|
|
|
|
|
|
|
|
sk_for_each(sk, &net->xdp.list) {
|
|
|
|
if (!net_eq(sock_net(sk), net))
|
|
|
|
continue;
|
|
|
|
if (num++ < s_num)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (xsk_diag_fill(sk, nlskb, req,
|
|
|
|
sk_user_ns(NETLINK_CB(cb->skb).sk),
|
|
|
|
NETLINK_CB(cb->skb).portid,
|
|
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
|
|
|
sock_i_ino(sk)) < 0) {
|
|
|
|
num--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&net->xdp.lock);
|
|
|
|
cb->args[0] = num;
|
|
|
|
return nlskb->len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int xsk_diag_handler_dump(struct sk_buff *nlskb, struct nlmsghdr *hdr)
|
|
|
|
{
|
|
|
|
struct netlink_dump_control c = { .dump = xsk_diag_dump };
|
|
|
|
int hdrlen = sizeof(struct xdp_diag_req);
|
|
|
|
struct net *net = sock_net(nlskb->sk);
|
|
|
|
|
|
|
|
if (nlmsg_len(hdr) < hdrlen)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!(hdr->nlmsg_flags & NLM_F_DUMP))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return netlink_dump_start(net->diag_nlsk, nlskb, hdr, &c);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct sock_diag_handler xsk_diag_handler = {
|
|
|
|
.family = AF_XDP,
|
|
|
|
.dump = xsk_diag_handler_dump,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init xsk_diag_init(void)
|
|
|
|
{
|
|
|
|
return sock_diag_register(&xsk_diag_handler);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit xsk_diag_exit(void)
|
|
|
|
{
|
|
|
|
sock_diag_unregister(&xsk_diag_handler);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(xsk_diag_init);
|
|
|
|
module_exit(xsk_diag_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, AF_XDP);
|