RE: [PATCH 02/10] IB/uverbs: Build the specs into a radix tree at runtime

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> -----Original Message-----
> From: Jason Gunthorpe
> Sent: Monday, August 13, 2018 7:03 PM
> To: Guy Levi(SW) <guyle@xxxxxxxxxxxx>
> Cc: linux-rdma@xxxxxxxxxxxxxxx; Leon Romanovsky <leonro@xxxxxxxxxxxx>; Yishai Hadas <yishaih@xxxxxxxxxxxx>; Ruhl, Michael J
> <michael.j.ruhl@xxxxxxxxx>
> Subject: Re: [PATCH 02/10] IB/uverbs: Build the specs into a radix tree at runtime
> 
> On Mon, Aug 13, 2018 at 08:52:35AM -0600, Guy Levi(SW) wrote:
> > Well, better late than never...
> > SB my comments :)
> >
> > > From: Jason Gunthorpe [mailto:jgg@xxxxxxxx]
> > > Sent: Friday, August 03, 2018 10:32 PM
> > > To: linux-rdma@xxxxxxxxxxxxxxx; Leon Romanovsky <leonro@xxxxxxxxxxxx>; Guy Levi(SW) <guyle@xxxxxxxxxxxx>; Yishai
> Hadas
> > > <yishaih@xxxxxxxxxxxx>; Ruhl, Michael J <michael.j.ruhl@xxxxxxxxx>
> > > Cc: Jason Gunthorpe <jgg@xxxxxxxxxxxx>
> > > Subject: [PATCH 02/10] IB/uverbs: Build the specs into a radix tree at runtime
> > >
> > > From: Jason Gunthorpe <jgg@xxxxxxxxxxxx>
> > >
> > > This radix tree datastructure is intended to replace the 'hash' structure
> > > used today for parsing ioctl methods during system calls. This first
> > > commit introduces the structure and builds it from the existing .rodata
> > > descriptions.
> > >
> > > The so-called hash arrangement is actually a 5 level open coded radix tree.
> > > This new version uses a 3 level radix tree built using the radix tree
> > > library.
> > >
> > > Overall this is much less code and much easier to build as the radix tree
> > > API allows for dynamic modification during the building. There is a small
> > > memory penalty to pay for this, but since the radix tree is allocated on
> > > a per device basis, a few kb of RAM seems immaterial considering the
> > > gained simplicity.
> > >
> > > The radix tree is similar to the existing tree, but also has a 'attr_bkey'
> > > +				return -EINVAL;
> > > +		} else {
> > > +			obj_elm->type_attrs = obj->type_attrs;
> > > +			if (obj->type_attrs) {
> >
> > Can be an obj w/o type_attrs?
> > Does the DSL let it?
> > How would it be used and for what purpose?
> 
> This flow is used by the DEVICE object, and other objects that cannot
> be created. The DSL looks like this:
> 
> DECLARE_UVERBS_GLOBAL_METHODS(UVERBS_OBJECT_DEVICE);

What is the purpose of such object? Does it have methods?
Can't be created by whom?
In this flow the driver has a non-exist object in his spec tree... so it is actually trying to create it...

> > > +				if (WARN_ON(is_driver &&
> > > +					    obj->type_attrs->type_class !=
> > > +						    &uverbs_idr_class))
> > > +					return -EINVAL;
> > > +			}
> > > +		}
> > > +
> > > +		if (!obj->methods)
> > > +			continue;
> >
> > How can be an object w/o methods?
> > Do you want to insert such object in the tree?
> > Shouldn't it return an error?
> 
> Currently we have lots of object defined with no ioctl methods because
> the kabi is not complete. The methods exist on the write side.
> 
> We still have to create the infrastructure though as we now require
> the struct uverbs_obj_type to exist for all object. So yes, we must
> inster the empty object in to the tree.

AFAIU, it is expected to occur on the default tree only, right?
Maybe should be prior check if (is_driver && !obj->methods) -> fail.... Or it is already protected by the DSL...

