From c8b876219f9784c87cf5b718242f81f0f7da7905 Mon Sep 17 00:00:00 2001 From: Alexander Kabaev Date: Mon, 16 Aug 2004 15:01:22 +0000 Subject: Upgrading a lock does not play well together with acquiring an exclusive lock and can lead to two threads being granted exclusive access. Check that no one has the same lock in exclusive mode before proceeding to acquire it. The LK_WANT_EXCL and LK_WANT_UPGRADE bits act as mini-locks and can block other threads. Normally this is not a problem since the mini locks are upgraded to full locks and the release of the locks will unblock the other threads. However if a thread reset the bits without obtaining a full lock other threads are not awoken. Add missing wakeups for these cases. PR: kern/69964 Submitted by: Stephan Uphoff Very good catch by: Stephan Uphoff --- sys/kern/kern_lock.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/sys/kern/kern_lock.c b/sys/kern/kern_lock.c index c819ba1861116..f1b6a427a126a 100644 --- a/sys/kern/kern_lock.c +++ b/sys/kern/kern_lock.c @@ -335,8 +335,12 @@ debuglockmgr(lkp, flags, interlkp, td, name, file, line) error = acquire(&lkp, extflags, LK_SHARE_NONZERO); lkp->lk_flags &= ~LK_WANT_UPGRADE; - if (error) - break; + if (error) { + if ((lkp->lk_flags & ( LK_WANT_EXCL | LK_WAIT_NONZERO)) == (LK_WANT_EXCL | LK_WAIT_NONZERO)) + wakeup((void *)lkp); + break; + } + lkp->lk_flags |= LK_HAVE_EXCL; lkp->lk_lockholder = thr; if (lkp->lk_exclusivecount != 0) @@ -382,17 +386,20 @@ debuglockmgr(lkp, flags, interlkp, td, name, file, line) /* * Try to acquire the want_exclusive flag. */ - error = acquire(&lkp, extflags, (LK_HAVE_EXCL | LK_WANT_EXCL)); + error = acquire(&lkp, extflags, LK_WANT_EXCL); if (error) break; lkp->lk_flags |= LK_WANT_EXCL; /* * Wait for shared locks and upgrades to finish. */ - error = acquire(&lkp, extflags, LK_WANT_UPGRADE | LK_SHARE_NONZERO); + error = acquire(&lkp, extflags, LK_HAVE_EXCL | LK_WANT_UPGRADE | LK_SHARE_NONZERO); lkp->lk_flags &= ~LK_WANT_EXCL; - if (error) + if (error) { + if (lkp->lk_flags & LK_WAIT_NONZERO) + wakeup((void *)lkp); break; + } lkp->lk_flags |= LK_HAVE_EXCL; lkp->lk_lockholder = thr; if (lkp->lk_exclusivecount != 0) -- cgit v1.3