]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
RDMA/verbs: Store the write/write_ex uapi entry points in the uverbs_api
authorJason Gunthorpe <jgg@mellanox.com>
Mon, 12 Nov 2018 20:59:55 +0000 (22:59 +0200)
committerJason Gunthorpe <jgg@mellanox.com>
Thu, 22 Nov 2018 18:57:33 +0000 (11:57 -0700)
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: Jason Gunthorpe <jgg@mellanox.com>
Signed-off-by: Leon Romanovsky <leonro@mellanox.com>
drivers/infiniband/core/rdma_core.h
drivers/infiniband/core/uverbs_uapi.c
include/rdma/uverbs_ioctl.h

index 93da02c12c3872412c33e780d749e803ba9d14cc..d36a0573c5e41145b38435bdfc4ec6bf3e697986 100644 (file)
@@ -136,6 +136,15 @@ struct uverbs_api_ioctl_method {
        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_attr_spec spec;
 };
@@ -144,6 +153,12 @@ struct uverbs_api {
        /* radix tree contains struct uverbs_api_* pointers */
        struct radix_tree_root radix;
        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 *
index 363a2d25728a4486772f78c562f53aa318d4274e..1cf79fc33c378563b91022602dff9cffab75f897 100644 (file)
@@ -8,6 +8,19 @@
 #include "rdma_core.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)
 {
        void *elm;
@@ -47,6 +60,42 @@ static void *uapi_add_get_elm(struct uverbs_api *uapi, u32 key,
        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,
                             struct uverbs_api_object *obj_elm, u32 obj_key,
                             const struct uverbs_method_def *method,
@@ -194,6 +243,7 @@ static int uapi_merge_def(struct uverbs_api *uapi, struct ib_device *ibdev,
 {
        const struct uapi_definition *def = def_list;
        u32 cur_obj_key = UVERBS_API_KEY_ERR;
+       bool exists;
        int rc;
 
        if (!def_list)
@@ -240,6 +290,23 @@ static int uapi_merge_def(struct uverbs_api *uapi, struct ib_device *ibdev,
                        if (rc)
                                return rc;
                        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);
                return -EINVAL;
@@ -266,8 +333,8 @@ uapi_finalize_ioctl_method(struct uverbs_api *uapi,
                u32 attr_bkey = uapi_bkey_attr(attr_key);
                u8 type = elm->spec.type;
 
-               if (uapi_key_attr_to_method(iter.index) !=
-                   uapi_key_attr_to_method(method_key))
+               if (uapi_key_attr_to_ioctl_method(iter.index) !=
+                   uapi_key_attr_to_ioctl_method(method_key))
                        break;
 
                if (elm->spec.mandatory)
@@ -309,9 +376,13 @@ uapi_finalize_ioctl_method(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;
        void __rcu **slot;
        int rc;
+       int i;
 
        radix_tree_for_each_slot (slot, &uapi->radix, &iter, 0) {
                struct uverbs_api_ioctl_method *method_elm =
@@ -323,6 +394,36 @@ static int uapi_finalize(struct uverbs_api *uapi)
                        if (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;
@@ -365,6 +466,23 @@ static u32 uapi_get_obj_id(struct uverbs_attr_spec *spec)
        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)
 {
        struct radix_tree_iter iter;
@@ -374,6 +492,8 @@ static void uapi_finalize_disable(struct uverbs_api *uapi)
 
 again:
        radix_tree_for_each_slot (slot, &uapi->radix, &iter, starting_key) {
+               uapi_key_okay(iter.index);
+
                if (uapi_key_is_object(iter.index)) {
                        struct uverbs_api_object *obj_elm =
                                rcu_dereference_protected(*slot, true);
@@ -400,6 +520,18 @@ static void uapi_finalize_disable(struct uverbs_api *uapi)
                        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)) {
                        struct uverbs_api_attr *attr_elm =
                                rcu_dereference_protected(*slot, true);
@@ -444,6 +576,7 @@ void uverbs_destroy_api(struct uverbs_api *uapi)
                return;
 
        uapi_remove_range(uapi, 0, U32_MAX);
+       kfree(uapi->write_methods);
        kfree(uapi);
 }
 
index 130f05e1a53b3e78d7622dbf1d5561b1607d3100..5df8ed79ba6c38703405f41a8bd0a89b990ddefe 100644 (file)
@@ -140,6 +140,13 @@ struct uverbs_attr_spec {
  *
  * 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.
+ *
+ * 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 {
        UVERBS_API_NS_FLAG = 1U << UVERBS_ID_NS_SHIFT,
@@ -147,12 +154,16 @@ enum uapi_radix_data {
        UVERBS_API_ATTR_KEY_BITS = 6,
        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_WRITE_KEY_NUM = 1 << UVERBS_API_ATTR_KEY_BITS,
 
        UVERBS_API_METHOD_KEY_BITS = 5,
        UVERBS_API_METHOD_KEY_SHIFT = UVERBS_API_ATTR_KEY_BITS,
-       UVERBS_API_METHOD_KEY_NUM_CORE = 24,
-       UVERBS_API_METHOD_KEY_NUM_DRIVER = (1 << UVERBS_API_METHOD_KEY_BITS) -
-                                          UVERBS_API_METHOD_KEY_NUM_CORE,
+       UVERBS_API_METHOD_KEY_NUM_CORE = 22,
+       UVERBS_API_METHOD_IS_WRITE = 30 << UVERBS_API_METHOD_KEY_SHIFT,
+       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_BITS + UVERBS_API_METHOD_KEY_SHIFT - 1,
                UVERBS_API_METHOD_KEY_SHIFT),
@@ -205,7 +216,22 @@ static inline __attribute_const__ u32 uapi_key_ioctl_method(u32 id)
        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 &
               (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)
 
 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;
 }
 
+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)
 {
        /* 0 is the method slot itself */
@@ -246,9 +285,12 @@ static inline __attribute_const__ u32 uapi_key_attr(u32 id)
        return id;
 }
 
+/* Only true for ioctl methods */
 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;
 }
 
@@ -298,6 +340,8 @@ struct uverbs_object_def {
 
 enum uapi_definition_kind {
        UAPI_DEF_END = 0,
+       UAPI_DEF_OBJECT_START,
+       UAPI_DEF_WRITE,
        UAPI_DEF_CHAIN_OBJ_TREE,
        UAPI_DEF_CHAIN,
        UAPI_DEF_IS_SUPPORTED_FUNC,
@@ -315,16 +359,54 @@ struct uapi_definition {
                struct {
                        u16 object_id;
                } object_start;
+               struct {
+                       u8 is_ex;
+                       u16 command_num;
+               } write;
        };
 
        union {
                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 uverbs_object_def *chain_obj_tree;
                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
  * ib_device is not NULL.