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"
2018-11-25 20:58:45 +02:00
static int ib_uverbs_notsupp ( struct uverbs_attr_bundle * attrs )
2018-11-12 22:59:55 +02:00
{
return - EOPNOTSUPP ;
}
2018-08-09 20:14:36 -06:00
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-11-12 22:59:58 +02:00
static int uapi_create_write ( struct uverbs_api * uapi ,
struct ib_device * ibdev ,
const struct uapi_definition * def ,
u32 obj_key ,
u32 * cur_method_key )
2018-11-12 22:59:55 +02:00
{
struct uverbs_api_write_method * method_elm ;
u32 method_key = obj_key ;
bool exists ;
if ( def - > write . is_ex )
method_key | = uapi_key_write_ex_method ( def - > write . command_num ) ;
else
method_key | = uapi_key_write_method ( def - > write . command_num ) ;
method_elm = uapi_add_get_elm ( uapi , method_key , sizeof ( * method_elm ) ,
& exists ) ;
if ( IS_ERR ( method_elm ) )
return PTR_ERR ( method_elm ) ;
2018-11-25 20:58:45 +02:00
if ( WARN_ON ( exists & & ( def - > write . is_ex ! = method_elm - > is_ex ) ) )
2018-11-12 22:59:55 +02:00
return - EINVAL ;
method_elm - > is_ex = def - > write . is_ex ;
2018-11-25 20:58:45 +02:00
method_elm - > handler = def - > func_write ;
if ( def - > write . is_ex )
2018-11-12 22:59:55 +02:00
method_elm - > disabled = ! ( ibdev - > uverbs_ex_cmd_mask &
BIT_ULL ( def - > write . command_num ) ) ;
2018-11-25 20:58:45 +02:00
else
2018-11-12 22:59:55 +02:00
method_elm - > disabled = ! ( ibdev - > uverbs_cmd_mask &
BIT_ULL ( def - > write . command_num ) ) ;
2018-11-12 22:59:58 +02:00
2018-11-25 20:51:16 +02:00
if ( ! def - > write . is_ex & & def - > func_write ) {
method_elm - > has_udata = def - > write . has_udata ;
method_elm - > has_resp = def - > write . has_resp ;
method_elm - > req_size = def - > write . req_size ;
method_elm - > resp_size = def - > write . resp_size ;
}
2018-11-12 22:59:58 +02:00
* cur_method_key = method_key ;
2018-11-12 22:59:55 +02:00
return 0 ;
}
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-26 08:28:34 +02:00
obj_elm - > id = obj - > id ;
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 ;
/*
2019-01-22 08:29:56 +02:00
* Today drivers are only permitted to use idr_class and
* fd_class types . We can revoke the IDR types during
* disassociation , and the FD types require the driver to use
* struct file_operations . owner to prevent the driver module
* code from unloading while the file is open . This provides
* enough safety that uverbs_close_fd ( ) will continue to work .
* Drivers using FD are responsible to handle disassociation of
* the device on their own .
2018-11-12 22:59:51 +02:00
*/
if ( WARN_ON ( is_driver & &
2019-01-22 08:29:56 +02:00
obj - > type_attrs - > type_class ! = & uverbs_idr_class & &
obj - > type_attrs - > type_class ! = & uverbs_fd_class ) )
2018-11-12 22:59:51 +02:00
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 ,
2018-11-12 22:59:58 +02:00
u32 obj_key ,
u32 method_key )
2018-11-12 22:59:52 +02:00
{
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 ;
}
2018-11-12 22:59:58 +02:00
if ( def - > scope = = UAPI_SCOPE_METHOD & &
uapi_key_is_ioctl_method ( method_key ) ) {
struct uverbs_api_ioctl_method * method_elm ;
method_elm = uapi_add_get_elm ( uapi , method_key ,
sizeof ( * method_elm ) , & exists ) ;
if ( IS_ERR ( method_elm ) )
return PTR_ERR ( method_elm ) ;
method_elm - > disabled = 1 ;
return 0 ;
}
if ( def - > scope = = UAPI_SCOPE_METHOD & &
( uapi_key_is_write_method ( method_key ) | |
uapi_key_is_write_ex_method ( method_key ) ) ) {
struct uverbs_api_write_method * write_elm ;
write_elm = uapi_add_get_elm ( uapi , method_key ,
sizeof ( * write_elm ) , & exists ) ;
if ( IS_ERR ( write_elm ) )
return PTR_ERR ( write_elm ) ;
write_elm - > disabled = 1 ;
return 0 ;
}
2018-11-12 22:59:52 +02:00
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:58 +02:00
u32 cur_method_key = UVERBS_API_KEY_ERR ;
2018-11-12 22:59:55 +02:00
bool exists ;
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 : {
2018-12-10 21:09:48 +02:00
void * * ibdev_fn =
( void * ) ( & ibdev - > ops ) + def - > needs_fn_offset ;
2018-11-12 22:59:52 +02:00
if ( * ibdev_fn )
continue ;
2018-11-12 22:59:58 +02:00
rc = uapi_disable_elm (
uapi , def , cur_obj_key , cur_method_key ) ;
2018-11-12 22:59:52 +02:00
if ( rc )
return rc ;
continue ;
}
case UAPI_DEF_IS_SUPPORTED_FUNC :
if ( def - > func_is_supported ( ibdev ) )
continue ;
2018-11-12 22:59:58 +02:00
rc = uapi_disable_elm (
uapi , def , cur_obj_key , cur_method_key ) ;
2018-11-12 22:59:52 +02:00
if ( rc )
return rc ;
continue ;
2018-11-12 22:59:55 +02:00
case UAPI_DEF_OBJECT_START : {
struct uverbs_api_object * obj_elm ;
cur_obj_key = uapi_key_obj ( def - > object_start . object_id ) ;
obj_elm = uapi_add_get_elm ( uapi , cur_obj_key ,
sizeof ( * obj_elm ) , & exists ) ;
if ( IS_ERR ( obj_elm ) )
return PTR_ERR ( obj_elm ) ;
continue ;
}
case UAPI_DEF_WRITE :
2018-11-12 22:59:58 +02:00
rc = uapi_create_write (
uapi , ibdev , def , cur_obj_key , & cur_method_key ) ;
2018-11-12 22:59:55 +02:00
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 ;
2018-11-12 22:59:55 +02:00
if ( uapi_key_attr_to_ioctl_method ( iter . index ) ! =
uapi_key_attr_to_ioctl_method ( method_key ) )
2018-08-09 20:14:36 -06:00
break ;
if ( elm - > spec . mandatory )
__set_bit ( attr_bkey , method_elm - > attr_mandatory ) ;
2018-11-25 20:51:20 +02:00
if ( elm - > spec . is_udata )
method_elm - > has_udata = true ;
2018-08-09 20:14:36 -06:00
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 )
{
2018-11-12 22:59:55 +02:00
const struct uverbs_api_write_method * * data ;
unsigned long max_write_ex = 0 ;
unsigned long max_write = 0 ;
2018-08-09 20:14:36 -06:00
struct radix_tree_iter iter ;
void __rcu * * slot ;
int rc ;
2018-11-12 22:59:55 +02:00
int i ;
2018-08-09 20:14:36 -06:00
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 ;
}
2018-11-12 22:59:55 +02:00
if ( uapi_key_is_write_method ( iter . index ) )
max_write = max ( max_write ,
iter . index & UVERBS_API_ATTR_KEY_MASK ) ;
if ( uapi_key_is_write_ex_method ( iter . index ) )
max_write_ex =
max ( max_write_ex ,
iter . index & UVERBS_API_ATTR_KEY_MASK ) ;
}
uapi - > notsupp_method . handler = ib_uverbs_notsupp ;
uapi - > num_write = max_write + 1 ;
uapi - > num_write_ex = max_write_ex + 1 ;
data = kmalloc_array ( uapi - > num_write + uapi - > num_write_ex ,
sizeof ( * uapi - > write_methods ) , GFP_KERNEL ) ;
for ( i = 0 ; i ! = uapi - > num_write + uapi - > num_write_ex ; i + + )
data [ i ] = & uapi - > notsupp_method ;
uapi - > write_methods = data ;
uapi - > write_ex_methods = data + uapi - > num_write ;
radix_tree_for_each_slot ( slot , & uapi - > radix , & iter , 0 ) {
if ( uapi_key_is_write_method ( iter . index ) )
uapi - > write_methods [ iter . index &
UVERBS_API_ATTR_KEY_MASK ] =
rcu_dereference_protected ( * slot , true ) ;
if ( uapi_key_is_write_ex_method ( iter . index ) )
uapi - > write_ex_methods [ iter . index &
UVERBS_API_ATTR_KEY_MASK ] =
rcu_dereference_protected ( * slot , true ) ;
2018-08-09 20:14:36 -06:00
}
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 ;
}
2018-11-12 22:59:55 +02:00
static void uapi_key_okay ( u32 key )
{
unsigned int count = 0 ;
if ( uapi_key_is_object ( key ) )
count + + ;
if ( uapi_key_is_ioctl_method ( key ) )
count + + ;
if ( uapi_key_is_write_method ( key ) )
count + + ;
if ( uapi_key_is_write_ex_method ( key ) )
count + + ;
if ( uapi_key_is_attr ( key ) )
count + + ;
WARN ( count ! = 1 , " Bad count %d key=%x " , count , key ) ;
}
2018-11-12 22:59:52 +02:00
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 ) {
2018-11-12 22:59:55 +02:00
uapi_key_okay ( iter . index ) ;
2018-11-12 22:59:52 +02:00
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 ;
}
2018-11-12 22:59:55 +02:00
if ( uapi_key_is_write_method ( iter . index ) | |
uapi_key_is_write_ex_method ( iter . index ) ) {
struct uverbs_api_write_method * method_elm =
rcu_dereference_protected ( * slot , true ) ;
if ( method_elm - > disabled ) {
kfree ( method_elm ) ;
radix_tree_iter_delete ( & uapi - > radix , & iter , slot ) ;
}
continue ;
}
2018-11-12 22:59:52 +02:00
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 ) ;
2018-11-26 08:28:33 +02:00
if ( IS_ERR ( tmp_obj ) ) {
if ( PTR_ERR ( tmp_obj ) = = - ENOMSG )
continue ;
} else {
if ( ! tmp_obj - > disabled )
continue ;
}
2018-11-12 22:59:52 +02:00
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-11-12 22:59:55 +02:00
kfree ( uapi - > write_methods ) ;
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 [ ] = {
2018-11-12 22:59:54 +02:00
UAPI_DEF_CHAIN ( uverbs_def_obj_counters ) ,
UAPI_DEF_CHAIN ( uverbs_def_obj_cq ) ,
2018-11-30 13:06:21 +02:00
UAPI_DEF_CHAIN ( uverbs_def_obj_device ) ,
2018-11-12 22:59:54 +02:00
UAPI_DEF_CHAIN ( uverbs_def_obj_dm ) ,
UAPI_DEF_CHAIN ( uverbs_def_obj_flow_action ) ,
2018-11-12 22:59:50 +02:00
UAPI_DEF_CHAIN ( uverbs_def_obj_intf ) ,
2018-11-12 22:59:54 +02:00
UAPI_DEF_CHAIN ( uverbs_def_obj_mr ) ,
2018-11-12 22:59:56 +02:00
UAPI_DEF_CHAIN ( uverbs_def_write_intf ) ,
2018-11-12 22:59:50 +02:00
{ } ,
} ;
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 ;
}
}
}