]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
IB/uverbs: Add IDRs array attribute type to ioctl() interface
authorGuy Levi <guyle@mellanox.com>
Thu, 6 Sep 2018 14:27:01 +0000 (17:27 +0300)
committerJason Gunthorpe <jgg@mellanox.com>
Tue, 11 Sep 2018 15:28:06 +0000 (09:28 -0600)
Methods sometimes need to get a flexible set of IDRs and not a strict set
as can be achieved today by the conventional IDR attribute. Add a new
IDRS_ARRAY attribute to the generic uverbs ioctl layer.

IDRS_ARRAY points to array of idrs of the same object type and same access
rights, only write and read are supported.

Signed-off-by: Guy Levi <guyle@mellanox.com>
Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>``
Signed-off-by: Leon Romanovsky <leonro@mellanox.com>
Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
drivers/infiniband/core/uverbs_ioctl.c
drivers/infiniband/core/uverbs_uapi.c
include/rdma/uverbs_ioctl.h
include/uapi/rdma/rdma_user_ioctl_cmds.h

index 4bafd4671de25c06f9634457b7c92b8c8d9ec246..0e95a5888274b4fd62508698bff65dd43e5a49db 100644 (file)
@@ -57,6 +57,7 @@ struct bundle_priv {
        struct ib_uverbs_attr *uattrs;
 
        DECLARE_BITMAP(uobj_finalize, UVERBS_API_ATTR_BKEY_LEN);
+       DECLARE_BITMAP(spec_finalize, UVERBS_API_ATTR_BKEY_LEN);
 
        /*
         * Must be last. bundle ends in a flex array which overlaps
@@ -143,6 +144,86 @@ static bool uverbs_is_attr_cleared(const struct ib_uverbs_attr *uattr,
                           0, uattr->len - len);
 }
 
+static int uverbs_process_idrs_array(struct bundle_priv *pbundle,
+                                    const struct uverbs_api_attr *attr_uapi,
+                                    struct uverbs_objs_arr_attr *attr,
+                                    struct ib_uverbs_attr *uattr,
+                                    u32 attr_bkey)
+{
+       const struct uverbs_attr_spec *spec = &attr_uapi->spec;
+       size_t array_len;
+       u32 *idr_vals;
+       int ret = 0;
+       size_t i;
+
+       if (uattr->attr_data.reserved)
+               return -EINVAL;
+
+       if (uattr->len % sizeof(u32))
+               return -EINVAL;
+
+       array_len = uattr->len / sizeof(u32);
+       if (array_len < spec->u2.objs_arr.min_len ||
+           array_len > spec->u2.objs_arr.max_len)
+               return -EINVAL;
+
+       attr->uobjects =
+               uverbs_alloc(&pbundle->bundle,
+                            array_size(array_len, sizeof(*attr->uobjects)));
+       if (IS_ERR(attr->uobjects))
+               return PTR_ERR(attr->uobjects);
+
+       /*
+        * Since idr is 4B and *uobjects is >= 4B, we can use attr->uobjects
+        * to store idrs array and avoid additional memory allocation. The
+        * idrs array is offset to the end of the uobjects array so we will be
+        * able to read idr and replace with a pointer.
+        */
+       idr_vals = (u32 *)(attr->uobjects + array_len) - array_len;
+
+       if (uattr->len > sizeof(uattr->data)) {
+               ret = copy_from_user(idr_vals, u64_to_user_ptr(uattr->data),
+                                    uattr->len);
+               if (ret)
+                       return -EFAULT;
+       } else {
+               memcpy(idr_vals, &uattr->data, uattr->len);
+       }
+
+       for (i = 0; i != array_len; i++) {
+               attr->uobjects[i] = uverbs_get_uobject_from_file(
+                       spec->u2.objs_arr.obj_type, pbundle->bundle.ufile,
+                       spec->u2.objs_arr.access, idr_vals[i]);
+               if (IS_ERR(attr->uobjects[i])) {
+                       ret = PTR_ERR(attr->uobjects[i]);
+                       break;
+               }
+       }
+
+       attr->len = i;
+       __set_bit(attr_bkey, pbundle->spec_finalize);
+       return ret;
+}
+
+static int uverbs_free_idrs_array(const struct uverbs_api_attr *attr_uapi,
+                                 struct uverbs_objs_arr_attr *attr,
+                                 bool commit)
+{
+       const struct uverbs_attr_spec *spec = &attr_uapi->spec;
+       int current_ret;
+       int ret = 0;
+       size_t i;
+
+       for (i = 0; i != attr->len; i++) {
+               current_ret = uverbs_finalize_object(
+                       attr->uobjects[i], spec->u2.objs_arr.access, commit);
+               if (!ret)
+                       ret = current_ret;
+       }
+
+       return ret;
+}
+
 static int uverbs_process_attr(struct bundle_priv *pbundle,
                               const struct uverbs_api_attr *attr_uapi,
                               struct ib_uverbs_attr *uattr, u32 attr_bkey)
