Hello,
I must admit I'm still kind of lost here. The email from kettenis@
with comment from linux helped a bit, but still my understanding
is really blurry here. Let me share my view on the recent
example from visa@.
</snip>
> >
> > Above, thread A would need a barrier before the release. Otherwise
> > the write might happen too late. This would be far from obvious if
> > the code was more complicated.
>
> Below is another, less far-fetched, example that shows that the current
> state of affairs is not safe:
>
> mtx_enter(&w->w_mtx);
> ...
> mtx_leave(&w->w_mtx);
> if (refcnt_rele(&w->w_refcnt))
> free(w);
>
> The mtx_leave() introduces a memory release barrier that forces all
> preceding memory accesses to complete before the decrement. However,
> there is no memory barrier after the write that releases the mutex.
>
> SPL adjusting aside, mtx_leave() essentially does this:
>
> membar_exit();
> mtx->mtx_owner = NULL;
>
after reading email from kettenis@ I can't see/understand a
problem described in the example here.
let's assume there are at least two threads:
A - which has released w->w_mtx and is going to do recnt_rele()
B - which is about to do mtx_enter(), in order to do mtx_enter(),
the thread B must also posses a reference to w
are those assumptions above right? If so, then the worst case which
may happen is that thread B will spin in mtx_enter() for a while
waiting for ->mtx_owner to become NULL. Does it sound right or
I am completely lost?
In my opinion the missing piece here is how both threads
obtain a reference to `w`. I believe the typical way involves
a look up operation under protection of some global lock:
rw_enter_read(&w_rw);
w = find_w();
if (w != NULL)
refcnt_take(&w->w_refcnt);
rw_exit_read(&w_rw);
> Now, because of inadequate memory synchronization, the writing
> of mtx_owner can become visible to other CPUs after the decrement.
>
as I've said earlier. the worst thing which happens in my
opinion is that the thread B can burn few cycles when spinning
to wait ->mtx_owner becomes NULL.
> I think the sanest solution is to add the release and acquire barriers
> in refcnt_rele().
>
so are we discussing the cost of memory barrier vs. cost of
cpu cycles burnt by thread B in mtx_enter() waiting for ->mtx_owner
to become NULL.
thanks for any hints on this
regards
sashan