Commit 494c5580 authored by Matan Barak's avatar Matan Barak Committed by Jason Gunthorpe

IB/uverbs: Add enum attribute type to ioctl() interface

Methods sometimes need to get one attribute out of a group of
pre-defined attributes. This is an enum-like behavior. Since
this is a common requirement, we add a new ENUM attribute to the
generic uverbs ioctl() layer. This attribute is embedded in methods,
like any other attributes we currently have. ENUM attributes point to
an array of standard UVERBS_ATTR_PTR_IN. The user-space encodes the
enum's attribute id in the id field and the internal PTR_IN attr id in
the enum_data.elem_id field. This ENUM attribute could be shared by
several attributes and it can get UVERBS_ATTR_SPEC_F_MANDATORY flag,
stating this attribute must be supported by the kernel, like any other
attribute.
Reviewed-by: default avatarYishai Hadas <yishaih@mellanox.com>
Signed-off-by: default avatarMatan Barak <matanb@mellanox.com>
Signed-off-by: default avatarLeon Romanovsky <leonro@mellanox.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@mellanox.com>
parent 8c84660b
...@@ -55,14 +55,12 @@ static int uverbs_process_attr(struct ib_device *ibdev, ...@@ -55,14 +55,12 @@ static int uverbs_process_attr(struct ib_device *ibdev,
struct ib_uverbs_attr __user *uattr_ptr) struct ib_uverbs_attr __user *uattr_ptr)
{ {
const struct uverbs_attr_spec *spec; const struct uverbs_attr_spec *spec;
const struct uverbs_attr_spec *val_spec;
struct uverbs_attr *e; struct uverbs_attr *e;
const struct uverbs_object_spec *object; const struct uverbs_object_spec *object;
struct uverbs_obj_attr *o_attr; struct uverbs_obj_attr *o_attr;
struct uverbs_attr *elements = attr_bundle_h->attrs; struct uverbs_attr *elements = attr_bundle_h->attrs;
if (uattr->reserved)
return -EINVAL;
if (attr_id >= attr_spec_bucket->num_attrs) { if (attr_id >= attr_spec_bucket->num_attrs) {
if (uattr->flags & UVERBS_ATTR_F_MANDATORY) if (uattr->flags & UVERBS_ATTR_F_MANDATORY)
return -EINVAL; return -EINVAL;
...@@ -74,26 +72,46 @@ static int uverbs_process_attr(struct ib_device *ibdev, ...@@ -74,26 +72,46 @@ static int uverbs_process_attr(struct ib_device *ibdev,
return -EINVAL; return -EINVAL;
spec = &attr_spec_bucket->attrs[attr_id]; spec = &attr_spec_bucket->attrs[attr_id];
val_spec = spec;
e = &elements[attr_id]; e = &elements[attr_id];
e->uattr = uattr_ptr; e->uattr = uattr_ptr;
switch (spec->type) { switch (spec->type) {
case UVERBS_ATTR_TYPE_ENUM_IN:
if (uattr->attr_data.enum_data.elem_id >= spec->enum_def.num_elems)
return -EOPNOTSUPP;
if (uattr->attr_data.enum_data.reserved)
return -EINVAL;
val_spec = &spec->enum_def.ids[uattr->attr_data.enum_data.elem_id];
/* Currently we only support PTR_IN based enums */
if (val_spec->type != UVERBS_ATTR_TYPE_PTR_IN)
return -EOPNOTSUPP;
e->ptr_attr.enum_id = uattr->attr_data.enum_data.elem_id;
/* fall through */
case UVERBS_ATTR_TYPE_PTR_IN: case UVERBS_ATTR_TYPE_PTR_IN:
/* Ensure that any data provided by userspace beyond the known /* Ensure that any data provided by userspace beyond the known
* struct is zero. Userspace that knows how to use some future * struct is zero. Userspace that knows how to use some future
* longer struct will fail here if used with an old kernel and * longer struct will fail here if used with an old kernel and
* non-zero content, making ABI compat/discovery simpler. * non-zero content, making ABI compat/discovery simpler.
*/ */
if (uattr->len > spec->ptr.len && if (uattr->len > val_spec->ptr.len &&
spec->flags & UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO && val_spec->flags & UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO &&
!uverbs_is_attr_cleared(uattr, spec->ptr.len)) !uverbs_is_attr_cleared(uattr, val_spec->ptr.len))
return -EOPNOTSUPP; return -EOPNOTSUPP;
/* fall through */ /* fall through */
case UVERBS_ATTR_TYPE_PTR_OUT: case UVERBS_ATTR_TYPE_PTR_OUT:
if (uattr->len < spec->ptr.min_len || if (uattr->len < val_spec->ptr.min_len ||
(!(spec->flags & UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO) && (!(val_spec->flags & UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO) &&
uattr->len > spec->ptr.len)) uattr->len > val_spec->ptr.len))
return -EINVAL;
if (spec->type != UVERBS_ATTR_TYPE_ENUM_IN &&
uattr->attr_data.reserved)
return -EINVAL; return -EINVAL;
e->ptr_attr.data = uattr->data; e->ptr_attr.data = uattr->data;
...@@ -106,6 +124,9 @@ static int uverbs_process_attr(struct ib_device *ibdev, ...@@ -106,6 +124,9 @@ static int uverbs_process_attr(struct ib_device *ibdev,
return -EINVAL; return -EINVAL;
/* fall through */ /* fall through */
case UVERBS_ATTR_TYPE_FD: case UVERBS_ATTR_TYPE_FD:
if (uattr->attr_data.reserved)
return -EINVAL;
if (uattr->len != 0 || !ucontext || uattr->data > INT_MAX) if (uattr->len != 0 || !ucontext || uattr->data > INT_MAX)
return -EINVAL; return -EINVAL;
......
...@@ -51,6 +51,7 @@ enum uverbs_attr_type { ...@@ -51,6 +51,7 @@ enum uverbs_attr_type {
UVERBS_ATTR_TYPE_PTR_OUT, UVERBS_ATTR_TYPE_PTR_OUT,
UVERBS_ATTR_TYPE_IDR, UVERBS_ATTR_TYPE_IDR,
UVERBS_ATTR_TYPE_FD, UVERBS_ATTR_TYPE_FD,
UVERBS_ATTR_TYPE_ENUM_IN,
}; };
enum uverbs_obj_access { enum uverbs_obj_access {
...@@ -95,6 +96,18 @@ struct uverbs_attr_spec { ...@@ -95,6 +96,18 @@ struct uverbs_attr_spec {
u16 obj_type; u16 obj_type;
u8 access; u8 access;
} obj; } obj;
struct {
enum uverbs_attr_type type;
/* Combination of bits from enum UVERBS_ATTR_SPEC_F_XXXX */
u8 flags;
u8 num_elems;
/*
* The enum attribute can select one of the attributes
* contained in the ids array. Currently only PTR_IN
* attributes are supported in the ids array.
*/
const struct uverbs_attr_spec *ids;
} enum_def;
}; };
}; };
...@@ -215,6 +228,10 @@ struct uverbs_object_tree_def { ...@@ -215,6 +228,10 @@ struct uverbs_object_tree_def {
UVERBS_ATTR(_id, UVERBS_ATTR_TYPE_PTR_OUT, ptr, _len, ##__VA_ARGS__) UVERBS_ATTR(_id, UVERBS_ATTR_TYPE_PTR_OUT, ptr, _len, ##__VA_ARGS__)
#define UVERBS_ATTR_PTR_OUT(_id, _type, ...) \ #define UVERBS_ATTR_PTR_OUT(_id, _type, ...) \
UVERBS_ATTR_PTR_OUT_SZ(_id, _type, ##__VA_ARGS__) UVERBS_ATTR_PTR_OUT_SZ(_id, _type, ##__VA_ARGS__)
#define UVERBS_ATTR_ENUM_IN(_id, _enum_arr, ...) \
UVERBS_ATTR(_id, UVERBS_ATTR_TYPE_ENUM_IN, enum_def, \
.ids = (_enum_arr), \
.num_elems = ARRAY_SIZE(_enum_arr), ##__VA_ARGS__)
/* /*
* In new compiler, UVERBS_ATTR_IDR (and FD) could be simplified by declaring * In new compiler, UVERBS_ATTR_IDR (and FD) could be simplified by declaring
...@@ -254,6 +271,11 @@ struct uverbs_object_tree_def { ...@@ -254,6 +271,11 @@ struct uverbs_object_tree_def {
#define DECLARE_UVERBS_ATTR_SPEC(_name, ...) \ #define DECLARE_UVERBS_ATTR_SPEC(_name, ...) \
const struct uverbs_attr_def _name = __VA_ARGS__ const struct uverbs_attr_def _name = __VA_ARGS__
#define DECLARE_UVERBS_ENUM(_name, ...) \
const struct uverbs_enum_spec _name = { \
.len = ARRAY_SIZE(((struct uverbs_attr_spec[]){__VA_ARGS__})),\
.ids = {__VA_ARGS__}, \
}
#define _UVERBS_METHOD_ATTRS_SZ(...) \ #define _UVERBS_METHOD_ATTRS_SZ(...) \
(sizeof((const struct uverbs_attr_def * const []){__VA_ARGS__}) /\ (sizeof((const struct uverbs_attr_def * const []){__VA_ARGS__}) /\
sizeof(const struct uverbs_attr_def *)) sizeof(const struct uverbs_attr_def *))
...@@ -305,6 +327,7 @@ struct uverbs_ptr_attr { ...@@ -305,6 +327,7 @@ struct uverbs_ptr_attr {
u16 len; u16 len;
/* Combination of bits from enum UVERBS_ATTR_F_XXXX */ /* Combination of bits from enum UVERBS_ATTR_F_XXXX */
u16 flags; u16 flags;
u8 enum_id;
}; };
struct uverbs_obj_attr { struct uverbs_obj_attr {
...@@ -374,6 +397,17 @@ static inline const struct uverbs_attr *uverbs_attr_get(const struct uverbs_attr ...@@ -374,6 +397,17 @@ static inline const struct uverbs_attr *uverbs_attr_get(const struct uverbs_attr
return &attrs_bundle->hash[idx_bucket].attrs[idx & ~UVERBS_ID_NS_MASK]; return &attrs_bundle->hash[idx_bucket].attrs[idx & ~UVERBS_ID_NS_MASK];
} }
static inline int uverbs_attr_get_enum_id(const struct uverbs_attr_bundle *attrs_bundle,
u16 idx)
{
const struct uverbs_attr *attr = uverbs_attr_get(attrs_bundle, idx);
if (IS_ERR(attr))
return PTR_ERR(attr);
return attr->ptr_attr.enum_id;
}
static inline int uverbs_copy_to(const struct uverbs_attr_bundle *attrs_bundle, static inline int uverbs_copy_to(const struct uverbs_attr_bundle *attrs_bundle,
size_t idx, const void *from, size_t size) size_t idx, const void *from, size_t size)
{ {
......
...@@ -55,7 +55,13 @@ struct ib_uverbs_attr { ...@@ -55,7 +55,13 @@ 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 */
__u16 flags; /* combination of UVERBS_ATTR_F_XXXX */ __u16 flags; /* combination of UVERBS_ATTR_F_XXXX */
union {
struct {
__u8 elem_id;
__u8 reserved;
} enum_data;
__u16 reserved; __u16 reserved;
} attr_data;
__aligned_u64 data; /* ptr to command, inline data or idr/fd */ __aligned_u64 data; /* ptr to command, inline data or idr/fd */
}; };
......
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