@@ -246,6 +327,11 @@ static int uverbs_process_attr(struct bundle_priv *pbundle,
                }
 
                break;
+
+       case UVERBS_ATTR_TYPE_IDRS_ARRAY:
+               return uverbs_process_idrs_array(pbundle, attr_uapi,
+                                                &e->objs_arr_attr, uattr,
+                                                attr_bkey);
        default:
                return -EOPNOTSUPP;
        }
@@ -384,6 +470,7 @@ static int bundle_destroy(struct bundle_priv *pbundle, bool commit)
        unsigned int i;
        int ret = 0;
 
+       /* fast path for simple uobjects */
        i = -1;
        while ((i = find_next_bit(pbundle->uobj_finalize, key_bitmap_len,
                                  i + 1)) < key_bitmap_len) {
@@ -397,6 +484,32 @@ static int bundle_destroy(struct bundle_priv *pbundle, bool commit)
                        ret = current_ret;
        }
 
+       i = -1;
+       while ((i = find_next_bit(pbundle->spec_finalize, key_bitmap_len,
+                                 i + 1)) < key_bitmap_len) {
+               struct uverbs_attr *attr = &pbundle->bundle.attrs[i];
+               const struct uverbs_api_attr *attr_uapi;
+               void __rcu **slot;
+               int current_ret;
+
+               slot = uapi_get_attr_for_method(
+                       pbundle,
+                       pbundle->method_key | uapi_bkey_to_key_attr(i));
+               if (WARN_ON(!slot))
+                       continue;
+
+               attr_uapi = srcu_dereference(
+                       *slot,
+                       &pbundle->bundle.ufile->device->disassociate_srcu);
+
+               if (attr_uapi->spec.type == UVERBS_ATTR_TYPE_IDRS_ARRAY) {
+                       current_ret = uverbs_free_idrs_array(
+                               attr_uapi, &attr->objs_arr_attr, commit);
+                       if (!ret)
+                               ret = current_ret;
+               }
+       }
+
        for (memblock = pbundle->allocated_mem; memblock;) {
                struct bundle_alloc_head *tmp = memblock;
 
@@ -461,6 +574,7 @@ static int ib_uverbs_cmd_verbs(struct ib_uverbs_file *ufile,
        memset(pbundle->bundle.attr_present, 0,
               sizeof(pbundle->bundle.attr_present));
        memset(pbundle->uobj_finalize, 0, sizeof(pbundle->uobj_finalize));
+       memset(pbundle->spec_finalize, 0, sizeof(pbundle->spec_finalize));
 
        ret = ib_uverbs_run_method(pbundle, hdr->num_attrs);
        destroy_ret = bundle_destroy(pbundle, ret == 0);
index 73ea6f0db88fb5c2b2bbd698be684baf34dc5331..cdf5ced2c84f9cfe64c431c7218a8339cc5a2a8f 100644 (file)
@@ -73,6 +73,18 @@ static int uapi_merge_method(struct uverbs_api *uapi,
                if (attr->attr.type == UVERBS_ATTR_TYPE_ENUM_IN)
                        method_elm->driver_method |= is_driver;
 
+               /*
+                * Like other uobject based things we only support a single
+                * uobject being NEW'd or DESTROY'd
+                */
+               if (attr->attr.type == UVERBS_ATTR_TYPE_IDRS_ARRAY) {
+                       u8 access = attr->attr.u2.objs_arr.access;
+
+                       if (WARN_ON(access == UVERBS_ACCESS_NEW ||
+                                   access == UVERBS_ACCESS_DESTROY))
+                               return -EINVAL;
+               }
+
                attr_slot =
                        uapi_add_elm(uapi, method_key | uapi_key_attr(attr->id),
                                     sizeof(*attr_slot));
index fc2e52234a2ac4c5425caf1743dccce2e521b52f..84d3d15f1f38c75669f6985c08e558ab17dfa14d 100644 (file)
@@ -52,6 +52,7 @@ enum uverbs_attr_type {
        UVERBS_ATTR_TYPE_IDR,
        UVERBS_ATTR_TYPE_FD,
        UVERBS_ATTR_TYPE_ENUM_IN,
+       UVERBS_ATTR_TYPE_IDRS_ARRAY,
 };
 
 enum uverbs_obj_access {
@@ -101,7 +102,7 @@ struct uverbs_attr_spec {
                } enum_def;
        } u;
 
-       /* This weird split of the enum lets us remove some padding */
+       /* This weird split lets us remove some padding */
        union {
                struct {
                        /*
@@ -111,6 +112,17 @@ struct uverbs_attr_spec {
                         */
                        const struct uverbs_attr_spec *ids;
                } enum_def;
+
+               struct {
+                       /*
+                        * higher bits mean the namespace and lower bits mean
+                        * the type id within the namespace.
+                        */
+                       u16                             obj_type;
+                       u16                             min_len;
+                       u16                             max_len;
+                       u8                              access;
+               } objs_arr;
        } u2;
 };
 
@@ -251,6 +263,11 @@ static inline __attribute_const__ u32 uapi_bkey_attr(u32 attr_key)
        return attr_key - 1;
 }
 
+static inline __attribute_const__ u32 uapi_bkey_to_key_attr(u32 attr_bkey)
+{
+       return attr_bkey + 1;
+}
+
 /*
  * =======================================
  *     Verbs definitions
@@ -323,6 +340,27 @@ struct uverbs_object_tree_def {
 #define UA_MANDATORY .mandatory = 1
 #define UA_OPTIONAL .mandatory = 0
 
+/*
+ * min_len must be bigger than 0 and _max_len must be smaller than 4095.  Only
+ * READ\WRITE accesses are supported.
+ */
+#define UVERBS_ATTR_IDRS_ARR(_attr_id, _idr_type, _access, _min_len, _max_len, \
+                            ...)                                              \
+       (&(const struct uverbs_attr_def){                                      \
+               .id = (_attr_id) +                                             \
+                     BUILD_BUG_ON_ZERO((_min_len) == 0 ||                     \
+                                       (_max_len) >                           \
+                                               PAGE_SIZE / sizeof(void *) ||  \
+                                       (_min_len) > (_max_len) ||             \
+                                       (_access) == UVERBS_ACCESS_NEW ||      \
+                                       (_access) == UVERBS_ACCESS_DESTROY),   \
+               .attr = { .type = UVERBS_ATTR_TYPE_IDRS_ARRAY,                 \
+                         .u2.objs_arr.obj_type = _idr_type,                   \
+                         .u2.objs_arr.access = _access,                       \
+                         .u2.objs_arr.min_len = _min_len,                     \
+                         .u2.objs_arr.max_len = _max_len,                     \
+                         __VA_ARGS__ } })
+
 #define UVERBS_ATTR_IDR(_attr_id, _idr_type, _access, ...)                     \
        (&(const struct uverbs_attr_def){                                      \
                .id = _attr_id,                                                \
@@ -440,10 +478,16 @@ struct uverbs_obj_attr {
        const struct uverbs_api_attr    *attr_elm;
 };
 
+struct uverbs_objs_arr_attr {
+       struct ib_uobject **uobjects;
+       u16 len;
+};
+
 struct uverbs_attr {
        union {
                struct uverbs_ptr_attr  ptr_attr;
                struct uverbs_obj_attr  obj_attr;
+               struct uverbs_objs_arr_attr objs_arr_attr;
        };
 };
 
@@ -516,6 +560,31 @@ uverbs_attr_get_len(const struct uverbs_attr_bundle *attrs_bundle, u16 idx)
        return attr->ptr_attr.len;
 }
 
+/**
+ * uverbs_attr_get_uobjs_arr() - Provides array's properties for attribute for
+ * UVERBS_ATTR_TYPE_IDRS_ARRAY.
+ * @arr: Returned pointer to array of pointers for uobjects or NULL if
+ *       the attribute isn't provided.
+ *
+ * Return: The array length or 0 if no attribute was provided.
+ */
+static inline int uverbs_attr_get_uobjs_arr(
+       const struct uverbs_attr_bundle *attrs_bundle, u16 attr_idx,
+       struct ib_uobject ***arr)
+{
+       const struct uverbs_attr *attr =
+                       uverbs_attr_get(attrs_bundle, attr_idx);
+
+       if (IS_ERR(attr)) {
+               *arr = NULL;
+               return 0;
+       }
+
+       *arr = attr->objs_arr_attr.uobjects;
+
+       return attr->objs_arr_attr.len;
+}
+
 static inline bool uverbs_attr_ptr_is_inline(const struct uverbs_attr *attr)
 {
        return attr->ptr_attr.len <= sizeof(attr->ptr_attr.data);
index 24800c6c1f3242c6e732f51ac1f4dada0d534f30..06c34d99be8541976afcd6f2cdd4ac83efacaceb 100644 (file)
@@ -53,7 +53,7 @@ enum {
 
 struct ib_uverbs_attr {
        __u16 attr_id;          /* command specific type attribute */
-       __u16 len;              /* only for pointers */
+       __u16 len;              /* only for pointers and IDRs array */
        __u16 flags;            /* combination of UVERBS_ATTR_F_XXXX */
        union {
                struct {
@@ -63,7 +63,10 @@ struct ib_uverbs_attr {
                __u16 reserved;
        } attr_data;
        union {
-               /* Used by PTR_IN/OUT, ENUM_IN and IDR */
+               /*
+                * ptr to command, inline data, idr/fd or
+                * ptr to __u32 array of IDRs
+                */
                __aligned_u64 data;
                /* Used by FD_IN and FD_OUT */
                __s64 data_s64;