On Sat, 24 Sep 2011, Arnaud Lacombe wrote:

Description:
The following block of code, in `sys/sys/buf_ring.h':


      /*
       * If there are other enqueues in progress
       * that preceeded us, we need to wait for them
       * to complete
       */
      while (br->br_prod_tail != prod_head)
              cpu_spinwait();
      br->br_prod_bufs++;
      br->br_prod_bytes += nbytes;
      br->br_prod_tail = prod_next;
      critical_exit();


can be seen at runtime, memory-wise as:

     while (br->br_prod_tail != prod_head)
             cpu_spinwait();
     br->br_prod_tail = prod_next;
     br->br_prod_bufs++;
     br->br_prod_bytes += nbytes;
     critical_exit();

That is, there is no memory barrier to enforce completion of the
load/increment/store/load/load/addition/store operations before
updating what other thread spin on.

The counters are 64 bits, so it also does non-atomic increments of them
no 32-bit arches.

Even if `br_prod_tail' is marked `volatile', there is no guarantee that it will 
not be re-ordered wrt. non-volatile write (to `br_prod_bufs' and 
`br_prod_bytes').

Using volatile is generally bogus.  Here it seems to mainly give
pessimizations and more opportunities for bad memory orders.  The i386
code for incrementing a 64-bit volatile x is:

        movl    x, %eax
        movl    x+4, %edx
        addl    $1, %eax
        adcl    $0, %edx
        movl    %eax, x
        movl    %edx, x+4

while for a 64-bit non-volatile it is:

        addl    $1, x
        adcl    $0, x+4

so volatile gives more caching in registers instead of less.  The following
are some of the bad memory orders possible:

with volatile:
       lo = br->br_prod_bytes.lo;
       hi = br->br_prod_bytes.hi;
       br->br_prod_tail = prod_next;
       br->br_prod_bufs++;
       lo += nbytes;
       hi += carry;
       br->br_prod_bytes.hi = hi;
       br->br_prod_bytes.lo = lo;

without volatile:

       br->br_prod_bytes.lo += nbytes;
       br->br_prod_tail = prod_next;
       br->br_prod_bufs++;
       br->br_prod_bytes.hi += carry;

I think the token method would make the nonatomic accesses to the
counters sufficiently atomic if it worked.  The necessary memory
barriers would probably have a memory clobber which effectively makes
all memory variables transiently volatile (where volatile actually
means non-volatile with respect to them changing -- holding the token
prevents them changing -- but actually means volatile with respect to
their memory accesses) so the effect of declaring the counters permanently
volatile would be reduced to a pessimization.  Even reads of them in
sysctls must hold the token to get a consistent snapshot.

Bruce
_______________________________________________
freebsd-bugs@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bugs
To unsubscribe, send any mail to "freebsd-bugs-unsubscr...@freebsd.org"

Reply via email to