IB/uverbs: Remove struct uverbs_root_spec and all supporting code
Everything now uses the uverbs_uapi data structure. Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
This commit is contained in:
parent
3a863577a7
commit
51d0a2b4cf
|
@ -35,7 +35,7 @@ ib_ucm-y := ucm.o
|
|||
|
||||
ib_uverbs-y := uverbs_main.o uverbs_cmd.o uverbs_marshall.o \
|
||||
rdma_core.o uverbs_std_types.o uverbs_ioctl.o \
|
||||
uverbs_ioctl_merge.o uverbs_std_types_cq.o \
|
||||
uverbs_std_types_cq.o \
|
||||
uverbs_std_types_flow_action.o uverbs_std_types_dm.o \
|
||||
uverbs_std_types_mr.o uverbs_std_types_counters.o \
|
||||
uverbs_uapi.o
|
||||
|
|
|
@ -42,51 +42,6 @@
|
|||
#include "core_priv.h"
|
||||
#include "rdma_core.h"
|
||||
|
||||
int uverbs_ns_idx(u16 *id, unsigned int ns_count)
|
||||
{
|
||||
int ret = (*id & UVERBS_ID_NS_MASK) >> UVERBS_ID_NS_SHIFT;
|
||||
|
||||
if (ret >= ns_count)
|
||||
return -EINVAL;
|
||||
|
||||
*id &= ~UVERBS_ID_NS_MASK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct uverbs_object_spec *uverbs_get_object(struct ib_uverbs_file *ufile,
|
||||
uint16_t object)
|
||||
{
|
||||
const struct uverbs_root_spec *object_hash = ufile->device->specs_root;
|
||||
const struct uverbs_object_spec_hash *objects;
|
||||
int ret = uverbs_ns_idx(&object, object_hash->num_buckets);
|
||||
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
|
||||
objects = object_hash->object_buckets[ret];
|
||||
|
||||
if (object >= objects->num_objects)
|
||||
return NULL;
|
||||
|
||||
return objects->objects[object];
|
||||
}
|
||||
|
||||
const struct uverbs_method_spec *uverbs_get_method(const struct uverbs_object_spec *object,
|
||||
uint16_t method)
|
||||
{
|
||||
const struct uverbs_method_spec_hash *methods;
|
||||
int ret = uverbs_ns_idx(&method, object->num_buckets);
|
||||
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
|
||||
methods = object->method_buckets[ret];
|
||||
if (method >= methods->num_methods)
|
||||
return NULL;
|
||||
|
||||
return methods->methods[method];
|
||||
}
|
||||
|
||||
void uverbs_uobject_get(struct ib_uobject *uobject)
|
||||
{
|
||||
kref_get(&uobject->ref);
|
||||
|
|
|
@ -45,12 +45,6 @@
|
|||
|
||||
struct ib_uverbs_device;
|
||||
|
||||
int uverbs_ns_idx(u16 *id, unsigned int ns_count);
|
||||
const struct uverbs_object_spec *uverbs_get_object(struct ib_uverbs_file *ufile,
|
||||
uint16_t object);
|
||||
const struct uverbs_method_spec *uverbs_get_method(const struct uverbs_object_spec *object,
|
||||
uint16_t method);
|
||||
|
||||
void uverbs_destroy_ufile_hw(struct ib_uverbs_file *ufile,
|
||||
enum rdma_remove_reason reason);
|
||||
|
||||
|
|
|
@ -111,7 +111,6 @@ struct ib_uverbs_device {
|
|||
struct mutex lists_mutex; /* protect lists */
|
||||
struct list_head uverbs_file_list;
|
||||
struct list_head uverbs_events_file_list;
|
||||
struct uverbs_root_spec *specs_root;
|
||||
struct uverbs_api *uapi;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,662 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
unsigned int equal_min = 0;
|
||||
|
||||
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;
|
||||
if (min == GET_ID(id))
|
||||
equal_min++;
|
||||
else
|
||||
equal_min = 1;
|
||||
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.
|
||||
*/
|
||||
memmove(iters, iters + num_iters - equal_min, sizeof(*iters) * equal_min);
|
||||
|
||||
*min_id = min;
|
||||
return equal_min;
|
||||
}
|
||||
|
||||
#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(struct_size(method, attr_buckets, num_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 + 1) * 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->u.obj.access == UVERBS_ACCESS_NEW ||
|
||||
attr->u.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->mandatory,
|
||||
"ib_uverbs: Tried to merge attr (%d) but it's an object with new/destroy access but isn't mandatory\n",
|
||||
min_id) ||
|
||||
WARN(IS_ATTR_OBJECT(attr) &&
|
||||
attr->zero_trailing,
|
||||
"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->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(struct_size(object, method_buckets,
|
||||
num_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(struct_size(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_method_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);
|
||||
}
|
||||
|
||||
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(struct_size(root_spec, object_buckets,
|
||||
num_objects_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(struct_size(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);
|
||||
}
|
|
@ -176,7 +176,6 @@ static void ib_uverbs_release_dev(struct kobject *kobj)
|
|||
|
||||
uverbs_destroy_api(dev->uapi);
|
||||
cleanup_srcu_struct(&dev->disassociate_srcu);
|
||||
uverbs_free_spec_tree(dev->specs_root);
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
|
@ -998,37 +997,12 @@ static CLASS_ATTR_STRING(abi_version, S_IRUGO,
|
|||
static int ib_uverbs_create_uapi(struct ib_device *device,
|
||||
struct ib_uverbs_device *uverbs_dev)
|
||||
{
|
||||
const struct uverbs_object_tree_def **specs;
|
||||
struct uverbs_root_spec *specs_root;
|
||||
unsigned int num_specs = 1;
|
||||
struct uverbs_api *uapi;
|
||||
unsigned int i;
|
||||
|
||||
if (device->driver_specs)
|
||||
for (i = 0; device->driver_specs[i]; i++)
|
||||
num_specs++;
|
||||
|
||||
specs = kmalloc_array(num_specs, sizeof(*specs), GFP_KERNEL);
|
||||
if (!specs)
|
||||
return -ENOMEM;
|
||||
|
||||
specs[0] = uverbs_default_get_objects();
|
||||
if (device->driver_specs)
|
||||
for (i = 0; device->driver_specs[i]; i++)
|
||||
specs[i+1] = device->driver_specs[i];
|
||||
|
||||
specs_root = uverbs_alloc_spec_tree(num_specs, specs);
|
||||
kfree(specs);
|
||||
if (IS_ERR(specs_root))
|
||||
return PTR_ERR(specs_root);
|
||||
|
||||
uapi = uverbs_alloc_api(device->driver_specs, device->driver_id);
|
||||
if (IS_ERR(uapi)) {
|
||||
uverbs_free_spec_tree(specs_root);
|
||||
if (IS_ERR(uapi))
|
||||
return PTR_ERR(uapi);
|
||||
}
|
||||
|
||||
uverbs_dev->specs_root = specs_root;
|
||||
uverbs_dev->uapi = uapi;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -114,46 +114,6 @@ struct uverbs_attr_spec {
|
|||
} u2;
|
||||
};
|
||||
|
||||
struct uverbs_attr_spec_hash {
|
||||
size_t num_attrs;
|
||||
unsigned long *mandatory_attrs_bitmask;
|
||||
struct uverbs_attr_spec attrs[0];
|
||||
};
|
||||
|
||||
struct uverbs_attr_bundle;
|
||||
struct ib_uverbs_file;
|
||||
|
||||
struct uverbs_method_spec {
|
||||
/* Combination of bits from enum UVERBS_ACTION_FLAG_XXXX */
|
||||
u32 flags;
|
||||
size_t num_buckets;
|
||||
size_t num_child_attrs;
|
||||
int (*handler)(struct ib_uverbs_file *ufile,
|
||||
struct uverbs_attr_bundle *ctx);
|
||||
struct uverbs_attr_spec_hash *attr_buckets[0];
|
||||
};
|
||||
|
||||
struct uverbs_method_spec_hash {
|
||||
size_t num_methods;
|
||||
struct uverbs_method_spec *methods[0];
|
||||
};
|
||||
|
||||
struct uverbs_object_spec {
|
||||
const struct uverbs_obj_type *type_attrs;
|
||||
size_t num_buckets;
|
||||
struct uverbs_method_spec_hash *method_buckets[0];
|
||||
};
|
||||
|
||||
struct uverbs_object_spec_hash {
|
||||
size_t num_objects;
|
||||
struct uverbs_object_spec *objects[0];
|
||||
};
|
||||
|
||||
struct uverbs_root_spec {
|
||||
size_t num_buckets;
|
||||
struct uverbs_object_spec_hash *object_buckets[0];
|
||||
};
|
||||
|
||||
/*
|
||||
* Information about the API is loaded into a radix tree. For IOCTL we start
|
||||
* with a tuple of:
|
||||
|
@ -673,55 +633,4 @@ static inline __malloc void *uverbs_zalloc(struct uverbs_attr_bundle *bundle,
|
|||
}
|
||||
#endif
|
||||
|
||||
/* =================================================
|
||||
* Definitions -> Specs infrastructure
|
||||
* =================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* uverbs_alloc_spec_tree - Merges different common and driver specific feature
|
||||
* into one parsing tree that every uverbs command will be parsed upon.
|
||||
*
|
||||
* @num_trees: Number of trees in the array @trees.
|
||||
* @trees: Array of pointers to tree root definitions to merge. Each such tree
|
||||
* possibly contains objects, methods and attributes definitions.
|
||||
*
|
||||
* Returns:
|
||||
* uverbs_root_spec *: The root of the merged parsing tree.
|
||||
* On error, we return an error code. Error is checked via IS_ERR.
|
||||
*
|
||||
* The following merges could take place:
|
||||
* a. Two trees representing the same method with different handler
|
||||
* -> We take the handler of the tree that its handler != NULL
|
||||
* and its index in the trees array is greater. The incentive for that
|
||||
* is that developers are expected to first merge common trees and then
|
||||
* merge trees that gives specialized the behaviour.
|
||||
* b. Two trees representing the same object with different
|
||||
* type_attrs (struct uverbs_obj_type):
|
||||
* -> We take the type_attrs of the tree that its type_attr != NULL
|
||||
* and its index in the trees array is greater. This could be used
|
||||
* in order to override the free function, allocation size, etc.
|
||||
* c. Two trees representing the same method attribute (same id but possibly
|
||||
* different attributes):
|
||||
* -> ERROR (-ENOENT), we believe that's not the programmer's intent.
|
||||
*
|
||||
* An object without any methods is considered invalid and will abort the
|
||||
* function with -ENOENT error.
|
||||
*/
|
||||
#if IS_ENABLED(CONFIG_INFINIBAND_USER_ACCESS)
|
||||
struct uverbs_root_spec *uverbs_alloc_spec_tree(unsigned int num_trees,
|
||||
const struct uverbs_object_tree_def **trees);
|
||||
void uverbs_free_spec_tree(struct uverbs_root_spec *root);
|
||||
#else
|
||||
static inline struct uverbs_root_spec *uverbs_alloc_spec_tree(unsigned int num_trees,
|
||||
const struct uverbs_object_tree_def **trees)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void uverbs_free_spec_tree(struct uverbs_root_spec *root)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue