]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
RDMA/uverbs: Add helpers to mark uapi functions as unsupported
authorJason Gunthorpe <jgg@mellanox.com>
Mon, 12 Nov 2018 20:59:52 +0000 (22:59 +0200)
committerJason Gunthorpe <jgg@mellanox.com>
Thu, 22 Nov 2018 18:57:32 +0000 (11:57 -0700)
We have many cases where parts of the uapi are not supported in a driver,
needs a certain protocol, or whatever. It is best to reflect this directly
into the struct uverbs_api when it is built so that everything is simply
blocked off, and future introspection can report a proper supported list.

This is done by adding some additional helpers to the definition list
language that disable objects based on a 'supported' call back, and a
helper that disables based on a NULL struct ib_device function pointer.

Disablement is global. For instance, if a driver disables an object then
everything connected to that object is removed, including core methods.

Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
Signed-off-by: Leon Romanovsky <leonro@mellanox.com>
drivers/infiniband/core/rdma_core.h
drivers/infiniband/core/uverbs_main.c
drivers/infiniband/core/uverbs_uapi.c
include/rdma/uverbs_ioctl.h

index e39e9da1ff71c39a15e5899ee2cebf2d8d6ceea3..ce042e51fc0069edc21d40b5bd32416dc64a71bd 100644 (file)
@@ -121,6 +121,7 @@ void release_ufile_idr_uobject(struct ib_uverbs_file *ufile);
 struct uverbs_api_object {
        const struct uverbs_obj_type *type_attrs;
        const struct uverbs_obj_type_class *type_class;
+       u8 disabled:1;
 };
 
 struct uverbs_api_ioctl_method {
@@ -130,6 +131,7 @@ struct uverbs_api_ioctl_method {
        u16 bundle_size;
        u8 use_stack:1;
        u8 driver_method:1;
+       u8 disabled:1;
        u8 key_bitmap_len;
        u8 destroy_bkey;
 };
@@ -138,7 +140,6 @@ struct uverbs_api_attr {
        struct uverbs_attr_spec spec;
 };
 
-struct uverbs_api_object;
 struct uverbs_api {
        /* radix tree contains struct uverbs_api_* pointers */
        struct radix_tree_root radix;
@@ -152,8 +153,7 @@ uapi_get_object(struct uverbs_api *uapi, u16 object_id)
 }
 
 char *uapi_key_format(char *S, unsigned int key);
-struct uverbs_api *uverbs_alloc_api(const struct uapi_definition *driver_def,
-                                   enum rdma_driver_id driver_id);
+struct uverbs_api *uverbs_alloc_api(struct ib_device *ibdev);
 void uverbs_disassociate_api_pre(struct ib_uverbs_device *uverbs_dev);
 void uverbs_disassociate_api(struct uverbs_api *uapi);
 void uverbs_destroy_api(struct uverbs_api *uapi);
index 9a3b88d0095a46f44fa5868e225ebf1bb70a2f93..375121a4dd12a7a706546c21a717739c989a1965 100644 (file)
@@ -1224,7 +1224,7 @@ static int ib_uverbs_create_uapi(struct ib_device *device,
 {
        struct uverbs_api *uapi;
 
-       uapi = uverbs_alloc_api(device->driver_def, device->driver_id);
+       uapi = uverbs_alloc_api(device);
        if (IS_ERR(uapi))
                return PTR_ERR(uapi);
 
index cb35f18647811fc6463c5a320f7e67126b29f6af..9a904dd51694a6b0d0f160a03ef3717b1d9a74a8 100644 (file)
@@ -167,11 +167,33 @@ static int uapi_merge_obj_tree(struct uverbs_api *uapi,
        return 0;
 }
 
-static int uapi_merge_def(struct uverbs_api *uapi,
+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,
                          const struct uapi_definition *def_list,
                          bool is_driver)
 {
        const struct uapi_definition *def = def_list;
+       u32 cur_obj_key = UVERBS_API_KEY_ERR;
        int rc;
 
        if (!def_list)
@@ -180,7 +202,7 @@ static int uapi_merge_def(struct uverbs_api *uapi,
        for (;; def++) {
                switch ((enum uapi_definition_kind)def->kind) {
                case UAPI_DEF_CHAIN:
-                       rc = uapi_merge_def(uapi, def->chain, is_driver);
+                       rc = uapi_merge_def(uapi, ibdev, def->chain, is_driver);
                        if (rc)
                                return rc;
                        continue;
@@ -190,6 +212,7 @@ static int uapi_merge_def(struct uverbs_api *uapi,
                                    def->chain_obj_tree->id))
                                return -EINVAL;
 
+                       cur_obj_key = uapi_key_obj(def->object_start.object_id);
                        rc = uapi_merge_obj_tree(uapi, def->chain_obj_tree,
                                                 is_driver);
                        if (rc)
@@ -198,6 +221,25 @@ static int uapi_merge_def(struct uverbs_api *uapi,
 
                case UAPI_DEF_END:
                        return 0;
+
+               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;
                }
                WARN_ON(true);
                return -EINVAL;
@@ -286,18 +328,122 @@ static int uapi_finalize(struct uverbs_api *uapi)
        return 0;
 }
 
-void uverbs_destroy_api(struct uverbs_api *uapi)
+static void uapi_remove_range(struct uverbs_api *uapi, u32 start, u32 last)
 {
        struct radix_tree_iter iter;
        void __rcu **slot;
 
-       if (!uapi)
-               return;
-
-       radix_tree_for_each_slot (slot, &uapi->radix, &iter, 0) {
+       radix_tree_for_each_slot (slot, &uapi->radix, &iter, start) {
+               if (iter.index > last)
+                       return;
                kfree(rcu_dereference_protected(*slot, true));
                radix_tree_iter_delete(&uapi->radix, &iter, slot);
        }
+}
+
+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);
        kfree(uapi);
 }
 
@@ -306,8 +452,7 @@ static const struct uapi_definition uverbs_core_api[] = {
        {},
 };
 
-struct uverbs_api *uverbs_alloc_api(const struct uapi_definition *driver_def,
-                                   enum rdma_driver_id driver_id)
+struct uverbs_api *uverbs_alloc_api(struct ib_device *ibdev)
 {
        struct uverbs_api *uapi;
        int rc;
@@ -317,15 +462,16 @@ struct uverbs_api *uverbs_alloc_api(const struct uapi_definition *driver_def,
                return ERR_PTR(-ENOMEM);
 
        INIT_RADIX_TREE(&uapi->radix, GFP_KERNEL);
-       uapi->driver_id = driver_id;
+       uapi->driver_id = ibdev->driver_id;
 
-       rc = uapi_merge_def(uapi, uverbs_core_api, false);
+       rc = uapi_merge_def(uapi, ibdev, uverbs_core_api, false);
        if (rc)
                goto err;
-       rc = uapi_merge_def(uapi, driver_def, true);
+       rc = uapi_merge_def(uapi, ibdev, ibdev->driver_def, true);
        if (rc)
                goto err;
 
+       uapi_finalize_disable(uapi);
        rc = uapi_finalize(uapi);
        if (rc)
                goto err;
@@ -333,8 +479,9 @@ struct uverbs_api *uverbs_alloc_api(const struct uapi_definition *driver_def,
        return uapi;
 err:
        if (rc != -ENOMEM)
-               pr_err("Setup of uverbs_api failed, kernel parsing tree description is not valid (%d)??\n",
-                      rc);
+               dev_err(&ibdev->dev,
+                       "Setup of uverbs_api failed, kernel parsing tree description is not valid (%d)??\n",
+                       rc);
 
        uverbs_destroy_api(uapi);
        return ERR_PTR(rc);
index 9fa0cca45783d2b798219db3178db48e82ead9f6..130f05e1a53b3e78d7622dbf1d5561b1607d3100 100644 (file)
@@ -300,10 +300,17 @@ enum uapi_definition_kind {
        UAPI_DEF_END = 0,
        UAPI_DEF_CHAIN_OBJ_TREE,
        UAPI_DEF_CHAIN,
+       UAPI_DEF_IS_SUPPORTED_FUNC,
+       UAPI_DEF_IS_SUPPORTED_DEV_FN,
+};
+
+enum uapi_definition_scope {
+       UAPI_SCOPE_OBJECT = 1,
 };
 
 struct uapi_definition {
        u8 kind;
+       u8 scope;
        union {
                struct {
                        u16 object_id;
@@ -311,11 +318,35 @@ struct uapi_definition {
        };
 
        union {
+               bool (*func_is_supported)(struct ib_device *device);
                const struct uapi_definition *chain;
                const struct uverbs_object_def *chain_obj_tree;
+               size_t needs_fn_offset;
        };
 };
 
+/*
+ * Object is only supported if the function pointer named ibdev_fn in struct
+ * ib_device is not NULL.
+ */
+#define UAPI_DEF_OBJ_NEEDS_FN(ibdev_fn)                                        \
+       {                                                                      \
+               .kind = UAPI_DEF_IS_SUPPORTED_DEV_FN,                          \
+               .scope = UAPI_SCOPE_OBJECT,                                    \
+               .needs_fn_offset =                                             \
+                       offsetof(struct ib_device, ibdev_fn) +                 \
+                       BUILD_BUG_ON_ZERO(                                     \
+                               sizeof(((struct ib_device *)0)->ibdev_fn) !=   \
+                               sizeof(void *)),                               \
+       }
+
+/* Call a function to determine if the entire object is supported or not */
+#define UAPI_DEF_IS_OBJ_SUPPORTED(_func)                                       \
+       {                                                                      \
+               .kind = UAPI_DEF_IS_SUPPORTED_FUNC,                            \
+               .scope = UAPI_SCOPE_OBJECT, .func_is_supported = _func,        \
+       }
+
 /* Include another struct uapi_definition in this one */
 #define UAPI_DEF_CHAIN(_def_var)                                               \
        {                                                                      \