> 
> > > +static int
> > > +uapi_finalize_ioctl_method(struct uverbs_api *uapi,
> > > +			   struct uverbs_api_ioctl_method *method_elm,
> > > +			   u32 method_key)
> > > +{
> > > +	struct radix_tree_iter iter;
> > > +	unsigned int max_bkey = 0;
> > > +	bool single_uobj = false;
> > > +	void __rcu **slot;
> > > +
> > > +	method_elm->destroy_bkey = UVERBS_API_ATTR_BKEY_LEN;
> > > +	radix_tree_for_each_slot (slot, &uapi->radix, &iter,
> > > +				  uapi_key_attrs_start(method_key)) {
> >
> > I don't understand how it goes over all method attributes if you
> > give specific attribute key... can you explain how it works?
> > Intuitively, I would say that you should give the method's key so it
> > go over all attributes (children)...
> 
> uapi_key_attrs_start() takes the method key and computes the first
> radix tree index for the method's associated attributes.
> 
> The attributes are organized in a linear block of radix tree keys
> within the range
> 
> [uapi_key_attrs_start(), method_key | UVERBS_API_ATTR_KEY_MASK]
> 
> > > +		struct uverbs_api_attr *elm =
> > > +			rcu_dereference_protected(*slot, true);
> > > +		u32 attr_key = iter.index & UVERBS_API_ATTR_KEY_MASK;
> >
> > Will be nice to have a comment what is the bkey represent.
> > e.g.  "A mask which each bit represents match attribute's key"
> 
> That comment is here:
> 
> /*
>  * This returns a value in the range [0 to UVERBS_API_ATTR_BKEY_LEN),
>  * basically it undoes the reservation of 0 in the ID numbering. attr_key
>  * must already be masked with UVERBS_API_ATTR_KEY_MASK, or be the output of
>  * uapi_key_attr().
>  */
> static inline __attribute_const__ u32 uapi_bkey_attr(u32 attr_key)
> 

Bkey naming is confusing.. it make me to think you are talking about a bit mask. 
I would rephrase it to key_bit_shift or something like that....

> > > +static int uapi_finalize(struct uverbs_api *uapi)
> > > +{
> > > +	struct radix_tree_iter iter;
> > > +	void __rcu **slot;
> > > +	int rc;
> > > +
> > > +	radix_tree_for_each_slot (slot, &uapi->radix, &iter, 0) {
> >
> > A redundant space.
> 
> The linux coding style has ' ' after for, eg
> 
>    for (i = 0; ....) {
> 
> Since radix_tree_for_each_slot is a 'for' loop not a function call, it
> is correct to place the space, despite checkpatch's incorrect
> complaining. clang-format gets this right automatically.
> 
> > > +struct uverbs_api *uverbs_alloc_api(
> > > +	const struct uverbs_object_tree_def *const *driver_specs,
> > > +	enum rdma_driver_id driver_id)
> > > +{
> > > +	struct uverbs_api *uapi;
> > > +	int rc;
> > > +
> > > +	uapi = kzalloc(sizeof(*uapi), GFP_KERNEL);
> > > +	if (!uapi)
> > > +		return ERR_PTR(-ENOMEM);
> > > +
> > > +	INIT_RADIX_TREE(&uapi->radix, GFP_KERNEL);
> > > +	uapi->driver_id = driver_id;
> > > +
> > > +	rc = uapi_merge_tree(uapi, uverbs_default_get_objects(), false);
> > > +	if (rc)
> > > +		goto err;
> > > +
> > > +	for (; *driver_specs; driver_specs++) {
> > > +		rc = uapi_merge_tree(uapi, *driver_specs, true);
> > > +		if (rc)
> > > +			goto err;
> > > +	}
> >
> > Just to make it clear (?):
> > a. the generic API which is not supported by driver will be blocked inside the driver handler
> > b. we abandoned the vision which the parsing tree will imply the driver capabilities.
> 
> No, that hasn't changed. The next step is to include more control over
> the parsing tree, ie the future flow will basically be
> 
> - Build up a radix tree that has 100% of all possiblities
> - Prune possibilites that do not match current capabilities
> 
> Today it is just a confusing mess with checks sprinkled all over the
> place.
> 
> > > +/*
> > > + * Information about the API is loaded into a radix tree. For IOCTL we start
> > > + * with a tuple of:
> > > + *  object_id, attr_id, method_id
> > > + *
> > > + * Which is a 48 bit value, with most of the bits guaranteed to be zero. Based
> > > + * on the current kernel support this is compressed into 16 bit key for the
> > > + * radix tree. Since this compression is entirely internal to the kernel the
> > > + * below limits can be revised if the kernel gains additional data.
> >
> > Does the code have any protection (by comments or compilation time
> > warning) against adding additional specs which exceeds the radix
> > capabilities?  AFAIU, the IDs are picked w/o a reference to any of
> > this radix limitation.
> 
> A compile time warning is a bit hard to create, but it will fail at
> run time while building the uapi, as packing a too big ID in any
> situation cases UVERBS_API_KEY_ERR to be returned and uapi_add_elm
> always fails with EOVERLFOW in that case..
> 
> The code is carefully organized so that the uapi_add_elm is always
> done before using an ID for the first time.
> 
> Also, the radix tree contruction OR's the object, method, and attrs
> keys so if any layer returns UVERBS_API_KEY_ERR then the OR will also
> equal UVERBS_API_KEY_ERR, and that is what is checked in
> uapi_add_elm()
> 
> In other cases, like lookup, the same is true. The radix tree will
> search on UVERBS_API_KEY_ERR and immediately fail. No particularly
> special checking is needed.
> 
> > > + *
> > > + * With 64 leafs per node this is a 3 level radix tree.
> > > + *
> > > + * 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.
> > > + */
> > > +enum uapi_radix_data {
> > > +	UVERBS_API_NS_FLAG = 1U << UVERBS_ID_NS_SHIFT,
> >
> > Why do you define it? Why 'UVERBS_UDATA_DRIVER_DATA_FLAG' isn't appropriate?
> > Anyway, will be more explicit to name it ' UVERBS_API_DRIVER_NS_FLAG'.
> 
> Ah, that would probably be better, this is one of those enduring
> messes it seems as we have the << version all over the place.
> 
> Let's make a folloup patch to drop all uses of UVERBS_ID_NS_SHIFT tree
> wide..
> 
> Jason




[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Photo]     [Yosemite News]     [Yosemite Photos]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux