scoutfs: don't livelock conflicting waiters

If there are two tasks waiting for conflicting modes, say a writer
waiting for a CW index lock and a index walker waiting for a PR index
lock, they can livelock.

In the ast one of their modes will be granted.  We'll wake them under
the lock now that they can see that their mode is ready.  But then while
still under the lock we see a conflicting waiter, and no users, so we
immediately start converting the lock away to the other waiting
conflicting mode.  The woken waiter is scheduled but now sees that the
lock isn't granted anymore because it's converting.  This bounces back
and forth forever.

The fix is to refuse to start conversion while there are still waiters
for the currently granted mode.  Once they finish it'll be able to
convert.

Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
Zach Brown
2018-02-21 13:31:01 -08:00
committed by Zach Brown
parent 302b0f5316
commit e31e828aff

View File

@@ -405,10 +405,10 @@ static void lock_process(struct lock_info *linfo, struct scoutfs_lock *lock)
/*
* Convert on behalf of waiters who aren't satisfied by the
* current mode when it won't conflict with users or a pending
* bast conversion. The new mode may or may not match the
* current granted mode so we may or may not need to block users
* during the transition.
* current mode when it won't conflict with specific waiters,
* matching users, or pending bast conversions. The new mode
* may or may not match the current granted mode so we may or
* may not need to block users during the transition.
*
* Remember that the presence of waiters doesn't necessarily
* mean that they're blocked. Multiple lock attempts naturally
@@ -418,6 +418,8 @@ static void lock_process(struct lock_info *linfo, struct scoutfs_lock *lock)
for (mode = 0; mode < SCOUTFS_LOCK_NR_MODES; mode++) {
if (lock->work_mode < 0 &&
lock->waiters[mode] &&
(lock->granted_mode < 0 ||
!lock->waiters[lock->granted_mode]) &&
!lock_modes_match(lock->granted_mode, mode) &&
lock_counts_match(mode, lock->users) &&
(lock->bast_mode < 0 ||