On Fri, Sep 27, 2019 at 02:49:29PM +0200, Peter Zijlstra wrote: > On Fri, Sep 27, 2019 at 11:51:07AM +0200, Andrea Parri wrote: > > > For the record, the LKMM doesn't currently model "order" derived from > > control dependencies to a _plain_ access (even if the plain access is > > a write): in particular, the following is racy (as far as the current > > LKMM is concerned): > > > > C rb > > > > { } > > > > P0(int *tail, int *data, int *head) > > { > > if (READ_ONCE(*tail)) { > > *data = 1; > > smp_wmb(); > > WRITE_ONCE(*head, 1); > > } > > } > > > > P1(int *tail, int *data, int *head) > > { > > int r0; > > int r1; > > > > r0 = READ_ONCE(*head); > > smp_rmb(); > > r1 = *data; > > smp_mb(); > > WRITE_ONCE(*tail, 1); > > } > > > > Replacing the plain "*data = 1" with "WRITE_ONCE(*data, 1)" (or doing > > s/READ_ONCE(*tail)/smp_load_acquire(tail)) suffices to avoid the race. > > Maybe I'm short of imagination this morning... but I can't currently > > see how the compiler could "break" the above scenario. > > The compiler; if sufficiently smart; is 'allowed' to change P0 into > something terrible like: > > *data = 1; > if (*tail) { > smp_wmb(); > *head = 1; > } else > *data = 0; > > > (assuming it knows *data was 0 from a prior store or something) > > Using WRITE_ONCE() defeats this because volatile indicates external > visibility. The much simpler solution might be writing it like: if (READ_ONCE(*tail) { barrier(); *data = 1; smp_wmb(); WRITE_ONCE(*head, 1); } which I don't think the compiler is allowed to mess up.