Commit 6884c6c4 authored by Jason Gunthorpe's avatar Jason Gunthorpe

RDMA/verbs: Store the write/write_ex uapi entry points in the uverbs_api

Bringing all uapi entry points into one place lets us deal with them
consistently. For instance the write, write_ex and ioctl paths can be
disabled when an API is not supported by the driver.

This will replace the uverbs_cmd_table static arrays.
Signed-off-by: default avatarJason Gunthorpe <jgg@mellanox.com>
Signed-off-by: default avatarLeon Romanovsky <leonro@mellanox.com>
parent 0bd01f3d
...@@ -136,6 +136,15 @@ struct uverbs_api_ioctl_method { ...@@ -136,6 +136,15 @@ struct uverbs_api_ioctl_method {
u8 destroy_bkey; u8 destroy_bkey;
}; };
struct uverbs_api_write_method {
ssize_t (*handler)(struct ib_uverbs_file *file, const char __user *buf,
int in_len, int out_len);
int (*handler_ex)(struct ib_uverbs_file *file, struct ib_udata *ucore,
struct ib_udata *uhw);
u8 disabled:1;
u8 is_ex:1;
};
struct uverbs_api_attr { struct uverbs_api_attr {
struct uverbs_attr_spec spec; struct uverbs_attr_spec spec;
}; };
...@@ -144,6 +153,12 @@ struct uverbs_api { ...@@ -144,6 +153,12 @@ struct uverbs_api {
/* radix tree contains struct uverbs_api_* pointers */ /* radix tree contains struct uverbs_api_* pointers */
struct radix_tree_root radix; struct radix_tree_root radix;
enum rdma_driver_id driver_id; enum rdma_driver_id driver_id;
unsigned int num_write;
unsigned int num_write_ex;
struct uverbs_api_write_method notsupp_method;
const struct uverbs_api_write_method **write_methods;
const struct uverbs_api_write_method **write_ex_methods;
}; };
static inline const struct uverbs_api_object * static inline const struct uverbs_api_object *
......
...@@ -8,6 +8,19 @@ ...@@ -8,6 +8,19 @@
#include "rdma_core.h" #include "rdma_core.h"
#include "uverbs.h" #include "uverbs.h"
static ssize_t ib_uverbs_notsupp(struct ib_uverbs_file *file,
const char __user *buf, int in_len,
int out_len)
{
return -EOPNOTSUPP;
}
static int ib_uverbs_ex_notsupp(struct ib_uverbs_file *file,
struct ib_udata *ucore, struct ib_udata *uhw)
{
return -EOPNOTSUPP;
}
static void *uapi_add_elm(struct uverbs_api *uapi, u32 key, size_t alloc_size) static void *uapi_add_elm(struct uverbs_api *uapi, u32 key, size_t alloc_size)
{ {
void *elm; void *elm;
...@@ -47,6 +60,42 @@ static void *uapi_add_get_elm(struct uverbs_api *uapi, u32 key, ...@@ -47,6 +60,42 @@ static void *uapi_add_get_elm(struct uverbs_api *uapi, u32 key,
return elm; return elm;
} }
static int uapi_create_write(struct uverbs_api *uapi, struct ib_device *ibdev,
const struct uapi_definition *def, u32 obj_key)
{
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);
if (WARN_ON(exists && (def->write.is_ex != method_elm->is_ex ||
method_elm->handler_ex || method_elm->handler)))
return -EINVAL;
method_elm->is_ex = def->write.is_ex;
if (def->write.is_ex) {
method_elm->handler_ex = def->func_write_ex;
method_elm->disabled = !(ibdev->uverbs_ex_cmd_mask &
BIT_ULL(def->write.command_num));
} else {
method_elm->handler = def->func_write;
method_elm->disabled = !(ibdev->uverbs_cmd_mask &
BIT_ULL(def->write.command_num));
}
return 0;
}
static int uapi_merge_method(struct uverbs_api *uapi, static int uapi_merge_method(struct uverbs_api *uapi,
struct uverbs_api_object *obj_elm, u32 obj_key, struct uverbs_api_object *obj_elm, u32 obj_key,
const struct uverbs_method_def *method, const struct uverbs_method_def *method,
...@@ -194,6 +243,7 @@ static int uapi_merge_def(struct uverbs_api *uapi, struct ib_device *ibdev, ...@@ -194,6 +243,7 @@ static int uapi_merge_def(struct uverbs_api *uapi, struct ib_device *ibdev,
{ {
const struct uapi_definition *def = def_list; const struct uapi_definition *def = def_list;
u32 cur_obj_key = UVERBS_API_KEY_ERR; u32 cur_obj_key = UVERBS_API_KEY_ERR;
bool exists;
int rc; int rc;
if (!def_list) if (!def_list)
...@@ -240,6 +290,23 @@ static int uapi_merge_def(struct uverbs_api *uapi, struct ib_device *ibdev, ...@@ -240,6 +290,23 @@ static int uapi_merge_def(struct uverbs_api *uapi, struct ib_device *ibdev,
if (rc) if (rc)
return rc; return rc;
continue; continue;
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:
rc = uapi_create_write(uapi, ibdev, def, cur_obj_key);
if (rc)
return rc;
continue;
} }
WARN_ON(true); WARN_ON(true);
return -EINVAL; return -EINVAL;
...@@ -266,8 +333,8 @@ uapi_finalize_ioctl_method(struct uverbs_api *uapi, ...@@ -266,8 +333,8 @@ uapi_finalize_ioctl_method(struct uverbs_api *uapi,
u32 attr_bkey = uapi_bkey_attr(attr_key); u32 attr_bkey = uapi_bkey_attr(attr_key);
u8 type = elm->spec.type; u8 type = elm->spec.type;
if (uapi_key_attr_to_method(iter.index) != if (uapi_key_attr_to_ioctl_method(iter.index) !=
uapi_key_attr_to_method(method_key)) uapi_key_attr_to_ioctl_method(method_key))
break; break;
if (elm->spec.mandatory) if (elm->spec.mandatory)
...@@ -309,9 +376,13 @@ uapi_finalize_ioctl_method(struct uverbs_api *uapi, ...@@ -309,9 +376,13 @@ uapi_finalize_ioctl_method(struct uverbs_api *uapi,
static int uapi_finalize(struct uverbs_api *uapi) static int uapi_finalize(struct uverbs_api *uapi)
{ {
const struct uverbs_api_write_method **data;
unsigned long max_write_ex = 0;
unsigned long max_write = 0;
struct radix_tree_iter iter; struct radix_tree_iter iter;
void __rcu **slot; void __rcu **slot;
int rc; int rc;
int i;
radix_tree_for_each_slot (slot, &uapi->radix, &iter, 0) { radix_tree_for_each_slot (slot, &uapi->radix, &iter, 0) {
struct uverbs_api_ioctl_method *method_elm = struct uverbs_api_ioctl_method *method_elm =
...@@ -323,6 +394,36 @@ static int uapi_finalize(struct uverbs_api *uapi) ...@@ -323,6 +394,36 @@ static int uapi_finalize(struct uverbs_api *uapi)
if (rc) if (rc)
return rc; return rc;
} }
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->notsupp_method.handler_ex = ib_uverbs_ex_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);
} }
return 0; return 0;
...@@ -365,6 +466,23 @@ static u32 uapi_get_obj_id(struct uverbs_attr_spec *spec) ...@@ -365,6 +466,23 @@ static u32 uapi_get_obj_id(struct uverbs_attr_spec *spec)
return UVERBS_API_KEY_ERR; return UVERBS_API_KEY_ERR;
} }
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);
}
static void uapi_finalize_disable(struct uverbs_api *uapi) static void uapi_finalize_disable(struct uverbs_api *uapi)
{ {
struct radix_tree_iter iter; struct radix_tree_iter iter;
...@@ -374,6 +492,8 @@ static void uapi_finalize_disable(struct uverbs_api *uapi) ...@@ -374,6 +492,8 @@ static void uapi_finalize_disable(struct uverbs_api *uapi)
again: again:
radix_tree_for_each_slot (slot, &uapi->radix, &iter, starting_key) { radix_tree_for_each_slot (slot, &uapi->radix, &iter, starting_key) {
uapi_key_okay(iter.index);
if (uapi_key_is_object(iter.index)) { if (uapi_key_is_object(iter.index)) {
struct uverbs_api_object *obj_elm = struct uverbs_api_object *obj_elm =
rcu_dereference_protected(*slot, true); rcu_dereference_protected(*slot, true);
...@@ -400,6 +520,18 @@ static void uapi_finalize_disable(struct uverbs_api *uapi) ...@@ -400,6 +520,18 @@ static void uapi_finalize_disable(struct uverbs_api *uapi)
continue; continue;
} }
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;
}
if (uapi_key_is_attr(iter.index)) { if (uapi_key_is_attr(iter.index)) {
struct uverbs_api_attr *attr_elm = struct uverbs_api_attr *attr_elm =
rcu_dereference_protected(*slot, true); rcu_dereference_protected(*slot, true);
...@@ -444,6 +576,7 @@ void uverbs_destroy_api(struct uverbs_api *uapi) ...@@ -444,6 +576,7 @@ void uverbs_destroy_api(struct uverbs_api *uapi)
return; return;
uapi_remove_range(uapi, 0, U32_MAX); uapi_remove_range(uapi, 0, U32_MAX);
kfree(uapi->write_methods);
kfree(uapi); kfree(uapi);
} }
......
...@@ -140,6 +140,13 @@ struct uverbs_attr_spec { ...@@ -140,6 +140,13 @@ struct uverbs_attr_spec {
* *
* The tree encodes multiple types, and uses a scheme where OBJ_ID,0,0 returns * The tree encodes multiple types, and uses a scheme where OBJ_ID,0,0 returns
* the object slot, and OBJ_ID,METH_ID,0 and returns the method slot. * the object slot, and OBJ_ID,METH_ID,0 and returns the method slot.
*
* This also encodes the tables for the write() and write() extended commands
* using the coding
* OBJ_ID,UVERBS_API_METHOD_IS_WRITE,command #
* OBJ_ID,UVERBS_API_METHOD_IS_WRITE_EX,command_ex #
* ie the WRITE path is treated as a special method type in the ioctl
* framework.
*/ */
enum uapi_radix_data { enum uapi_radix_data {
UVERBS_API_NS_FLAG = 1U << UVERBS_ID_NS_SHIFT, UVERBS_API_NS_FLAG = 1U << UVERBS_ID_NS_SHIFT,
...@@ -147,12 +154,16 @@ enum uapi_radix_data { ...@@ -147,12 +154,16 @@ enum uapi_radix_data {
UVERBS_API_ATTR_KEY_BITS = 6, UVERBS_API_ATTR_KEY_BITS = 6,
UVERBS_API_ATTR_KEY_MASK = GENMASK(UVERBS_API_ATTR_KEY_BITS - 1, 0), UVERBS_API_ATTR_KEY_MASK = GENMASK(UVERBS_API_ATTR_KEY_BITS - 1, 0),
UVERBS_API_ATTR_BKEY_LEN = (1 << UVERBS_API_ATTR_KEY_BITS) - 1, UVERBS_API_ATTR_BKEY_LEN = (1 << UVERBS_API_ATTR_KEY_BITS) - 1,
UVERBS_API_WRITE_KEY_NUM = 1 << UVERBS_API_ATTR_KEY_BITS,
UVERBS_API_METHOD_KEY_BITS = 5, UVERBS_API_METHOD_KEY_BITS = 5,
UVERBS_API_METHOD_KEY_SHIFT = UVERBS_API_ATTR_KEY_BITS, UVERBS_API_METHOD_KEY_SHIFT = UVERBS_API_ATTR_KEY_BITS,
UVERBS_API_METHOD_KEY_NUM_CORE = 24, UVERBS_API_METHOD_KEY_NUM_CORE = 22,
UVERBS_API_METHOD_KEY_NUM_DRIVER = (1 << UVERBS_API_METHOD_KEY_BITS) - UVERBS_API_METHOD_IS_WRITE = 30 << UVERBS_API_METHOD_KEY_SHIFT,
UVERBS_API_METHOD_KEY_NUM_CORE, UVERBS_API_METHOD_IS_WRITE_EX = 31 << UVERBS_API_METHOD_KEY_SHIFT,
UVERBS_API_METHOD_KEY_NUM_DRIVER =
(UVERBS_API_METHOD_IS_WRITE >> UVERBS_API_METHOD_KEY_SHIFT) -
UVERBS_API_METHOD_KEY_NUM_CORE,
UVERBS_API_METHOD_KEY_MASK = GENMASK( UVERBS_API_METHOD_KEY_MASK = GENMASK(
UVERBS_API_METHOD_KEY_BITS + UVERBS_API_METHOD_KEY_SHIFT - 1, UVERBS_API_METHOD_KEY_BITS + UVERBS_API_METHOD_KEY_SHIFT - 1,
UVERBS_API_METHOD_KEY_SHIFT), UVERBS_API_METHOD_KEY_SHIFT),
...@@ -205,7 +216,22 @@ static inline __attribute_const__ u32 uapi_key_ioctl_method(u32 id) ...@@ -205,7 +216,22 @@ static inline __attribute_const__ u32 uapi_key_ioctl_method(u32 id)
return id << UVERBS_API_METHOD_KEY_SHIFT; return id << UVERBS_API_METHOD_KEY_SHIFT;
} }
static inline __attribute_const__ u32 uapi_key_attr_to_method(u32 attr_key) static inline __attribute_const__ u32 uapi_key_write_method(u32 id)
{
if (id >= UVERBS_API_WRITE_KEY_NUM)
return UVERBS_API_KEY_ERR;
return UVERBS_API_METHOD_IS_WRITE | id;
}
static inline __attribute_const__ u32 uapi_key_write_ex_method(u32 id)
{
if (id >= UVERBS_API_WRITE_KEY_NUM)
return UVERBS_API_KEY_ERR;
return UVERBS_API_METHOD_IS_WRITE_EX | id;
}
static inline __attribute_const__ u32
uapi_key_attr_to_ioctl_method(u32 attr_key)
{ {
return attr_key & return attr_key &
(UVERBS_API_OBJ_KEY_MASK | UVERBS_API_METHOD_KEY_MASK); (UVERBS_API_OBJ_KEY_MASK | UVERBS_API_METHOD_KEY_MASK);
...@@ -213,10 +239,23 @@ static inline __attribute_const__ u32 uapi_key_attr_to_method(u32 attr_key) ...@@ -213,10 +239,23 @@ static inline __attribute_const__ u32 uapi_key_attr_to_method(u32 attr_key)
static inline __attribute_const__ bool uapi_key_is_ioctl_method(u32 key) static inline __attribute_const__ bool uapi_key_is_ioctl_method(u32 key)
{ {
return (key & UVERBS_API_METHOD_KEY_MASK) != 0 && unsigned int method = key & UVERBS_API_METHOD_KEY_MASK;
return method != 0 && method < UVERBS_API_METHOD_IS_WRITE &&
(key & UVERBS_API_ATTR_KEY_MASK) == 0; (key & UVERBS_API_ATTR_KEY_MASK) == 0;
} }
static inline __attribute_const__ bool uapi_key_is_write_method(u32 key)
{
return (key & UVERBS_API_METHOD_KEY_MASK) == UVERBS_API_METHOD_IS_WRITE;
}
static inline __attribute_const__ bool uapi_key_is_write_ex_method(u32 key)
{
return (key & UVERBS_API_METHOD_KEY_MASK) ==
UVERBS_API_METHOD_IS_WRITE_EX;
}
static inline __attribute_const__ u32 uapi_key_attrs_start(u32 ioctl_method_key) static inline __attribute_const__ u32 uapi_key_attrs_start(u32 ioctl_method_key)
{ {
/* 0 is the method slot itself */ /* 0 is the method slot itself */
...@@ -246,9 +285,12 @@ static inline __attribute_const__ u32 uapi_key_attr(u32 id) ...@@ -246,9 +285,12 @@ static inline __attribute_const__ u32 uapi_key_attr(u32 id)
return id; return id;
} }
/* Only true for ioctl methods */
static inline __attribute_const__ bool uapi_key_is_attr(u32 key) static inline __attribute_const__ bool uapi_key_is_attr(u32 key)
{ {
return (key & UVERBS_API_METHOD_KEY_MASK) != 0 && unsigned int method = key & UVERBS_API_METHOD_KEY_MASK;
return method != 0 && method < UVERBS_API_METHOD_IS_WRITE &&
(key & UVERBS_API_ATTR_KEY_MASK) != 0; (key & UVERBS_API_ATTR_KEY_MASK) != 0;
} }
...@@ -298,6 +340,8 @@ struct uverbs_object_def { ...@@ -298,6 +340,8 @@ struct uverbs_object_def {
enum uapi_definition_kind { enum uapi_definition_kind {
UAPI_DEF_END = 0, UAPI_DEF_END = 0,
UAPI_DEF_OBJECT_START,
UAPI_DEF_WRITE,
UAPI_DEF_CHAIN_OBJ_TREE, UAPI_DEF_CHAIN_OBJ_TREE,
UAPI_DEF_CHAIN, UAPI_DEF_CHAIN,
UAPI_DEF_IS_SUPPORTED_FUNC, UAPI_DEF_IS_SUPPORTED_FUNC,
...@@ -315,16 +359,54 @@ struct uapi_definition { ...@@ -315,16 +359,54 @@ struct uapi_definition {
struct { struct {
u16 object_id; u16 object_id;
} object_start; } object_start;
struct {
u8 is_ex;
u16 command_num;
} write;
}; };
union { union {
bool (*func_is_supported)(struct ib_device *device); bool (*func_is_supported)(struct ib_device *device);
ssize_t (*func_write)(struct ib_uverbs_file *file,
const char __user *buf, int in_len,
int out_len);
int (*func_write_ex)(struct ib_uverbs_file *file,
struct ib_udata *ucore,
struct ib_udata *uhw);
const struct uapi_definition *chain; const struct uapi_definition *chain;
const struct uverbs_object_def *chain_obj_tree; const struct uverbs_object_def *chain_obj_tree;
size_t needs_fn_offset; size_t needs_fn_offset;
}; };
}; };
/* Define things connected to object_id */
#define DECLARE_UVERBS_OBJECT(_object_id, ...) \
{ \
.kind = UAPI_DEF_OBJECT_START, \
.object_start = { .object_id = _object_id }, \
}, \
##__VA_ARGS__
/* Use in a var_args of DECLARE_UVERBS_OBJECT */
#define DECLARE_UVERBS_WRITE(_command_num, _func, ...) \
{ \
.kind = UAPI_DEF_WRITE, \
.scope = UAPI_SCOPE_OBJECT, \
.write = { .is_ex = 0, .command_num = _command_num }, \
.func_write = _func, \
}, \
##__VA_ARGS__
/* Use in a var_args of DECLARE_UVERBS_OBJECT */
#define DECLARE_UVERBS_WRITE_EX(_command_num, _func, ...) \
{ \
.kind = UAPI_DEF_WRITE, \
.scope = UAPI_SCOPE_OBJECT, \
.write = { .is_ex = 1, .command_num = _command_num }, \
.func_write_ex = _func, \
}, \
##__VA_ARGS__
/* /*
* Object is only supported if the function pointer named ibdev_fn in struct * Object is only supported if the function pointer named ibdev_fn in struct
* ib_device is not NULL. * ib_device is not NULL.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment