2019-01-24 21:59:39 +03: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 )
{
2020-08-28 11:26:18 +03:00
struct xsk_buff_pool * pool = xs - > pool ;
2019-01-24 21:59:39 +03:00
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 ;
2020-05-20 22:20:53 +03:00
du . chunk_size = umem - > chunk_size ;
2019-01-24 21:59:39 +03:00
du . headroom = umem - > headroom ;
2020-09-02 11:52:23 +03:00
du . ifindex = ( pool & & pool - > netdev ) ? pool - > netdev - > ifindex : 0 ;
du . queue_id = pool ? pool - > queue_id : 0 ;
2019-01-24 21:59:39 +03:00
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 ) ;
2020-09-02 11:52:23 +03:00
if ( ! err & & pool & & pool - > fq )
2020-08-28 11:26:18 +03:00
err = xsk_diag_put_ring ( pool - > fq ,
XDP_DIAG_UMEM_FILL_RING , nlskb ) ;
2020-09-02 11:52:23 +03:00
if ( ! err & & pool & & pool - > cq )
err = xsk_diag_put_ring ( pool - > cq ,
XDP_DIAG_UMEM_COMPLETION_RING , nlskb ) ;
2019-01-24 21:59:39 +03:00
return err ;
}
2020-07-08 10:28:35 +03:00
static int xsk_diag_put_stats ( const struct xdp_sock * xs , struct sk_buff * nlskb )
{
struct xdp_diag_stats du = { } ;
du . n_rx_dropped = xs - > rx_dropped ;
du . n_rx_invalid = xskq_nb_invalid_descs ( xs - > rx ) ;
du . n_rx_full = xs - > rx_queue_full ;
2020-08-28 11:26:18 +03:00
du . n_fill_ring_empty = xs - > pool ? xskq_nb_queue_empty_descs ( xs - > pool - > fq ) : 0 ;
2020-07-08 10:28:35 +03:00
du . n_tx_invalid = xskq_nb_invalid_descs ( xs - > tx ) ;
du . n_tx_ring_empty = xskq_nb_queue_empty_descs ( xs - > tx ) ;
return nla_put ( nlskb , XDP_DIAG_STATS , sizeof ( du ) , & du ) ;
}
2019-01-24 21:59:39 +03:00
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 14:49:13 +03:00
mutex_lock ( & xs - > mutex ) ;
2019-01-24 21:59:39 +03: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 ;
2020-07-08 10:28:35 +03:00
if ( ( req - > xdiag_show & XDP_SHOW_STATS ) & &
xsk_diag_put_stats ( xs , nlskb ) )
goto out_nlmsg_trim ;
2019-09-04 14:49:13 +03:00
mutex_unlock ( & xs - > mutex ) ;
2019-01-24 21:59:39 +03:00
nlmsg_end ( nlskb , nlh ) ;
return 0 ;
out_nlmsg_trim :
2019-09-04 14:49:13 +03:00
mutex_unlock ( & xs - > mutex ) ;
2019-01-24 21:59:39 +03: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 ) ;