2018-05-02 14:01:28 +03:00
// SPDX-License-Identifier: GPL-2.0
/* XSKMAP used for AF_XDP sockets
* Copyright ( c ) 2018 Intel Corporation .
*/
# include <linux/bpf.h>
# include <linux/capability.h>
# include <net/xdp_sock.h>
# include <linux/slab.h>
# include <linux/sched.h>
2020-05-20 22:20:50 +03:00
# include "xsk.h"
2019-08-15 12:30:13 +03:00
static struct xsk_map_node * xsk_map_node_alloc ( struct xsk_map * map ,
struct xdp_sock * * map_entry )
{
struct xsk_map_node * node ;
2020-12-02 00:58:43 +03:00
node = bpf_map_kzalloc ( & map - > map , sizeof ( * node ) ,
GFP_ATOMIC | __GFP_NOWARN ) ;
2019-08-15 12:30:13 +03:00
if ( ! node )
2019-09-24 19:25:21 +03:00
return ERR_PTR ( - ENOMEM ) ;
2019-08-15 12:30:13 +03:00
2020-11-26 18:03:18 +03:00
bpf_map_inc ( & map - > map ) ;
2019-08-15 12:30:13 +03:00
node - > map = map ;
node - > map_entry = map_entry ;
return node ;
}
static void xsk_map_node_free ( struct xsk_map_node * node )
{
2020-11-26 18:03:18 +03:00
bpf_map_put ( & node - > map - > map ) ;
2019-08-15 12:30:13 +03:00
kfree ( node ) ;
}
static void xsk_map_sock_add ( struct xdp_sock * xs , struct xsk_map_node * node )
{
spin_lock_bh ( & xs - > map_list_lock ) ;
list_add_tail ( & node - > node , & xs - > map_list ) ;
spin_unlock_bh ( & xs - > map_list_lock ) ;
}
static void xsk_map_sock_delete ( struct xdp_sock * xs ,
struct xdp_sock * * map_entry )
{
struct xsk_map_node * n , * tmp ;
spin_lock_bh ( & xs - > map_list_lock ) ;
list_for_each_entry_safe ( n , tmp , & xs - > map_list , node ) {
if ( map_entry = = n - > map_entry ) {
list_del ( & n - > node ) ;
xsk_map_node_free ( n ) ;
}
}
spin_unlock_bh ( & xs - > map_list_lock ) ;
}
2018-05-02 14:01:28 +03:00
static struct bpf_map * xsk_map_alloc ( union bpf_attr * attr )
{
struct xsk_map * m ;
2020-12-02 00:58:56 +03:00
int numa_node ;
2019-12-19 09:10:02 +03:00
u64 size ;
2018-05-02 14:01:28 +03:00
if ( ! capable ( CAP_NET_ADMIN ) )
return ERR_PTR ( - EPERM ) ;
if ( attr - > max_entries = = 0 | | attr - > key_size ! = 4 | |
attr - > value_size ! = 4 | |
attr - > map_flags & ~ ( BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY ) )
return ERR_PTR ( - EINVAL ) ;
2019-11-01 14:03:44 +03:00
numa_node = bpf_map_attr_numa_node ( attr ) ;
size = struct_size ( m , xsk_map , attr - > max_entries ) ;
m = bpf_map_area_alloc ( size , numa_node ) ;
2020-12-02 00:58:56 +03:00
if ( ! m )
2018-05-02 14:01:28 +03:00
return ERR_PTR ( - ENOMEM ) ;
bpf_map_init_from_attr ( & m - > map , attr ) ;
2019-08-15 12:30:13 +03:00
spin_lock_init ( & m - > lock ) ;
2018-05-02 14:01:28 +03:00
return & m - > map ;
}
static void xsk_map_free ( struct bpf_map * map )
{
struct xsk_map * m = container_of ( map , struct xsk_map , map ) ;
2018-08-18 00:26:14 +03:00
bpf_clear_redirect_map ( map ) ;
2018-05-02 14:01:28 +03:00
synchronize_net ( ) ;
2019-11-01 14:03:44 +03:00
bpf_map_area_free ( m ) ;
2018-05-02 14:01:28 +03:00
}
static int xsk_map_get_next_key ( struct bpf_map * map , void * key , void * next_key )
{
struct xsk_map * m = container_of ( map , struct xsk_map , map ) ;
u32 index = key ? * ( u32 * ) key : U32_MAX ;
u32 * next = next_key ;
if ( index > = m - > map . max_entries ) {
* next = 0 ;
return 0 ;
}
if ( index = = m - > map . max_entries - 1 )
return - ENOENT ;
* next = index + 1 ;
return 0 ;
}
2020-10-11 02:40:03 +03:00
static int xsk_map_gen_lookup ( struct bpf_map * map , struct bpf_insn * insn_buf )
2019-11-01 14:03:45 +03:00
{
const int ret = BPF_REG_0 , mp = BPF_REG_1 , index = BPF_REG_2 ;
struct bpf_insn * insn = insn_buf ;
* insn + + = BPF_LDX_MEM ( BPF_W , ret , index , 0 ) ;
* insn + + = BPF_JMP_IMM ( BPF_JGE , ret , map - > max_entries , 5 ) ;
* insn + + = BPF_ALU64_IMM ( BPF_LSH , ret , ilog2 ( sizeof ( struct xsk_sock * ) ) ) ;
* insn + + = BPF_ALU64_IMM ( BPF_ADD , mp , offsetof ( struct xsk_map , xsk_map ) ) ;
* insn + + = BPF_ALU64_REG ( BPF_ADD , ret , mp ) ;
* insn + + = BPF_LDX_MEM ( BPF_SIZEOF ( struct xsk_sock * ) , ret , ret , 0 ) ;
* insn + + = BPF_JMP_IMM ( BPF_JA , 0 , 0 , 1 ) ;
* insn + + = BPF_MOV64_IMM ( ret , 0 ) ;
return insn - insn_buf ;
}
2018-05-02 14:01:28 +03:00
static void * xsk_map_lookup_elem ( struct bpf_map * map , void * key )
2019-06-06 23:59:40 +03:00
{
WARN_ON_ONCE ( ! rcu_read_lock_held ( ) ) ;
return __xsk_map_lookup_elem ( map , * ( u32 * ) key ) ;
}
static void * xsk_map_lookup_elem_sys_only ( struct bpf_map * map , void * key )
2018-05-02 14:01:28 +03:00
{
2018-10-09 04:04:50 +03:00
return ERR_PTR ( - EOPNOTSUPP ) ;
2018-05-02 14:01:28 +03:00
}
static int xsk_map_update_elem ( struct bpf_map * map , void * key , void * value ,
u64 map_flags )
{
struct xsk_map * m = container_of ( map , struct xsk_map , map ) ;
2019-08-15 12:30:13 +03:00
struct xdp_sock * xs , * old_xs , * * map_entry ;
2018-05-02 14:01:28 +03:00
u32 i = * ( u32 * ) key , fd = * ( u32 * ) value ;
2019-08-15 12:30:13 +03:00
struct xsk_map_node * node ;
2018-05-02 14:01:28 +03:00
struct socket * sock ;
int err ;
if ( unlikely ( map_flags > BPF_EXIST ) )
return - EINVAL ;
if ( unlikely ( i > = m - > map . max_entries ) )
return - E2BIG ;
sock = sockfd_lookup ( fd , & err ) ;
if ( ! sock )
return err ;
if ( sock - > sk - > sk_family ! = PF_XDP ) {
sockfd_put ( sock ) ;
return - EOPNOTSUPP ;
}
xs = ( struct xdp_sock * ) sock - > sk ;
2019-08-15 12:30:13 +03:00
map_entry = & m - > xsk_map [ i ] ;
node = xsk_map_node_alloc ( m , map_entry ) ;
if ( IS_ERR ( node ) ) {
sockfd_put ( sock ) ;
return PTR_ERR ( node ) ;
}
2018-05-02 14:01:28 +03:00
2019-08-15 12:30:13 +03:00
spin_lock_bh ( & m - > lock ) ;
old_xs = READ_ONCE ( * map_entry ) ;
if ( old_xs = = xs ) {
err = 0 ;
goto out ;
2019-08-15 12:30:14 +03:00
} else if ( old_xs & & map_flags = = BPF_NOEXIST ) {
err = - EEXIST ;
goto out ;
} else if ( ! old_xs & & map_flags = = BPF_EXIST ) {
err = - ENOENT ;
goto out ;
2019-08-15 12:30:13 +03:00
}
xsk_map_sock_add ( xs , node ) ;
WRITE_ONCE ( * map_entry , xs ) ;
2018-10-08 20:40:16 +03:00
if ( old_xs )
2019-08-15 12:30:13 +03:00
xsk_map_sock_delete ( old_xs , map_entry ) ;
spin_unlock_bh ( & m - > lock ) ;
2018-05-02 14:01:28 +03:00
sockfd_put ( sock ) ;
return 0 ;
2019-08-15 12:30:13 +03:00
out :
spin_unlock_bh ( & m - > lock ) ;
sockfd_put ( sock ) ;
xsk_map_node_free ( node ) ;
return err ;
2018-05-02 14:01:28 +03:00
}
static int xsk_map_delete_elem ( struct bpf_map * map , void * key )
{
struct xsk_map * m = container_of ( map , struct xsk_map , map ) ;
2019-08-15 12:30:13 +03:00
struct xdp_sock * old_xs , * * map_entry ;
2018-05-02 14:01:28 +03:00
int k = * ( u32 * ) key ;
if ( k > = map - > max_entries )
return - EINVAL ;
2019-08-15 12:30:13 +03:00
spin_lock_bh ( & m - > lock ) ;
map_entry = & m - > xsk_map [ k ] ;
old_xs = xchg ( map_entry , NULL ) ;
2018-10-08 20:40:16 +03:00
if ( old_xs )
2019-08-15 12:30:13 +03:00
xsk_map_sock_delete ( old_xs , map_entry ) ;
spin_unlock_bh ( & m - > lock ) ;
2018-05-02 14:01:28 +03:00
return 0 ;
}
2019-08-15 12:30:13 +03:00
void xsk_map_try_sock_delete ( struct xsk_map * map , struct xdp_sock * xs ,
struct xdp_sock * * map_entry )
{
spin_lock_bh ( & map - > lock ) ;
if ( READ_ONCE ( * map_entry ) = = xs ) {
WRITE_ONCE ( * map_entry , NULL ) ;
xsk_map_sock_delete ( xs , map_entry ) ;
}
spin_unlock_bh ( & map - > lock ) ;
}
2020-08-28 04:18:13 +03:00
static bool xsk_map_meta_equal ( const struct bpf_map * meta0 ,
const struct bpf_map * meta1 )
{
return meta0 - > max_entries = = meta1 - > max_entries & &
bpf_map_meta_equal ( meta0 , meta1 ) ;
}
2020-06-20 00:11:44 +03:00
static int xsk_map_btf_id ;
2018-05-02 14:01:28 +03:00
const struct bpf_map_ops xsk_map_ops = {
2020-08-28 04:18:13 +03:00
. map_meta_equal = xsk_map_meta_equal ,
2018-05-02 14:01:28 +03:00
. map_alloc = xsk_map_alloc ,
. map_free = xsk_map_free ,
. map_get_next_key = xsk_map_get_next_key ,
. map_lookup_elem = xsk_map_lookup_elem ,
2019-11-01 14:03:45 +03:00
. map_gen_lookup = xsk_map_gen_lookup ,
2019-06-06 23:59:40 +03:00
. map_lookup_elem_sys_only = xsk_map_lookup_elem_sys_only ,
2018-05-02 14:01:28 +03:00
. map_update_elem = xsk_map_update_elem ,
. map_delete_elem = xsk_map_delete_elem ,
2018-08-12 02:59:17 +03:00
. map_check_btf = map_check_no_btf ,
2020-06-20 00:11:44 +03:00
. map_btf_name = " xsk_map " ,
. map_btf_id = & xsk_map_btf_id ,
2018-05-02 14:01:28 +03:00
} ;