mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-02 23:27:06 +00:00
IB/uverbs: Add IDRs array attribute type to ioctl() interface
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>
This commit is contained in:
parent
e806f9328b
commit
70cd20aed0
4 changed files with 201 additions and 3 deletions
|
@ -57,6 +57,7 @@ struct bundle_priv {
|
||||||
struct ib_uverbs_attr *uattrs;
|
struct ib_uverbs_attr *uattrs;
|
||||||
|
|
||||||
DECLARE_BITMAP(uobj_finalize, UVERBS_API_ATTR_BKEY_LEN);
|
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
|
* 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);
|
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,
|
static int uverbs_process_attr(struct bundle_priv *pbundle,
|
||||||
const struct uverbs_api_attr *attr_uapi,
|
const struct uverbs_api_attr *attr_uapi,
|
||||||
struct ib_uverbs_attr *uattr, u32 attr_bkey)
|
struct ib_uverbs_attr *uattr, u32 attr_bkey)
|
||||||
|
@ -246,6 +327,11 @@ static int uverbs_process_attr(struct bundle_priv *pbundle,
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case UVERBS_ATTR_TYPE_IDRS_ARRAY:
|
||||||
|
return uverbs_process_idrs_array(pbundle, attr_uapi,
|
||||||
|
&e->objs_arr_attr, uattr,
|
||||||
|
attr_bkey);
|
||||||
default:
|
default:
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
@ -384,6 +470,7 @@ static int bundle_destroy(struct bundle_priv *pbundle, bool commit)
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
/* fast path for simple uobjects */
|
||||||
i = -1;
|
i = -1;
|
||||||
while ((i = find_next_bit(pbundle->uobj_finalize, key_bitmap_len,
|
while ((i = find_next_bit(pbundle->uobj_finalize, key_bitmap_len,
|
||||||
i + 1)) < 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;
|
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;) {
|
for (memblock = pbundle->allocated_mem; memblock;) {
|
||||||
struct bundle_alloc_head *tmp = 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,
|
memset(pbundle->bundle.attr_present, 0,
|
||||||
sizeof(pbundle->bundle.attr_present));
|
sizeof(pbundle->bundle.attr_present));
|
||||||
memset(pbundle->uobj_finalize, 0, sizeof(pbundle->uobj_finalize));
|
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);
|
ret = ib_uverbs_run_method(pbundle, hdr->num_attrs);
|
||||||
destroy_ret = bundle_destroy(pbundle, ret == 0);
|
destroy_ret = bundle_destroy(pbundle, ret == 0);
|
||||||
|
|
|
@ -73,6 +73,18 @@ static int uapi_merge_method(struct uverbs_api *uapi,
|
||||||
if (attr->attr.type == UVERBS_ATTR_TYPE_ENUM_IN)
|
if (attr->attr.type == UVERBS_ATTR_TYPE_ENUM_IN)
|
||||||
method_elm->driver_method |= is_driver;
|
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 =
|
attr_slot =
|
||||||
uapi_add_elm(uapi, method_key | uapi_key_attr(attr->id),
|
uapi_add_elm(uapi, method_key | uapi_key_attr(attr->id),
|
||||||
sizeof(*attr_slot));
|
sizeof(*attr_slot));
|
||||||
|
|
|
@ -52,6 +52,7 @@ enum uverbs_attr_type {
|
||||||
UVERBS_ATTR_TYPE_IDR,
|
UVERBS_ATTR_TYPE_IDR,
|
||||||
UVERBS_ATTR_TYPE_FD,
|
UVERBS_ATTR_TYPE_FD,
|
||||||
UVERBS_ATTR_TYPE_ENUM_IN,
|
UVERBS_ATTR_TYPE_ENUM_IN,
|
||||||
|
UVERBS_ATTR_TYPE_IDRS_ARRAY,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum uverbs_obj_access {
|
enum uverbs_obj_access {
|
||||||
|
@ -101,7 +102,7 @@ struct uverbs_attr_spec {
|
||||||
} enum_def;
|
} enum_def;
|
||||||
} u;
|
} u;
|
||||||
|
|
||||||
/* This weird split of the enum lets us remove some padding */
|
/* This weird split lets us remove some padding */
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
/*
|
/*
|
||||||
|
@ -111,6 +112,17 @@ struct uverbs_attr_spec {
|
||||||
*/
|
*/
|
||||||
const struct uverbs_attr_spec *ids;
|
const struct uverbs_attr_spec *ids;
|
||||||
} enum_def;
|
} 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;
|
} u2;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -251,6 +263,11 @@ static inline __attribute_const__ u32 uapi_bkey_attr(u32 attr_key)
|
||||||
return attr_key - 1;
|
return attr_key - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline __attribute_const__ u32 uapi_bkey_to_key_attr(u32 attr_bkey)
|
||||||
|
{
|
||||||
|
return attr_bkey + 1;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* =======================================
|
* =======================================
|
||||||
* Verbs definitions
|
* Verbs definitions
|
||||||
|
@ -323,6 +340,27 @@ struct uverbs_object_tree_def {
|
||||||
#define UA_MANDATORY .mandatory = 1
|
#define UA_MANDATORY .mandatory = 1
|
||||||
#define UA_OPTIONAL .mandatory = 0
|
#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, ...) \
|
#define UVERBS_ATTR_IDR(_attr_id, _idr_type, _access, ...) \
|
||||||
(&(const struct uverbs_attr_def){ \
|
(&(const struct uverbs_attr_def){ \
|
||||||
.id = _attr_id, \
|
.id = _attr_id, \
|
||||||
|
@ -440,10 +478,16 @@ struct uverbs_obj_attr {
|
||||||
const struct uverbs_api_attr *attr_elm;
|
const struct uverbs_api_attr *attr_elm;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct uverbs_objs_arr_attr {
|
||||||
|
struct ib_uobject **uobjects;
|
||||||
|
u16 len;
|
||||||
|
};
|
||||||
|
|
||||||
struct uverbs_attr {
|
struct uverbs_attr {
|
||||||
union {
|
union {
|
||||||
struct uverbs_ptr_attr ptr_attr;
|
struct uverbs_ptr_attr ptr_attr;
|
||||||
struct uverbs_obj_attr obj_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;
|
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)
|
static inline bool uverbs_attr_ptr_is_inline(const struct uverbs_attr *attr)
|
||||||
{
|
{
|
||||||
return attr->ptr_attr.len <= sizeof(attr->ptr_attr.data);
|
return attr->ptr_attr.len <= sizeof(attr->ptr_attr.data);
|
||||||
|
|
|
@ -53,7 +53,7 @@ enum {
|
||||||
|
|
||||||
struct ib_uverbs_attr {
|
struct ib_uverbs_attr {
|
||||||
__u16 attr_id; /* command specific type attribute */
|
__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 */
|
__u16 flags; /* combination of UVERBS_ATTR_F_XXXX */
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
|
@ -63,7 +63,10 @@ struct ib_uverbs_attr {
|
||||||
__u16 reserved;
|
__u16 reserved;
|
||||||
} attr_data;
|
} attr_data;
|
||||||
union {
|
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;
|
__aligned_u64 data;
|
||||||
/* Used by FD_IN and FD_OUT */
|
/* Used by FD_IN and FD_OUT */
|
||||||
__s64 data_s64;
|
__s64 data_s64;
|
||||||
|
|
Loading…
Reference in a new issue