kernel - Fix rare missed wakeup() in lockmgr
authorMatthew Dillon <dillon@apollo.backplane.com>
Wed, 14 Feb 2018 19:49:40 +0000 (11:49 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Wed, 14 Feb 2018 19:49:40 +0000 (11:49 -0800)
commit8319957b9f679319761213e5c0e28293afd26e34
tree2a6c120db02d8688c59d59f7d0e530eb37cf27ff
parent0be2fad4ba2faab5d60374d141ab83e895a9181a
kernel - Fix rare missed wakeup() in lockmgr

* Fix a rare missed wakeup() case in lockmgr.  lk_count can briefly
  become (SHARED | 0 counts | (EXREQ or UPREQ)).  Three competing cores
  can then cause a situation where undo_shreq() fails to issue a wakeup()
  to threads acquiring a shared lock that are blocked waiting for the
  EXREQ or UPREQ

  Issue the missing wakeup() for this case.

* This race arises because of an optimization we make when dropping a
  shared lock.  atomic_fcmpset*() loops are fairly poor at dealing
  with concurrent increments and decrements, so undo_shreq() (which
  is also used when releasing a standard shared lock) decrements the
  shared lock count first, then deals with EXREQ or UPREQ afterwords.

  Usually a shared lock request bumps the shared lock count before
  blocking, which other lock releases use to determine the need for a
  wakeup().  However, shared lock requests cannot bump the shared lock
  count when the lock is already held SHARED but there is an EXREQ or
  UPREQ pending, because doing do basically grants the shared lock
  immediately.

  This combination leads to the brief situation which allows other cpu
  cores to squeeze in operations of their own without realizing that
  someone might be blocked trying to obtain a shared lock, but with no
  shared count present to indicate so.  If the undo_shreq()'s later
  atomic_fcmpset*() calls then fail, or find that there is no EXREQ or
  UPREQ pending, it fails to issue the needed wakeup().
sys/kern/kern_lock.c