All of lore.kernel.org
 help / color / mirror / Atom feed
* xarray reserve/release?
@ 2019-02-19 23:53 Jason Gunthorpe
  2019-02-20  1:26 ` Matthew Wilcox
  0 siblings, 1 reply; 7+ messages in thread
From: Jason Gunthorpe @ 2019-02-19 23:53 UTC (permalink / raw)
  To: Matthew Wilcox; +Cc: linux-kernel

Hey Matt,

Did you intend that xa_release doesn't work on allocating arrays:

	xa_init_flags(&xa, XA_FLAGS_ALLOC);
	xa_reserve(&xa, 0, GFP_KERNEL);
	WARN_ON(xa_empty(&xa));
	xa_release(&xa, 0);
	WARN_ON(!xa_empty(&xa));

Triggers the second WARN_ON. 

If FLAGS_ALLOC is removed this passes

This creates kind of a confusing / super unexpected situation where 

   xa_for_each (xa, id)
       xa_erase(xa, id);
   WARN_ON(!xa_empty(&xa))

Is not safe with XA_FLAGS_ALLOC??

Seems like at least deserves a documentation note.. Or maybe ALLOC
xa_empty should do something different?

Thanks,
Jason

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: xarray reserve/release?
  2019-02-19 23:53 xarray reserve/release? Jason Gunthorpe
@ 2019-02-20  1:26 ` Matthew Wilcox
  2019-02-20  3:46   ` Jason Gunthorpe
  0 siblings, 1 reply; 7+ messages in thread
From: Matthew Wilcox @ 2019-02-20  1:26 UTC (permalink / raw)
  To: Jason Gunthorpe; +Cc: linux-kernel

On Tue, Feb 19, 2019 at 04:53:49PM -0700, Jason Gunthorpe wrote:
> Hey Matt,
> 
> Did you intend that xa_release doesn't work on allocating arrays:

That surprises me.  I'll take a look in the morning.

(giving me the test case is perfect, btw)

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: xarray reserve/release?
  2019-02-20  1:26 ` Matthew Wilcox
@ 2019-02-20  3:46   ` Jason Gunthorpe
  2019-02-20 17:14     ` Matthew Wilcox
  0 siblings, 1 reply; 7+ messages in thread
From: Jason Gunthorpe @ 2019-02-20  3:46 UTC (permalink / raw)
  To: Matthew Wilcox; +Cc: linux-kernel

On Tue, Feb 19, 2019 at 05:26:09PM -0800, Matthew Wilcox wrote:
> On Tue, Feb 19, 2019 at 04:53:49PM -0700, Jason Gunthorpe wrote:
> > Hey Matt,
> > 
> > Did you intend that xa_release doesn't work on allocating arrays:
> 
> That surprises me.  I'll take a look in the morning.

I think the issue is that this:

