Re: [PATCH 4/4] locking: Introduce smp_cond_acquire()

Oleg Nesterov
2015-11-11T18:43:51Z

On 11/11, Peter Zijlstra wrote:
>
> On Wed, Nov 11, 2015 at 05:39:40PM +0800, Boqun Feng wrote:
>
> > Just be curious, should spin_unlock_wait() semantically be an ACQUIRE?
>
> I did wonder the same thing, it would simplify a number of things if
> this were so.

Yes, me too.

Sometimes I even think it should have both ACQUIRE + RELEASE semantics.
IOW, it should be "equivalent" to spin_lock() + spin_unlock().

Consider this code:

object_t *object;
spinlock_t lock;

void update(void)
{
object_t *o;

spin_lock(&lock);
o = READ_ONCE(object);
if (o) {
BUG_ON(o->dead);
do_something(o);
}
spin_unlock(&lock);
}

void destroy(void) // can be called only once, can't race with itself
{
object_t *o;

o = object;
object = NULL;

/*
* pairs with lock/ACQUIRE. The next update() must see
* object == NULL after spin_lock();
*/
smp_mb();

spin_unlock_wait(&lock);

/*
* pairs with unlock/RELEASE. The previous update() has
* already passed BUG_ON(o->dead).
*
* (Yes, yes, in this particular case it is not needed,
* we can rely on the control dependency).
*/
smp_mb();

o->dead = true;
}

I believe the code above is correct and it needs the barriers on both sides.

If it is wrong, then task_work_run() is buggy: it relies on mb() implied by
cmpxchg() before spin_unlock_wait() the same way: the next task_work_cancel()
should see the result of our cmpxchg(), it must not try to read work->next or
work->func.

Hmm. Not sure I really understand what I am trying to say... Perhaps in fact
I mean that unlock_wait() should be removed because it is too subtle for me ;)

Oleg.