2018-08-09 20:14:36 -06:00
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* Copyright ( c ) 2017 , Mellanox Technologies inc . All rights reserved .
*/
# include <rdma/uverbs_ioctl.h>
# include <rdma/rdma_user_ioctl.h>
# include <linux/bitops.h>
# include "rdma_core.h"
# include "uverbs.h"
static void * uapi_add_elm ( struct uverbs_api * uapi , u32 key , size_t alloc_size )
{
void * elm ;
int rc ;
if ( key = = UVERBS_API_KEY_ERR )
return ERR_PTR ( - EOVERFLOW ) ;
elm = kzalloc ( alloc_size , GFP_KERNEL ) ;
rc = radix_tree_insert ( & uapi - > radix , key , elm ) ;
if ( rc ) {
kfree ( elm ) ;
return ERR_PTR ( rc ) ;
}
return elm ;
}
2018-11-12 22:59:51 +02:00
static void * uapi_add_get_elm ( struct uverbs_api * uapi , u32 key ,
size_t alloc_size , bool * exists )
{
void * elm ;
elm = uapi_add_elm ( uapi , key , alloc_size ) ;
if ( ! IS_ERR ( elm ) ) {
* exists = false ;
return elm ;
}
if ( elm ! = ERR_PTR ( - EEXIST ) )
return elm ;
elm = radix_tree_lookup ( & uapi - > radix , key ) ;
if ( WARN_ON ( ! elm ) )
return ERR_PTR ( - EINVAL ) ;
* exists = true ;
return elm ;
}
2018-08-09 20:14:36 -06:00
static int uapi_merge_method ( struct uverbs_api * uapi ,
struct uverbs_api_object * obj_elm , u32 obj_key ,
const struct uverbs_method_def * method ,
bool is_driver )
{
u32 method_key = obj_key | uapi_key_ioctl_method ( method - > id ) ;
struct uverbs_api_ioctl_method * method_elm ;
unsigned int i ;
2018-11-12 22:59:51 +02:00
bool exists ;
2018-08-09 20:14:36 -06:00
if ( ! method - > attrs )
return 0 ;
2018-11-12 22:59:51 +02:00
method_elm = uapi_add_get_elm ( uapi , method_key , sizeof ( * method_elm ) ,
& exists ) ;
if ( IS_ERR ( method_elm ) )
return PTR_ERR ( method_elm ) ;
if ( exists ) {
2018-08-09 20:14:36 -06:00
/*
* This occurs when a driver uses ADD_UVERBS_ATTRIBUTES_SIMPLE
*/
if ( WARN_ON ( method - > handler ) )
return - EINVAL ;
} else {
WARN_ON ( ! method - > handler ) ;
rcu_assign_pointer ( method_elm - > handler , method - > handler ) ;
if ( method - > handler ! = uverbs_destroy_def_handler )
method_elm - > driver_method = is_driver ;
}
for ( i = 0 ; i ! = method - > num_attrs ; i + + ) {
const struct uverbs_attr_def * attr = ( * method - > attrs ) [ i ] ;
struct uverbs_api_attr * attr_slot ;
if ( ! attr )
continue ;
/*
* ENUM_IN contains the ' ids ' pointer to the driver ' s . rodata ,
* so if it is specified by a driver then it always makes this
* into a driver method .
*/
if ( attr - > attr . type = = UVERBS_ATTR_TYPE_ENUM_IN )
method_elm - > driver_method | = is_driver ;
2018-09-06 17:27:01 +03:00
/*
* Like other uobject based things we only support a single
* uobject being NEW ' d or DESTROY ' d
*/
if ( attr - > attr . type = = UVERBS_ATTR_TYPE_IDRS_ARRAY ) {
u8 access = attr - > attr . u2 . objs_arr . access ;
if ( WARN_ON ( access = = UVERBS_ACCESS_NEW | |
access = = UVERBS_ACCESS_DESTROY ) )
return - EINVAL ;
}
2018-08-09 20:14:36 -06:00
attr_slot =
uapi_add_elm ( uapi , method_key | uapi_key_attr ( attr - > id ) ,
sizeof ( * attr_slot ) ) ;
/* Attributes are not allowed to be modified by drivers */
if ( IS_ERR ( attr_slot ) )
return PTR_ERR ( attr_slot ) ;
attr_slot - > spec = attr - > attr ;
}
return 0 ;
}
2018-11-12 22:59:50 +02:00
static int uapi_merge_obj_tree ( struct uverbs_api * uapi ,
const struct uverbs_object_def * obj ,
bool is_driver )
2018-08-09 20:14:36 -06:00
{
2018-11-12 22:59:50 +02:00
struct uverbs_api_object * obj_elm ;
unsigned int i ;
u32 obj_key ;
2018-11-12 22:59:51 +02:00
bool exists ;
2018-08-09 20:14:36 -06:00
int rc ;
2018-11-12 22:59:50 +02:00
obj_key = uapi_key_obj ( obj - > id ) ;
2018-11-12 22:59:51 +02:00
obj_elm = uapi_add_get_elm ( uapi , obj_key , sizeof ( * obj_elm ) , & exists ) ;
if ( IS_ERR ( obj_elm ) )
return PTR_ERR ( obj_elm ) ;
2018-11-12 22:59:50 +02:00
2018-11-12 22:59:51 +02:00
if ( obj - > type_attrs ) {
if ( WARN_ON ( obj_elm - > type_attrs ) )
2018-11-12 22:59:50 +02:00
return - EINVAL ;
2018-11-12 22:59:51 +02:00
2018-11-12 22:59:50 +02:00
obj_elm - > type_attrs = obj - > type_attrs ;
2018-11-12 22:59:51 +02:00
obj_elm - > type_class = obj - > type_attrs - > type_class ;
/*
* Today drivers are only permitted to use idr_class
* types . They cannot use FD types because we currently have
* no way to revoke the fops pointer after device
* disassociation .
*/
if ( WARN_ON ( is_driver & &
obj - > type_attrs - > type_class ! = & uverbs_idr_class ) )
return - EINVAL ;
2018-11-12 22:59:50 +02:00
}
if ( ! obj - > methods )
2018-08-09 20:14:36 -06:00
return 0 ;
2018-11-12 22:59:50 +02:00
for ( i = 0 ; i ! = obj - > num_methods ; i + + ) {
const struct uverbs_method_def * method = ( * obj - > methods ) [ i ] ;
2018-08-09 20:14:36 -06:00
2018-11-12 22:59:50 +02:00
if ( ! method )
2018-08-09 20:14:36 -06:00
continue ;
2018-11-12 22:59:50 +02:00
rc = uapi_merge_method ( uapi , obj_elm , obj_key , method ,
is_driver ) ;
if ( rc )
return rc ;
}
2018-08-09 20:14:36 -06:00
2018-11-12 22:59:50 +02:00
return 0 ;
}
2018-11-12 22:59:52 +02:00
static int uapi_disable_elm ( struct uverbs_api * uapi ,
const struct uapi_definition * def ,
u32 obj_key )
{
bool exists ;
if ( def - > scope = = UAPI_SCOPE_OBJECT ) {
struct uverbs_api_object * obj_elm ;
obj_elm = uapi_add_get_elm (
uapi , obj_key , sizeof ( * obj_elm ) , & exists ) ;
if ( IS_ERR ( obj_elm ) )
return PTR_ERR ( obj_elm ) ;
obj_elm - > disabled = 1 ;
return 0 ;
}
WARN_ON ( true ) ;
return - EINVAL ;
}
static int uapi_merge_def ( struct uverbs_api * uapi , struct ib_device * ibdev ,
2018-11-12 22:59:50 +02:00
const struct uapi_definition * def_list ,
bool is_driver )
{
const struct uapi_definition * def = def_list ;
2018-11-12 22:59:52 +02:00
u32 cur_obj_key = UVERBS_API_KEY_ERR ;
2018-11-12 22:59:50 +02:00
int rc ;
if ( ! def_list )
return 0 ;
2018-08-09 20:14:36 -06:00
2018-11-12 22:59:50 +02:00
for ( ; ; def + + ) {
switch ( ( enum uapi_definition_kind ) def - > kind ) {
case UAPI_DEF_CHAIN :
2018-11-12 22:59:52 +02:00
rc = uapi_merge_def ( uapi , ibdev , def - > chain , is_driver ) ;
2018-11-12 22:59:50 +02:00
if ( rc )
return rc ;
2018-08-09 20:14:36 -06:00
continue ;
2018-11-12 22:59:50 +02:00
case UAPI_DEF_CHAIN_OBJ_TREE :
if ( WARN_ON ( def - > object_start . object_id ! =
def - > chain_obj_tree - > id ) )
return - EINVAL ;
2018-08-09 20:14:36 -06:00
2018-11-12 22:59:52 +02:00
cur_obj_key = uapi_key_obj ( def - > object_start . object_id ) ;
2018-11-12 22:59:50 +02:00
rc = uapi_merge_obj_tree ( uapi , def - > chain_obj_tree ,
is_driver ) ;
2018-08-09 20:14:36 -06:00
if ( rc )
return rc ;
2018-11-12 22:59:50 +02:00
continue ;
case UAPI_DEF_END :
return 0 ;
2018-11-12 22:59:52 +02:00
case UAPI_DEF_IS_SUPPORTED_DEV_FN : {
void * * ibdev_fn = ( void * ) ibdev + def - > needs_fn_offset ;
if ( * ibdev_fn )
continue ;
rc = uapi_disable_elm ( uapi , def , cur_obj_key ) ;
if ( rc )
return rc ;
continue ;
}
case UAPI_DEF_IS_SUPPORTED_FUNC :
if ( def - > func_is_supported ( ibdev ) )
continue ;
rc = uapi_disable_elm ( uapi , def , cur_obj_key ) ;
if ( rc )
return rc ;
continue ;
2018-08-09 20:14:36 -06:00
}
2018-11-12 22:59:50 +02:00
WARN_ON ( true ) ;
return - EINVAL ;
2018-08-09 20:14:36 -06:00
}
}
static int
uapi_finalize_ioctl_method ( struct uverbs_api * uapi ,
struct uverbs_api_ioctl_method * method_elm ,
u32 method_key )
{
struct radix_tree_iter iter ;
2018-08-09 20:14:42 -06:00
unsigned int num_attrs = 0 ;
2018-08-09 20:14:36 -06:00
unsigned int max_bkey = 0 ;
bool single_uobj = false ;
void __rcu * * slot ;
method_elm - > destroy_bkey = UVERBS_API_ATTR_BKEY_LEN ;
radix_tree_for_each_slot ( slot , & uapi - > radix , & iter ,
uapi_key_attrs_start ( method_key ) ) {
struct uverbs_api_attr * elm =
rcu_dereference_protected ( * slot , true ) ;
u32 attr_key = iter . index & UVERBS_API_ATTR_KEY_MASK ;
u32 attr_bkey = uapi_bkey_attr ( attr_key ) ;
u8 type = elm - > spec . type ;
if ( uapi_key_attr_to_method ( iter . index ) ! =
uapi_key_attr_to_method ( method_key ) )
break ;
if ( elm - > spec . mandatory )
__set_bit ( attr_bkey , method_elm - > attr_mandatory ) ;
if ( type = = UVERBS_ATTR_TYPE_IDR | |
type = = UVERBS_ATTR_TYPE_FD ) {
u8 access = elm - > spec . u . obj . access ;
/*
* Verbs specs may only have one NEW / DESTROY , we don ' t
* have the infrastructure to abort multiple NEW ' s or
* cope with multiple DESTROY failure .
*/
if ( access = = UVERBS_ACCESS_NEW | |
access = = UVERBS_ACCESS_DESTROY ) {
if ( WARN_ON ( single_uobj ) )
return - EINVAL ;
single_uobj = true ;
if ( WARN_ON ( ! elm - > spec . mandatory ) )
return - EINVAL ;
}
if ( access = = UVERBS_ACCESS_DESTROY )
method_elm - > destroy_bkey = attr_bkey ;
}
max_bkey = max ( max_bkey , attr_bkey ) ;
2018-08-09 20:14:42 -06:00
num_attrs + + ;
2018-08-09 20:14:36 -06:00
}
method_elm - > key_bitmap_len = max_bkey + 1 ;
WARN_ON ( method_elm - > key_bitmap_len > UVERBS_API_ATTR_BKEY_LEN ) ;
2018-08-09 20:14:42 -06:00
uapi_compute_bundle_size ( method_elm , num_attrs ) ;
2018-08-09 20:14:36 -06:00
return 0 ;
}
static int uapi_finalize ( struct uverbs_api * uapi )
{
struct radix_tree_iter iter ;
void __rcu * * slot ;
int rc ;
radix_tree_for_each_slot ( slot , & uapi - > radix , & iter , 0 ) {
struct uverbs_api_ioctl_method * method_elm =
rcu_dereference_protected ( * slot , true ) ;
if ( uapi_key_is_ioctl_method ( iter . index ) ) {
rc = uapi_finalize_ioctl_method ( uapi , method_elm ,
iter . index ) ;
if ( rc )
return rc ;
}
}
return 0 ;
}
2018-11-12 22:59:52 +02:00
static void uapi_remove_range ( struct uverbs_api * uapi , u32 start , u32 last )
2018-08-09 20:14:36 -06:00
{
struct radix_tree_iter iter ;
void __rcu * * slot ;
2018-11-12 22:59:52 +02:00
radix_tree_for_each_slot ( slot , & uapi - > radix , & iter , start ) {
if ( iter . index > last )
return ;
2018-08-09 20:14:36 -06:00
kfree ( rcu_dereference_protected ( * slot , true ) ) ;
radix_tree_iter_delete ( & uapi - > radix , & iter , slot ) ;
}
2018-11-12 22:59:52 +02:00
}
static void uapi_remove_object ( struct uverbs_api * uapi , u32 obj_key )
{
uapi_remove_range ( uapi , obj_key ,
obj_key | UVERBS_API_METHOD_KEY_MASK |
UVERBS_API_ATTR_KEY_MASK ) ;
}
static void uapi_remove_method ( struct uverbs_api * uapi , u32 method_key )
{
uapi_remove_range ( uapi , method_key ,
method_key | UVERBS_API_ATTR_KEY_MASK ) ;
}
static u32 uapi_get_obj_id ( struct uverbs_attr_spec * spec )
{
if ( spec - > type = = UVERBS_ATTR_TYPE_IDR | |
spec - > type = = UVERBS_ATTR_TYPE_FD )
return spec - > u . obj . obj_type ;
if ( spec - > type = = UVERBS_ATTR_TYPE_IDRS_ARRAY )
return spec - > u2 . objs_arr . obj_type ;
return UVERBS_API_KEY_ERR ;
}
static void uapi_finalize_disable ( struct uverbs_api * uapi )
{
struct radix_tree_iter iter ;
u32 starting_key = 0 ;
bool scan_again = false ;
void __rcu * * slot ;
again :
radix_tree_for_each_slot ( slot , & uapi - > radix , & iter , starting_key ) {
if ( uapi_key_is_object ( iter . index ) ) {
struct uverbs_api_object * obj_elm =
rcu_dereference_protected ( * slot , true ) ;
if ( obj_elm - > disabled ) {
/* Have to check all the attrs again */
scan_again = true ;
starting_key = iter . index ;
uapi_remove_object ( uapi , iter . index ) ;
goto again ;
}
continue ;
}
if ( uapi_key_is_ioctl_method ( iter . index ) ) {
struct uverbs_api_ioctl_method * method_elm =
rcu_dereference_protected ( * slot , true ) ;
if ( method_elm - > disabled ) {
starting_key = iter . index ;
uapi_remove_method ( uapi , iter . index ) ;
goto again ;
}
continue ;
}
if ( uapi_key_is_attr ( iter . index ) ) {
struct uverbs_api_attr * attr_elm =
rcu_dereference_protected ( * slot , true ) ;
const struct uverbs_api_object * tmp_obj ;
u32 obj_key ;
/*
* If the method has a mandatory object handle
* attribute which relies on an object which is not
* present then the entire method is uncallable .
*/
if ( ! attr_elm - > spec . mandatory )
continue ;
obj_key = uapi_get_obj_id ( & attr_elm - > spec ) ;
if ( obj_key = = UVERBS_API_KEY_ERR )
continue ;
tmp_obj = uapi_get_object ( uapi , obj_key ) ;
if ( tmp_obj & & ! tmp_obj - > disabled )
continue ;
starting_key = iter . index ;
uapi_remove_method (
uapi ,
iter . index & ( UVERBS_API_OBJ_KEY_MASK |
UVERBS_API_METHOD_KEY_MASK ) ) ;
goto again ;
}
WARN_ON ( false ) ;
}
if ( ! scan_again )
return ;
scan_again = false ;
starting_key = 0 ;
goto again ;
}
void uverbs_destroy_api ( struct uverbs_api * uapi )
{
if ( ! uapi )
return ;
uapi_remove_range ( uapi , 0 , U32_MAX ) ;
2018-09-25 11:23:55 +03:00
kfree ( uapi ) ;
2018-08-09 20:14:36 -06:00
}
2018-11-12 22:59:50 +02:00
static const struct uapi_definition uverbs_core_api [ ] = {
UAPI_DEF_CHAIN ( uverbs_def_obj_intf ) ,
{ } ,
} ;
2018-11-12 22:59:52 +02:00
struct uverbs_api * uverbs_alloc_api ( struct ib_device * ibdev )
2018-08-09 20:14:36 -06:00
{
struct uverbs_api * uapi ;
int rc ;
uapi = kzalloc ( sizeof ( * uapi ) , GFP_KERNEL ) ;
if ( ! uapi )
return ERR_PTR ( - ENOMEM ) ;
INIT_RADIX_TREE ( & uapi - > radix , GFP_KERNEL ) ;
2018-11-12 22:59:52 +02:00
uapi - > driver_id = ibdev - > driver_id ;
2018-08-09 20:14:36 -06:00
2018-11-12 22:59:52 +02:00
rc = uapi_merge_def ( uapi , ibdev , uverbs_core_api , false ) ;
2018-11-12 22:59:50 +02:00
if ( rc )
goto err ;
2018-11-12 22:59:52 +02:00
rc = uapi_merge_def ( uapi , ibdev , ibdev - > driver_def , true ) ;
2018-08-09 20:14:36 -06:00
if ( rc )
goto err ;
2018-11-12 22:59:52 +02:00
uapi_finalize_disable ( uapi ) ;
2018-08-09 20:14:36 -06:00
rc = uapi_finalize ( uapi ) ;
if ( rc )
goto err ;
return uapi ;
err :
if ( rc ! = - ENOMEM )
2018-11-12 22:59:52 +02:00
dev_err ( & ibdev - > dev ,
" Setup of uverbs_api failed, kernel parsing tree description is not valid (%d)?? \n " ,
rc ) ;
2018-08-09 20:14:36 -06:00
uverbs_destroy_api ( uapi ) ;
return ERR_PTR ( rc ) ;
}
/*
* The pre version is done before destroying the HW objects , it only blocks
* off method access . All methods that require the ib_dev or the module data
* must test one of these assignments prior to continuing .
*/
void uverbs_disassociate_api_pre ( struct ib_uverbs_device * uverbs_dev )
{
struct uverbs_api * uapi = uverbs_dev - > uapi ;
struct radix_tree_iter iter ;
void __rcu * * slot ;
rcu_assign_pointer ( uverbs_dev - > ib_dev , NULL ) ;
radix_tree_for_each_slot ( slot , & uapi - > radix , & iter , 0 ) {
if ( uapi_key_is_ioctl_method ( iter . index ) ) {
struct uverbs_api_ioctl_method * method_elm =
rcu_dereference_protected ( * slot , true ) ;
if ( method_elm - > driver_method )
rcu_assign_pointer ( method_elm - > handler , NULL ) ;
}
}
synchronize_srcu ( & uverbs_dev - > disassociate_srcu ) ;
}
/*
* Called when a driver disassociates from the ib_uverbs_device . The
* assumption is that the driver module will unload after . Replace everything
* related to the driver with NULL as a safety measure .
*/
void uverbs_disassociate_api ( struct uverbs_api * uapi )
{
struct radix_tree_iter iter ;
void __rcu * * slot ;
radix_tree_for_each_slot ( slot , & uapi - > radix , & iter , 0 ) {
if ( uapi_key_is_object ( iter . index ) ) {
struct uverbs_api_object * object_elm =
rcu_dereference_protected ( * slot , true ) ;
/*
* Some type_attrs are in the driver module . We don ' t
* bother to keep track of which since there should be
* no use of this after disassociate .
*/
object_elm - > type_attrs = NULL ;
} else if ( uapi_key_is_attr ( iter . index ) ) {
struct uverbs_api_attr * elm =
rcu_dereference_protected ( * slot , true ) ;
if ( elm - > spec . type = = UVERBS_ATTR_TYPE_ENUM_IN )
elm - > spec . u2 . enum_def . ids = NULL ;
}
}
}