2017-08-03 16:07:00 +03:00
/*
* Copyright ( c ) 2017 , Mellanox Technologies inc . All rights reserved .
*
* This software is available to you under a choice of one of two
* licenses . You may choose to be licensed under the terms of the GNU
* General Public License ( GPL ) Version 2 , available from the file
* COPYING in the main directory of this source tree , or the
* OpenIB . org BSD license below :
*
* Redistribution and use in source and binary forms , with or
* without modification , are permitted provided that the following
* conditions are met :
*
* - Redistributions of source code must retain the above
* copyright notice , this list of conditions and the following
* disclaimer .
*
* - Redistributions in binary form must reproduce the above
* copyright notice , this list of conditions and the following
* disclaimer in the documentation and / or other materials
* provided with the distribution .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND ,
* EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN
* ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE .
*/
# include <rdma/uverbs_ioctl.h>
# include <rdma/rdma_user_ioctl.h>
# include <linux/bitops.h>
# include "uverbs.h"
# define UVERBS_NUM_NS (UVERBS_ID_NS_MASK >> UVERBS_ID_NS_SHIFT)
# define GET_NS_ID(idx) (((idx) & UVERBS_ID_NS_MASK) >> UVERBS_ID_NS_SHIFT)
# define GET_ID(idx) ((idx) & ~UVERBS_ID_NS_MASK)
# define _for_each_element(elem, tmpi, tmpj, hashes, num_buckets_offset, \
buckets_offset ) \
for ( tmpj = 0 , \
elem = ( * ( const void * * * ) ( ( hashes ) [ tmpi ] + \
( buckets_offset ) ) ) [ 0 ] ; \
tmpj < * ( size_t * ) ( ( hashes ) [ tmpi ] + ( num_buckets_offset ) ) ; \
tmpj + + ) \
if ( ( elem = ( ( * ( const void * * * ) ( hashes [ tmpi ] + \
( buckets_offset ) ) ) [ tmpj ] ) ) )
/*
* Iterate all elements of a few @ hashes . The number of given hashes is
* indicated by @ num_hashes . The offset of the number of buckets in the hash is
* represented by @ num_buckets_offset , while the offset of the buckets array in
* the hash structure is represented by @ buckets_offset . tmpi and tmpj are two
* short ( or int ) based indices that are given by the user . tmpi iterates over
* the different hashes . @ elem points the current element in the hashes [ tmpi ]
* bucket we are looping on . To be honest , @ hashes representation isn ' t exactly
* a hash , but more a collection of elements . These elements ' ids are treated
* in a hash like manner , where the first upper bits are the bucket number .
* These elements are later mapped into a perfect - hash .
*/
# define for_each_element(elem, tmpi, tmpj, hashes, num_hashes, \
num_buckets_offset , buckets_offset ) \
for ( tmpi = 0 ; tmpi < ( num_hashes ) ; tmpi + + ) \
_for_each_element ( elem , tmpi , tmpj , hashes , num_buckets_offset , \
buckets_offset )
# define get_elements_iterators_entry_above(iters, num_elements, elements, \
num_objects_fld , objects_fld , bucket , \
min_id ) \
get_elements_above_id ( ( const void * * ) iters , num_elements , \
( const void * * ) ( elements ) , \
offsetof ( typeof ( * * elements ) , \
num_objects_fld ) , \
offsetof ( typeof ( * * elements ) , objects_fld ) , \
offsetof ( typeof ( * * * ( * elements ) - > objects_fld ) , id ) , \
bucket , min_id )
# define get_objects_above_id(iters, num_trees, trees, bucket, min_id) \
get_elements_iterators_entry_above ( iters , num_trees , trees , \
num_objects , objects , bucket , min_id )
# define get_methods_above_id(method_iters, num_iters, iters, bucket, min_id)\
get_elements_iterators_entry_above ( method_iters , num_iters , iters , \
num_methods , methods , bucket , min_id )
# define get_attrs_above_id(attrs_iters, num_iters, iters, bucket, min_id)\
get_elements_iterators_entry_above ( attrs_iters , num_iters , iters , \
num_attrs , attrs , bucket , min_id )
/*
* get_elements_above_id get a few hashes represented by @ elements and
* @ num_elements . The hashes fields are described by @ num_offset , @ data_offset
* and @ id_offset in the same way as required by for_each_element . The function
* returns an array of @ iters , represents an array of elements in the hashes
* buckets , which their ids are the smallest ids in all hashes but are all
* larger than the id given by min_id . Elements are only added to the iters
* array if their id belongs to the bucket @ bucket . The number of elements in
* the returned array is returned by the function . @ min_id is also updated to
* reflect the new min_id of all elements in iters .
*/
static size_t get_elements_above_id ( const void * * iters ,
unsigned int num_elements ,
const void * * elements ,
size_t num_offset ,
size_t data_offset ,
size_t id_offset ,
u16 bucket ,
short * min_id )
{
size_t num_iters = 0 ;
short min = SHRT_MAX ;
const void * elem ;
int i , j , last_stored = - 1 ;
for_each_element ( elem , i , j , elements , num_elements , num_offset ,
data_offset ) {
u16 id = * ( u16 * ) ( elem + id_offset ) ;
if ( GET_NS_ID ( id ) ! = bucket )
continue ;
if ( GET_ID ( id ) < * min_id | |
( min ! = SHRT_MAX & & GET_ID ( id ) > min ) )
continue ;
/*
* We first iterate all hashes represented by @ elements . When
* we do , we try to find an element @ elem in the bucket @ bucket
* which its id is min . Since we can ' t ensure the user sorted
* the elements in increasing order , we override this hash ' s
* minimal id element we found , if a new element with a smaller
* id was just found .
*/
iters [ last_stored = = i ? num_iters - 1 : num_iters + + ] = elem ;
last_stored = i ;
min = GET_ID ( id ) ;
}
/*
* We only insert to our iters array an element , if its id is smaller
* than all previous ids . Therefore , the final iters array is sorted so
* that smaller ids are in the end of the array .
* Therefore , we need to clean the beginning of the array to make sure
* all ids of final elements are equal to min .
*/
for ( i = num_iters - 1 ; i > = 0 & &
GET_ID ( * ( u16 * ) ( iters [ i ] + id_offset ) ) = = min ; i - - )
;
num_iters - = i + 1 ;
memmove ( iters , iters + i + 1 , sizeof ( * iters ) * num_iters ) ;
* min_id = min ;
return num_iters ;
}
# define find_max_element_entry_id(num_elements, elements, num_objects_fld, \
objects_fld , bucket ) \
find_max_element_id ( num_elements , ( const void * * ) ( elements ) , \
offsetof ( typeof ( * * elements ) , num_objects_fld ) , \
offsetof ( typeof ( * * elements ) , objects_fld ) , \
offsetof ( typeof ( * * * ( * elements ) - > objects_fld ) , id ) , \
bucket )
static short find_max_element_ns_id ( unsigned int num_elements ,
const void * * elements ,
size_t num_offset ,
size_t data_offset ,
size_t id_offset )
{
short max_ns = SHRT_MIN ;
const void * elem ;
int i , j ;
for_each_element ( elem , i , j , elements , num_elements , num_offset ,
data_offset ) {
u16 id = * ( u16 * ) ( elem + id_offset ) ;
if ( GET_NS_ID ( id ) > max_ns )
max_ns = GET_NS_ID ( id ) ;
}
return max_ns ;
}
static short find_max_element_id ( unsigned int num_elements ,
const void * * elements ,
size_t num_offset ,
size_t data_offset ,
size_t id_offset ,
u16 bucket )
{
short max_id = SHRT_MIN ;
const void * elem ;
int i , j ;
for_each_element ( elem , i , j , elements , num_elements , num_offset ,
data_offset ) {
u16 id = * ( u16 * ) ( elem + id_offset ) ;
if ( GET_NS_ID ( id ) = = bucket & &
GET_ID ( id ) > max_id )
max_id = GET_ID ( id ) ;
}
return max_id ;
}
# define find_max_element_entry_id(num_elements, elements, num_objects_fld, \
objects_fld , bucket ) \
find_max_element_id ( num_elements , ( const void * * ) ( elements ) , \
offsetof ( typeof ( * * elements ) , num_objects_fld ) , \
offsetof ( typeof ( * * elements ) , objects_fld ) , \
offsetof ( typeof ( * * * ( * elements ) - > objects_fld ) , id ) , \
bucket )
# define find_max_element_ns_entry_id(num_elements, elements, \
num_objects_fld , objects_fld ) \
find_max_element_ns_id ( num_elements , ( const void * * ) ( elements ) , \
offsetof ( typeof ( * * elements ) , num_objects_fld ) , \
offsetof ( typeof ( * * elements ) , objects_fld ) , \
offsetof ( typeof ( * * * ( * elements ) - > objects_fld ) , id ) )
/*
* find_max_xxxx_ns_id gets a few elements . Each element is described by an id
* which its upper bits represents a namespace . It finds the max namespace . This
* could be used in order to know how many buckets do we need to allocate . If no
* elements exist , SHRT_MIN is returned . Namespace represents here different
* buckets . The common example is " common bucket " and " driver bucket " .
*
* find_max_xxxx_id gets a few elements and a bucket . Each element is described
* by an id which its upper bits represent a namespace . It returns the max id
* which is contained in the same namespace defined in @ bucket . This could be
* used in order to know how many elements do we need to allocate in the bucket .
* If no elements exist , SHRT_MIN is returned .
*/
# define find_max_object_id(num_trees, trees, bucket) \
find_max_element_entry_id ( num_trees , trees , num_objects , \
objects , bucket )
# define find_max_object_ns_id(num_trees, trees) \
find_max_element_ns_entry_id ( num_trees , trees , \
num_objects , objects )
# define find_max_method_id(num_iters, iters, bucket) \
find_max_element_entry_id ( num_iters , iters , num_methods , \
methods , bucket )
# define find_max_method_ns_id(num_iters, iters) \
find_max_element_ns_entry_id ( num_iters , iters , \
num_methods , methods )
# define find_max_attr_id(num_iters, iters, bucket) \
find_max_element_entry_id ( num_iters , iters , num_attrs , \
attrs , bucket )
# define find_max_attr_ns_id(num_iters, iters) \
find_max_element_ns_entry_id ( num_iters , iters , \
num_attrs , attrs )
static void free_method ( struct uverbs_method_spec * method )
{
unsigned int i ;
if ( ! method )
return ;
for ( i = 0 ; i < method - > num_buckets ; i + + )
kfree ( method - > attr_buckets [ i ] ) ;
kfree ( method ) ;
}
# define IS_ATTR_OBJECT(attr) ((attr)->type == UVERBS_ATTR_TYPE_IDR || \
( attr ) - > type = = UVERBS_ATTR_TYPE_FD )
/*
* This function gets array of size @ num_method_defs which contains pointers to
* method definitions @ method_defs . The function allocates an
* uverbs_method_spec structure and initializes its number of buckets and the
* elements in buckets to the correct attributes . While doing that , it
* validates that there aren ' t conflicts between attributes of different
* method_defs .
*/
static struct uverbs_method_spec * build_method_with_attrs ( const struct uverbs_method_def * * method_defs ,
size_t num_method_defs )
{
int bucket_idx ;
int max_attr_buckets = 0 ;
size_t num_attr_buckets = 0 ;
int res = 0 ;
struct uverbs_method_spec * method = NULL ;
const struct uverbs_attr_def * * attr_defs ;
unsigned int num_of_singularities = 0 ;
max_attr_buckets = find_max_attr_ns_id ( num_method_defs , method_defs ) ;
if ( max_attr_buckets > = 0 )
num_attr_buckets = max_attr_buckets + 1 ;
method = kzalloc ( sizeof ( * method ) +
num_attr_buckets * sizeof ( * method - > attr_buckets ) ,
GFP_KERNEL ) ;
if ( ! method )
return ERR_PTR ( - ENOMEM ) ;
method - > num_buckets = num_attr_buckets ;
attr_defs = kcalloc ( num_method_defs , sizeof ( * attr_defs ) , GFP_KERNEL ) ;
if ( ! attr_defs ) {
res = - ENOMEM ;
goto free_method ;
}
for ( bucket_idx = 0 ; bucket_idx < method - > num_buckets ; bucket_idx + + ) {
short min_id = SHRT_MIN ;
int attr_max_bucket = 0 ;
struct uverbs_attr_spec_hash * hash = NULL ;
attr_max_bucket = find_max_attr_id ( num_method_defs , method_defs ,
bucket_idx ) ;
if ( attr_max_bucket < 0 )
continue ;
hash = kzalloc ( sizeof ( * hash ) +
ALIGN ( sizeof ( * hash - > attrs ) * ( attr_max_bucket + 1 ) ,
sizeof ( long ) ) +
BITS_TO_LONGS ( attr_max_bucket ) * sizeof ( long ) ,
GFP_KERNEL ) ;
if ( ! hash ) {
res = - ENOMEM ;
goto free ;
}
hash - > num_attrs = attr_max_bucket + 1 ;
method - > num_child_attrs + = hash - > num_attrs ;
hash - > mandatory_attrs_bitmask = ( void * ) ( hash + 1 ) +
ALIGN ( sizeof ( * hash - > attrs ) *
( attr_max_bucket + 1 ) ,
sizeof ( long ) ) ;
method - > attr_buckets [ bucket_idx ] = hash ;
do {
size_t num_attr_defs ;
struct uverbs_attr_spec * attr ;
bool attr_obj_with_special_access ;
num_attr_defs =
get_attrs_above_id ( attr_defs ,
num_method_defs ,
method_defs ,
bucket_idx ,
& min_id ) ;
/* Last attr in bucket */
if ( ! num_attr_defs )
break ;
if ( num_attr_defs > 1 ) {
/*
* We don ' t allow two attribute definitions for
* the same attribute . This is usually a
* programmer error . If required , it ' s better to
* just add a new attribute to capture the new
* semantics .
*/
res = - EEXIST ;
goto free ;
}
attr = & hash - > attrs [ min_id ] ;
memcpy ( attr , & attr_defs [ 0 ] - > attr , sizeof ( * attr ) ) ;
attr_obj_with_special_access = IS_ATTR_OBJECT ( attr ) & &
( attr - > obj . access = = UVERBS_ACCESS_NEW | |
attr - > obj . access = = UVERBS_ACCESS_DESTROY ) ;
num_of_singularities + = ! ! attr_obj_with_special_access ;
if ( WARN ( num_of_singularities > 1 ,
" ib_uverbs: Method contains more than one object attr (%d) with new/destroy access \n " ,
min_id ) | |
WARN ( attr_obj_with_special_access & &
! ( attr - > flags & UVERBS_ATTR_SPEC_F_MANDATORY ) ,
2017-09-01 09:22:14 +01:00
" ib_uverbs: Tried to merge attr (%d) but it's an object with new/destroy access but isn't mandatory \n " ,
2017-08-03 16:07:00 +03:00
min_id ) | |
WARN ( IS_ATTR_OBJECT ( attr ) & &
attr - > flags & UVERBS_ATTR_SPEC_F_MIN_SZ ,
" ib_uverbs: Tried to merge attr (%d) but it's an object with min_sz flag \n " ,
min_id ) ) {
res = - EINVAL ;
goto free ;
}
if ( attr - > flags & UVERBS_ATTR_SPEC_F_MANDATORY )
set_bit ( min_id , hash - > mandatory_attrs_bitmask ) ;
min_id + + ;
} while ( 1 ) ;
}
kfree ( attr_defs ) ;
return method ;
free :
kfree ( attr_defs ) ;
free_method :
free_method ( method ) ;
return ERR_PTR ( res ) ;
}
static void free_object ( struct uverbs_object_spec * object )
{
unsigned int i , j ;
if ( ! object )
return ;
for ( i = 0 ; i < object - > num_buckets ; i + + ) {
struct uverbs_method_spec_hash * method_buckets =
object - > method_buckets [ i ] ;
if ( ! method_buckets )
continue ;
for ( j = 0 ; j < method_buckets - > num_methods ; j + + )
free_method ( method_buckets - > methods [ j ] ) ;
kfree ( method_buckets ) ;
}
kfree ( object ) ;
}
/*
* This function gets array of size @ num_object_defs which contains pointers to
* object definitions @ object_defs . The function allocated an
* uverbs_object_spec structure and initialize its number of buckets and the
* elements in buckets to the correct methods . While doing that , it
* sorts out the correct relationship between conflicts in the same method .
*/
static struct uverbs_object_spec * build_object_with_methods ( const struct uverbs_object_def * * object_defs ,
size_t num_object_defs )
{
u16 bucket_idx ;
int max_method_buckets = 0 ;
u16 num_method_buckets = 0 ;
int res = 0 ;
struct uverbs_object_spec * object = NULL ;
const struct uverbs_method_def * * method_defs ;
max_method_buckets = find_max_method_ns_id ( num_object_defs , object_defs ) ;
if ( max_method_buckets > = 0 )
num_method_buckets = max_method_buckets + 1 ;
object = kzalloc ( sizeof ( * object ) +
num_method_buckets *
sizeof ( * object - > method_buckets ) , GFP_KERNEL ) ;
if ( ! object )
return ERR_PTR ( - ENOMEM ) ;
object - > num_buckets = num_method_buckets ;
method_defs = kcalloc ( num_object_defs , sizeof ( * method_defs ) , GFP_KERNEL ) ;
if ( ! method_defs ) {
res = - ENOMEM ;
goto free_object ;
}
for ( bucket_idx = 0 ; bucket_idx < object - > num_buckets ; bucket_idx + + ) {
short min_id = SHRT_MIN ;
int methods_max_bucket = 0 ;
struct uverbs_method_spec_hash * hash = NULL ;
methods_max_bucket = find_max_method_id ( num_object_defs , object_defs ,
bucket_idx ) ;
if ( methods_max_bucket < 0 )
continue ;
hash = kzalloc ( sizeof ( * hash ) +
sizeof ( * hash - > methods ) * ( methods_max_bucket + 1 ) ,
GFP_KERNEL ) ;
if ( ! hash ) {
res = - ENOMEM ;
goto free ;
}
hash - > num_methods = methods_max_bucket + 1 ;
object - > method_buckets [ bucket_idx ] = hash ;
do {
size_t num_method_defs ;
struct uverbs_method_spec * method ;
int i ;
num_method_defs =
get_methods_above_id ( method_defs ,
num_object_defs ,
object_defs ,
bucket_idx ,
& min_id ) ;
/* Last method in bucket */
if ( ! num_method_defs )
break ;
method = build_method_with_attrs ( method_defs ,
num_method_defs ) ;
if ( IS_ERR ( method ) ) {
res = PTR_ERR ( method ) ;
goto free ;
}
/*
* The last tree which is given as an argument to the
* merge overrides previous method handler .
* Therefore , we iterate backwards and search for the
* first handler which ! = NULL . This also defines the
* set of flags used for this handler .
*/
for ( i = num_object_defs - 1 ;
i > = 0 & & ! method_defs [ i ] - > handler ; i - - )
;
hash - > methods [ min_id + + ] = method ;
/* NULL handler isn't allowed */
if ( WARN ( i < 0 ,
" ib_uverbs: tried to merge function id %d, but all handlers are NULL \n " ,
min_id ) ) {
res = - EINVAL ;
goto free ;
}
method - > handler = method_defs [ i ] - > handler ;
method - > flags = method_defs [ i ] - > flags ;
} while ( 1 ) ;
}
kfree ( method_defs ) ;
return object ;
free :
kfree ( method_defs ) ;
free_object :
free_object ( object ) ;
return ERR_PTR ( res ) ;
}
void uverbs_free_spec_tree ( struct uverbs_root_spec * root )
{
unsigned int i , j ;
if ( ! root )
return ;
for ( i = 0 ; i < root - > num_buckets ; i + + ) {
struct uverbs_object_spec_hash * object_hash =
root - > object_buckets [ i ] ;
if ( ! object_hash )
continue ;
for ( j = 0 ; j < object_hash - > num_objects ; j + + )
free_object ( object_hash - > objects [ j ] ) ;
kfree ( object_hash ) ;
}
kfree ( root ) ;
}
EXPORT_SYMBOL ( uverbs_free_spec_tree ) ;
struct uverbs_root_spec * uverbs_alloc_spec_tree ( unsigned int num_trees ,
const struct uverbs_object_tree_def * * trees )
{
u16 bucket_idx ;
short max_object_buckets = 0 ;
size_t num_objects_buckets = 0 ;
struct uverbs_root_spec * root_spec = NULL ;
const struct uverbs_object_def * * object_defs ;
int i ;
int res = 0 ;
max_object_buckets = find_max_object_ns_id ( num_trees , trees ) ;
/*
* Devices which don ' t want to support ib_uverbs , should just allocate
* an empty parsing tree . Every user - space command won ' t hit any valid
* entry in the parsing tree and thus will fail .
*/
if ( max_object_buckets > = 0 )
num_objects_buckets = max_object_buckets + 1 ;
root_spec = kzalloc ( sizeof ( * root_spec ) +
num_objects_buckets * sizeof ( * root_spec - > object_buckets ) ,
GFP_KERNEL ) ;
if ( ! root_spec )
return ERR_PTR ( - ENOMEM ) ;
root_spec - > num_buckets = num_objects_buckets ;
object_defs = kcalloc ( num_trees , sizeof ( * object_defs ) ,
GFP_KERNEL ) ;
if ( ! object_defs ) {
res = - ENOMEM ;
goto free_root ;
}
for ( bucket_idx = 0 ; bucket_idx < root_spec - > num_buckets ; bucket_idx + + ) {
short min_id = SHRT_MIN ;
short objects_max_bucket ;
struct uverbs_object_spec_hash * hash = NULL ;
objects_max_bucket = find_max_object_id ( num_trees , trees ,
bucket_idx ) ;
if ( objects_max_bucket < 0 )
continue ;
hash = kzalloc ( sizeof ( * hash ) +
sizeof ( * hash - > objects ) * ( objects_max_bucket + 1 ) ,
GFP_KERNEL ) ;
if ( ! hash ) {
res = - ENOMEM ;
goto free ;
}
hash - > num_objects = objects_max_bucket + 1 ;
root_spec - > object_buckets [ bucket_idx ] = hash ;
do {
size_t num_object_defs ;
struct uverbs_object_spec * object ;
num_object_defs = get_objects_above_id ( object_defs ,
num_trees ,
trees ,
bucket_idx ,
& min_id ) ;
/* Last object in bucket */
if ( ! num_object_defs )
break ;
object = build_object_with_methods ( object_defs ,
num_object_defs ) ;
if ( IS_ERR ( object ) ) {
res = PTR_ERR ( object ) ;
goto free ;
}
/*
* The last tree which is given as an argument to the
* merge overrides previous object ' s type_attrs .
* Therefore , we iterate backwards and search for the
* first type_attrs which ! = NULL .
*/
for ( i = num_object_defs - 1 ;
i > = 0 & & ! object_defs [ i ] - > type_attrs ; i - - )
;
/*
* NULL is a valid type_attrs . It means an object we
* can ' t instantiate ( like DEVICE ) .
*/
object - > type_attrs = i < 0 ? NULL :
object_defs [ i ] - > type_attrs ;
hash - > objects [ min_id + + ] = object ;
} while ( 1 ) ;
}
kfree ( object_defs ) ;
return root_spec ;
free :
kfree ( object_defs ) ;
free_root :
uverbs_free_spec_tree ( root_spec ) ;
return ERR_PTR ( res ) ;
}
EXPORT_SYMBOL ( uverbs_alloc_spec_tree ) ;