Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 70cd20ae authored by Guy Levi's avatar Guy Levi Committed by Jason Gunthorpe
Browse files

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: default avatarGuy Levi <guyle@mellanox.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@mellanox.com&gt;``>
Signed-off-by: default avatarLeon Romanovsky <leonro@mellanox.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@mellanox.com>
parent e806f932
Loading
Loading
Loading
Loading
+114 −0
Original line number Diff line number Diff line
@@ -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);
+12 −0
Original line number Diff line number Diff line
@@ -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));
+70 −1
Original line number Diff line number Diff line
@@ -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);
+5 −2
Original line number Diff line number Diff line
@@ -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;