static inline void xa_release(struct xarray *xa, unsigned long index)
{
	xa_cmpxchg(xa, index, NULL, NULL, 0);

relies on the NULL actually being xas_store(NULL), but cmpxchg
transforms it into xas_store(XA_ZERO_ENTRY) when allocating..

So xa_reserve(), xa_release() and xa_cmpxchg() all do the same thing
for allocating arrays.

Perhaps this:

void __xa_release(struct xarray *xa, unsigned long index)
{
	XA_STATE(xas, xa, index);
	void *curr;

	curr = xas_load(&xas);
	if (curr == XA_ZERO_ENTRY)
		xas_store(&xas, NULL);
}

?

Also, I wonder if xa_reserve() is better written as as

       xa_cmpxchg(xa, index, NULL, XA_ZERO_ENTRY)

Bit clearer what is going on..

Jason

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: xarray reserve/release?
  2019-02-20  3:46   ` Jason Gunthorpe
@ 2019-02-20 17:14     ` Matthew Wilcox
  2019-02-20 17:43       ` Jason Gunthorpe
  0 siblings, 1 reply; 7+ messages in thread
From: Matthew Wilcox @ 2019-02-20 17:14 UTC (permalink / raw)
  To: Jason Gunthorpe; +Cc: linux-kernel

On Tue, Feb 19, 2019 at 08:46:27PM -0700, Jason Gunthorpe wrote:
> On Tue, Feb 19, 2019 at 05:26:09PM -0800, Matthew Wilcox wrote:
> > On Tue, Feb 19, 2019 at 04:53:49PM -0700, Jason Gunthorpe wrote:
> > > Hey Matt,
> > > 
> > > Did you intend that xa_release doesn't work on allocating arrays:
> > 
> > That surprises me.  I'll take a look in the morning.
> 
> I think the issue is that this:
> 
> static inline void xa_release(struct xarray *xa, unsigned long index)
> {
> 	xa_cmpxchg(xa, index, NULL, NULL, 0);
> 
> relies on the NULL actually being xas_store(NULL), but cmpxchg
> transforms it into xas_store(XA_ZERO_ENTRY) when allocating..

Yes, you're right.

> So xa_reserve(), xa_release() and xa_cmpxchg() all do the same thing
> for allocating arrays.
> 
> Perhaps this:
> 
> void __xa_release(struct xarray *xa, unsigned long index)
> {
> 	XA_STATE(xas, xa, index);
> 	void *curr;
> 
> 	curr = xas_load(&xas);
> 	if (curr == XA_ZERO_ENTRY)
> 		xas_store(&xas, NULL);
> }
> 
> ?

I decided to instead remove the magic from xa_cmpxchg().  I used
to prohibit any internal entry being passed to the regular API, but
I recently changed that with 76b4e5299565 ("XArray: Permit storing
2-byte-aligned pointers").  Now that we can pass XA_ZERO_ENTRY, I
think this all makes much more sense.

> Also, I wonder if xa_reserve() is better written as as
> 
>        xa_cmpxchg(xa, index, NULL, XA_ZERO_ENTRY)
> 
> Bit clearer what is going on..

Yes, I agree.  I've pushed a couple of new commits to
http://git.infradead.org/users/willy/linux-dax.git/shortlog/refs/heads/xarray

which should fix your problem, and implement this optimisation.
Thanks for the report!

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: xarray reserve/release?
  2019-02-20 17:14     ` Matthew Wilcox
@ 2019-02-20 17:43       ` Jason Gunthorpe
  2019-02-20 20:47         ` Matthew Wilcox
  0 siblings, 1 reply; 7+ messages in thread
From: Jason Gunthorpe @ 2019-02-20 17:43 UTC (permalink / raw)
  To: Matthew Wilcox; +Cc: linux-kernel

On Wed, Feb 20, 2019 at 09:14:14AM -0800, Matthew Wilcox wrote:

> > void __xa_release(struct xarray *xa, unsigned long index)
> > {
> > 	XA_STATE(xas, xa, index);
> > 	void *curr;
> > 
> > 	curr = xas_load(&xas);
> > 	if (curr == XA_ZERO_ENTRY)
> > 		xas_store(&xas, NULL);
> > }
> > 
> > ?
> 
> I decided to instead remove the magic from xa_cmpxchg().  I used
> to prohibit any internal entry being passed to the regular API, but
> I recently changed that with 76b4e5299565 ("XArray: Permit storing
> 2-byte-aligned pointers").  Now that we can pass XA_ZERO_ENTRY, I
> think this all makes much more sense.

Except that for allocating arrays xa_cmpxchg and xa_store now do
different things with NULL. Not necessarily bad, but if you have this
ABI variation it should be mentioned in the kdoc comment.

This is a bit worrysome though:

                curr = xas_load(&xas);
-               if (curr == XA_ZERO_ENTRY)
-                       curr = NULL;
                if (curr == old) {

It means any cmpxchg user has to care explicitly about the possibility
for true-NULL vs reserved. Seems like a difficult API.

What about writing it like this:

   if ((curr == XA_ZERO_ENTRY && old == NULL) || curr == old)

? I can't think of a use case to cmpxchg against real-null only.

And here:
                        xas_store(&xas, entry);
-                       if (xa_track_free(xa))
+                       if (xa_track_free(xa) && !old)
                                xas_clear_mark(&xas, XA_FREE_MARK);

Should this be

    if (xa_track_free(xa) && entry && !old)

? Ie we don't want to clear the XA_FREE_MARK if we just wrote NULL

Also I would think !curr is clearer? I assume the point is to not pay
the price of xas_clear_mark if we already know the index stored is
marked?

> > Also, I wonder if xa_reserve() is better written as as
> > 
> >        xa_cmpxchg(xa, index, NULL, XA_ZERO_ENTRY)
> > 
> > Bit clearer what is going on..
> 
> Yes, I agree.  I've pushed a couple of new commits to
> http://git.infradead.org/users/willy/linux-dax.git/shortlog/refs/heads/xarray

That looks really readable now that reserve and release are tidy
paired operations.

Thanks,
Jason

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: xarray reserve/release?
  2019-02-20 17:43       ` Jason Gunthorpe
@ 2019-02-20 20:47         ` Matthew Wilcox
  2019-02-20 21:18           ` Jason Gunthorpe
  0 siblings, 1 reply; 7+ messages in thread
From: Matthew Wilcox @ 2019-02-20 20:47 UTC (permalink / raw)
  To: Jason Gunthorpe; +Cc: linux-kernel

On Wed, Feb 20, 2019 at 10:43:33AM -0700, Jason Gunthorpe wrote:
> On Wed, Feb 20, 2019 at 09:14:14AM -0800, Matthew Wilcox wrote:
> > > void __xa_release(struct xarray *xa, unsigned long index)
> > > {
> > > 	XA_STATE(xas, xa, index);
> > > 	void *curr;
> > > 
> > > 	curr = xas_load(&xas);
> > > 	if (curr == XA_ZERO_ENTRY)
> > > 		xas_store(&xas, NULL);
> > > }
> > > 
> > > ?
> > 
> > I decided to instead remove the magic from xa_cmpxchg().  I used
> > to prohibit any internal entry being passed to the regular API, but
> > I recently changed that with 76b4e5299565 ("XArray: Permit storing
> > 2-byte-aligned pointers").  Now that we can pass XA_ZERO_ENTRY, I
> > think this all makes much more sense.
> 
> Except that for allocating arrays xa_cmpxchg and xa_store now do
> different things with NULL. Not necessarily bad, but if you have this
> ABI variation it should be mentioned in the kdoc comment.

I'm worrying about the whole xa_store(... NULL, gfp) situation.  Before
I realised I needed to unify the XArray and the IDR (I'd originally
intended to have the IDR be a client of the XArray the same way that
it was a client of the radix tree), everything was nice and simple.
xa_erase() was a synonym for xa_store(... NULL, gfp).  Then the IDR
users showed up with their understanding of what storing NULL meant,
and now xa_store(NULL) means something different depending what kind of
array you have.  This sucks.  I'm tempted to have xa_store(NULL) always
transform the NULL into XA_ZERO_ENTRY, but I worry I might break some
users without noticing.

> This is a bit worrysome though:
> 
>                 curr = xas_load(&xas);
> -               if (curr == XA_ZERO_ENTRY)
> -                       curr = NULL;
>                 if (curr == old) {
> 
> It means any cmpxchg user has to care explicitly about the possibility
> for true-NULL vs reserved. Seems like a difficult API.

I think the users know, though.  I went through the current users of
xa_cmpxchg() and they're not the same users which are using xa_reserve()
or xa_alloc().

> What about writing it like this:
> 
>    if ((curr == XA_ZERO_ENTRY && old == NULL) || curr == old)
> 
> ? I can't think of a use case to cmpxchg against real-null only.
> 
> And here:
>                         xas_store(&xas, entry);
> -                       if (xa_track_free(xa))
> +                       if (xa_track_free(xa) && !old)
>                                 xas_clear_mark(&xas, XA_FREE_MARK);
> 
> Should this be
> 
>     if (xa_track_free(xa) && entry && !old)
> 
> ? Ie we don't want to clear the XA_FREE_MARK if we just wrote NULL

There are four cases to consider: old is NULL/present, entry is NULL/present.
xas_store() will set XA_FREE_MARK (by calling xas_init_marks()) if entry
is NULL.  Otherwise it leaves the marks alone.

old	entry	FREE before	FREE after xas_store	should be FREE
NULL	NULL	true		true			true
NULL	present	true		true			false
present	NULL	false		true			true
present	present	false		false			false

So the only case we need to clear the bit is if old is NULL and entry is
present.  I didn't consider the NULL->NULL transition, so you're right.

> Also I would think !curr is clearer? I assume the point is to not pay
> the price of xas_clear_mark if we already know the index stored is
> marked?

If you find it clearer, I'll use 'curr'.  They're equal at this point anyway.

> That looks really readable now that reserve and release are tidy
> paired operations.

Great!

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: xarray reserve/release?
  2019-02-20 20:47         ` Matthew Wilcox
@ 2019-02-20 21:18           ` Jason Gunthorpe
  0 siblings, 0 replies; 7+ messages in thread
From: Jason Gunthorpe @ 2019-02-20 21:18 UTC (permalink / raw)
  To: Matthew Wilcox; +Cc: linux-kernel

On Wed, Feb 20, 2019 at 12:47:26PM -0800, Matthew Wilcox wrote:
> On Wed, Feb 20, 2019 at 10:43:33AM -0700, Jason Gunthorpe wrote:
> > On Wed, Feb 20, 2019 at 09:14:14AM -0800, Matthew Wilcox wrote:
> > > > void __xa_release(struct xarray *xa, unsigned long index)
> > > > {
> > > > 	XA_STATE(xas, xa, index);
> > > > 	void *curr;
> > > > 
> > > > 	curr = xas_load(&xas);
> > > > 	if (curr == XA_ZERO_ENTRY)
> > > > 		xas_store(&xas, NULL);
> > > > }
> > > > 
> > > > ?
> > > 
> > > I decided to instead remove the magic from xa_cmpxchg().  I used
> > > to prohibit any internal entry being passed to the regular API, but
> > > I recently changed that with 76b4e5299565 ("XArray: Permit storing
> > > 2-byte-aligned pointers").  Now that we can pass XA_ZERO_ENTRY, I
> > > think this all makes much more sense.
> > 
> > Except that for allocating arrays xa_cmpxchg and xa_store now do
> > different things with NULL. Not necessarily bad, but if you have this
> > ABI variation it should be mentioned in the kdoc comment.
> 
> I'm worrying about the whole xa_store(... NULL, gfp) situation.  Before
> I realised I needed to unify the XArray and the IDR (I'd originally
> intended to have the IDR be a client of the XArray the same way that
> it was a client of the radix tree), everything was nice and simple.
> xa_erase() was a synonym for xa_store(... NULL, gfp).  Then the IDR
> users showed up with their understanding of what storing NULL meant,
> and now xa_store(NULL) means something different depending what kind of
> array you have.  This sucks.  I'm tempted to have xa_store(NULL) always
> transform the NULL into XA_ZERO_ENTRY, but I worry I might break some
> users without noticing.

So you will end up with xa_store, xa_insert, xa_alloc doing the
conversion on store, and xa_cmpxchg not doing it.

I'd excuse xa_alloc/xa_insert, as anyone calling them surely means to
reserve the entry. The comment for xa_insert even says it does
this. Great.

xa_store(NULL) == xa_erase(), sometimes, is weird, IMHO. Two APIs
would be clearer: xa_store() which always does the XA_ZERO_ENTRY and
xa_store_erase() which always does xa_erase() if the argument is
NULL.

This makes the itent of the call site super clear without having to
find the xa_init and check the mode.

If this was done then xa_track_free() would only cause the mark to be
updated, or not, and has no other behavior change.

For completeness xa_cmpxchg() should probably get a similar comment as
xa_insert():

  If the predicate matches then a NULL entry will do xa_erase() on the
  index. Otherwise the value is stored.

  When matching the predicate the value NULL only matches unreserved
  entries. [this more or less matches xa_insert anyhow]

> > This is a bit worrysome though:
> > 
> >                 curr = xas_load(&xas);
> > -               if (curr == XA_ZERO_ENTRY)
> > -                       curr = NULL;
> >                 if (curr == old) {
> > 
> > It means any cmpxchg user has to care explicitly about the possibility
> > for true-NULL vs reserved. Seems like a difficult API.
> 
> I think the users know, though.  I went through the current users of
> xa_cmpxchg() and they're not the same users which are using xa_reserve()
> or xa_alloc().

Reflecting on this more, the use case I was looking at was basically

   // First thread here wins ownership of 'id' and does reserve
   ret = xa_cmpxchg(xa, id NULL, XA_ZERO_ENTRY)
   if (ret != NULL)
       return;

   ...

   // Can't fail
   xa_store(xa, id, something);

So that actually works better the way you have it.

Can always have another cmpxchg varient if a use comes up.

> > Also I would think !curr is clearer? I assume the point is to not pay
> > the price of xas_clear_mark if we already know the index stored is
> > marked?
> 
> If you find it clearer, I'll use 'curr'.  They're equal at this point anyway.

Yeah, it brings to mind your table more directly then having to also
work out that old == curr == xa_load ==> optimizing away unneeded clear

BTW, will you send conversion patches for drivers/infiniband sometime
soonish? Feel like I know enough about xarray to review them these
days

Thanks,
Jason

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2019-02-20 21:18 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-02-19 23:53 xarray reserve/release? Jason Gunthorpe
2019-02-20  1:26 ` Matthew Wilcox
2019-02-20  3:46   ` Jason Gunthorpe
2019-02-20 17:14     ` Matthew Wilcox
2019-02-20 17:43       ` Jason Gunthorpe
2019-02-20 20:47         ` Matthew Wilcox
2019-02-20 21:18           ` Jason Gunthorpe

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.