From e31e828aff00c2f01722ea5e69a4433907365203 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 21 Feb 2018 13:31:01 -0800 Subject: [PATCH] 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 --- kmod/src/lock.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/kmod/src/lock.c b/kmod/src/lock.c index 565a1c8c..3d6e868e 100644 --- a/kmod/src/lock.c +++ b/kmod/src/lock.c @@ -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 ||