kernel - Fix spin_lock_shared() race
authorMatthew Dillon <dillon@apollo.backplane.com>
Tue, 15 Oct 2013 19:13:49 +0000 (12:13 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Tue, 15 Oct 2013 19:25:56 +0000 (12:25 -0700)
commit79a7c522d4894f079dd0900cfdb6a545560982f7
tree33d212fb112cf3138b870fd6247b925288e54fe4
parent26d7cf899fa2915134979c2fab29764be33982e6
kernel - Fix spin_lock_shared() race

* Fix a serious bug in the shared spinlock code.  There are numerous races.
  The main problem is that the spin_lock_shared*() inlines set the
  SPINLOCK_SHARED bit based on a non-atomic test, assuming that the previous
  atomic operation guarded the test:

      atomic_add_int(&spin->counta, 1);
      if (spin->counta == 1)
    atomic_set_int(&spin->counta, SPINLOCK_SHARED);

  However, this can race an exclusive spin lock in another thread inbetween
  the conditional and the atomic_set_int().  The exclusive spinlock code
  would have seen contention, but then does this:

atomic_clear_int(&spin->counta, SPINLOCK_SHARED);
atomic_add_int(&spin->counta, SPINLOCK_EXCLWAIT - 1);
...

  The exclusive spinlock code then assumes that the shared spinlock code
  can no longer set SPINLOCK_SHARED.  However, if this occurs just after
  the shared spinlock code's if (spin->counta == 1) but before it atomically
  sets the SHARED bit, we wind up in a situation where the exclusive spinlock
  code completes its operation and leaves the SHARED bit set.

  In other words, the code which believes it has sucessfully obtained an
  exclusive spinlock actually winds up getting a shared spinlock.  Oops!

* Fixed by guarding the shared lock conditional and atomic op and changing
  the way shared lock contention is handled.

* Case was only reproducable on monster, probably due to massive shared
  spinlock use in the pmap code on 48 cpu cores all fork/exec'ing /bin/sh
  at the same time.
sys/kern/kern_spinlock.c
sys/sys/spinlock2.h