2020-08-25 21:29:17 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( c ) 2019 Facebook
* Copyright 2020 Google LLC .
*/
# include <linux/rculist.h>
# include <linux/list.h>
# include <linux/hash.h>
# include <linux/types.h>
# include <linux/spinlock.h>
# include <linux/bpf.h>
# include <linux/bpf_local_storage.h>
# include <net/sock.h>
# include <uapi/linux/sock_diag.h>
# include <uapi/linux/btf.h>
# include <linux/bpf_lsm.h>
# include <linux/btf_ids.h>
# include <linux/fdtable.h>
DEFINE_BPF_STORAGE_CACHE ( inode_cache ) ;
static struct bpf_local_storage __rcu * *
inode_storage_ptr ( void * owner )
{
struct inode * inode = owner ;
struct bpf_storage_blob * bsb ;
bsb = bpf_inode ( inode ) ;
if ( ! bsb )
return NULL ;
return & bsb - > storage ;
}
static struct bpf_local_storage_data * inode_storage_lookup ( struct inode * inode ,
struct bpf_map * map ,
bool cacheit_lockit )
{
struct bpf_local_storage * inode_storage ;
struct bpf_local_storage_map * smap ;
struct bpf_storage_blob * bsb ;
bsb = bpf_inode ( inode ) ;
if ( ! bsb )
return NULL ;
inode_storage = rcu_dereference ( bsb - > storage ) ;
if ( ! inode_storage )
return NULL ;
smap = ( struct bpf_local_storage_map * ) map ;
return bpf_local_storage_lookup ( inode_storage , smap , cacheit_lockit ) ;
}
void bpf_inode_storage_free ( struct inode * inode )
{
struct bpf_local_storage_elem * selem ;
struct bpf_local_storage * local_storage ;
bool free_inode_storage = false ;
struct bpf_storage_blob * bsb ;
struct hlist_node * n ;
bsb = bpf_inode ( inode ) ;
if ( ! bsb )
return ;
rcu_read_lock ( ) ;
local_storage = rcu_dereference ( bsb - > storage ) ;
if ( ! local_storage ) {
rcu_read_unlock ( ) ;
return ;
}
2021-05-25 05:56:59 +03:00
/* Neither the bpf_prog nor the bpf-map's syscall
2020-08-25 21:29:17 +03:00
* could be modifying the local_storage - > list now .
* Thus , no elem can be added - to or deleted - from the
* local_storage - > list by the bpf_prog or by the bpf - map ' s syscall .
*
* It is racing with bpf_local_storage_map_free ( ) alone
* when unlinking elem from the local_storage - > list and
* the map ' s bucket - > list .
*/
raw_spin_lock_bh ( & local_storage - > lock ) ;
hlist_for_each_entry_safe ( selem , n , & local_storage - > list , snode ) {
/* Always unlink from map before unlinking from
* local_storage .
*/
bpf_selem_unlink_map ( selem ) ;
free_inode_storage = bpf_selem_unlink_storage_nolock (
local_storage , selem , false ) ;
}
raw_spin_unlock_bh ( & local_storage - > lock ) ;
rcu_read_unlock ( ) ;
/* free_inoode_storage should always be true as long as
* local_storage - > list was non - empty .
*/
if ( free_inode_storage )
kfree_rcu ( local_storage , rcu ) ;
}
static void * bpf_fd_inode_storage_lookup_elem ( struct bpf_map * map , void * key )
{
struct bpf_local_storage_data * sdata ;
struct file * f ;
int fd ;
fd = * ( int * ) key ;
f = fget_raw ( fd ) ;
if ( ! f )
2021-03-07 15:09:48 +03:00
return ERR_PTR ( - EBADF ) ;
2020-08-25 21:29:17 +03:00
sdata = inode_storage_lookup ( f - > f_inode , map , true ) ;
fput ( f ) ;
return sdata ? sdata - > data : NULL ;
}
static int bpf_fd_inode_storage_update_elem ( struct bpf_map * map , void * key ,
void * value , u64 map_flags )
{
struct bpf_local_storage_data * sdata ;
struct file * f ;
int fd ;
fd = * ( int * ) key ;
f = fget_raw ( fd ) ;
2021-01-21 05:08:56 +03:00
if ( ! f )
return - EBADF ;
if ( ! inode_storage_ptr ( f - > f_inode ) ) {
fput ( f ) ;
2020-08-25 21:29:17 +03:00
return - EBADF ;
2021-01-21 05:08:56 +03:00
}
2020-08-25 21:29:17 +03:00
sdata = bpf_local_storage_update ( f - > f_inode ,
( struct bpf_local_storage_map * ) map ,
value , map_flags ) ;
fput ( f ) ;
return PTR_ERR_OR_ZERO ( sdata ) ;
}
static int inode_storage_delete ( struct inode * inode , struct bpf_map * map )
{
struct bpf_local_storage_data * sdata ;
sdata = inode_storage_lookup ( inode , map , false ) ;
if ( ! sdata )
return - ENOENT ;
bpf_selem_unlink ( SELEM ( sdata ) ) ;
return 0 ;
}
static int bpf_fd_inode_storage_delete_elem ( struct bpf_map * map , void * key )
{
struct file * f ;
int fd , err ;
fd = * ( int * ) key ;
f = fget_raw ( fd ) ;
if ( ! f )
return - EBADF ;
err = inode_storage_delete ( f - > f_inode , map ) ;
fput ( f ) ;
return err ;
}
BPF_CALL_4 ( bpf_inode_storage_get , struct bpf_map * , map , struct inode * , inode ,
void * , value , u64 , flags )
{
struct bpf_local_storage_data * sdata ;
if ( flags & ~ ( BPF_LOCAL_STORAGE_GET_F_CREATE ) )
return ( unsigned long ) NULL ;
/* explicitly check that the inode_storage_ptr is not
* NULL as inode_storage_lookup returns NULL in this case and
* bpf_local_storage_update expects the owner to have a
* valid storage pointer .
*/
2021-01-12 10:55:24 +03:00
if ( ! inode | | ! inode_storage_ptr ( inode ) )
2020-08-25 21:29:17 +03:00
return ( unsigned long ) NULL ;
sdata = inode_storage_lookup ( inode , map , true ) ;
if ( sdata )
return ( unsigned long ) sdata - > data ;
2021-01-12 10:55:25 +03:00
/* This helper must only called from where the inode is guaranteed
2020-08-25 21:29:17 +03:00
* to have a refcount and cannot be freed .
*/
if ( flags & BPF_LOCAL_STORAGE_GET_F_CREATE ) {
sdata = bpf_local_storage_update (
inode , ( struct bpf_local_storage_map * ) map , value ,
BPF_NOEXIST ) ;
return IS_ERR ( sdata ) ? ( unsigned long ) NULL :
( unsigned long ) sdata - > data ;
}
return ( unsigned long ) NULL ;
}
BPF_CALL_2 ( bpf_inode_storage_delete ,
struct bpf_map * , map , struct inode * , inode )
{
2021-01-12 10:55:24 +03:00
if ( ! inode )
return - EINVAL ;
2021-01-12 10:55:25 +03:00
/* This helper must only called from where the inode is guaranteed
2020-08-25 21:29:17 +03:00
* to have a refcount and cannot be freed .
*/
return inode_storage_delete ( inode , map ) ;
}
static int notsupp_get_next_key ( struct bpf_map * map , void * key ,
void * next_key )
{
return - ENOTSUPP ;
}
static struct bpf_map * inode_storage_map_alloc ( union bpf_attr * attr )
{
struct bpf_local_storage_map * smap ;
smap = bpf_local_storage_map_alloc ( attr ) ;
if ( IS_ERR ( smap ) )
return ERR_CAST ( smap ) ;
smap - > cache_idx = bpf_local_storage_cache_idx_get ( & inode_cache ) ;
return & smap - > map ;
}
static void inode_storage_map_free ( struct bpf_map * map )
{
struct bpf_local_storage_map * smap ;
smap = ( struct bpf_local_storage_map * ) map ;
bpf_local_storage_cache_idx_free ( & inode_cache , smap - > cache_idx ) ;
2021-02-26 02:43:15 +03:00
bpf_local_storage_map_free ( smap , NULL ) ;
2020-08-25 21:29:17 +03:00
}
static int inode_storage_map_btf_id ;
const struct bpf_map_ops inode_storage_map_ops = {
2020-08-28 04:18:06 +03:00
. map_meta_equal = bpf_map_meta_equal ,
2020-08-25 21:29:17 +03:00
. map_alloc_check = bpf_local_storage_map_alloc_check ,
. map_alloc = inode_storage_map_alloc ,
. map_free = inode_storage_map_free ,
. map_get_next_key = notsupp_get_next_key ,
. map_lookup_elem = bpf_fd_inode_storage_lookup_elem ,
. map_update_elem = bpf_fd_inode_storage_update_elem ,
. map_delete_elem = bpf_fd_inode_storage_delete_elem ,
. map_check_btf = bpf_local_storage_map_check_btf ,
. map_btf_name = " bpf_local_storage_map " ,
. map_btf_id = & inode_storage_map_btf_id ,
. map_owner_storage_ptr = inode_storage_ptr ,
} ;
2020-09-21 15:12:20 +03:00
BTF_ID_LIST_SINGLE ( bpf_inode_storage_btf_ids , struct , inode )
2020-08-25 21:29:17 +03:00
const struct bpf_func_proto bpf_inode_storage_get_proto = {
. func = bpf_inode_storage_get ,
. gpl_only = false ,
. ret_type = RET_PTR_TO_MAP_VALUE_OR_NULL ,
. arg1_type = ARG_CONST_MAP_PTR ,
. arg2_type = ARG_PTR_TO_BTF_ID ,
2020-09-21 15:12:20 +03:00
. arg2_btf_id = & bpf_inode_storage_btf_ids [ 0 ] ,
2020-08-25 21:29:17 +03:00
. arg3_type = ARG_PTR_TO_MAP_VALUE_OR_NULL ,
. arg4_type = ARG_ANYTHING ,
} ;
const struct bpf_func_proto bpf_inode_storage_delete_proto = {
. func = bpf_inode_storage_delete ,
. gpl_only = false ,
. ret_type = RET_INTEGER ,
. arg1_type = ARG_CONST_MAP_PTR ,
. arg2_type = ARG_PTR_TO_BTF_ID ,
2020-09-21 15:12:20 +03:00
. arg2_btf_id = & bpf_inode_storage_btf_ids [ 0 ] ,
2020-08-25 21:29:17 +03:00
} ;