mirror of
https://github.com/versity/scoutfs.git
synced 2026-02-07 19:20:44 +00:00
scoutfs: more carefully set lock bast mode
Locks get a bast call from the dlm when a remote node is blocked waiting for the mode of a lock to change. We'd set the mode that we need to convert to and kick off lock work to make forward progress. The bast calls can happen at any old time. If a call came in as we were unlocking a lock we'd set its bast mode even though it was being unlocked and would not need to be down converted. Usually this bad mode would be fine because the lock was idle and would just be freed after being locked. But if someone was actively waiting for the lock it would get stuck in an unlocked state. The bad bast mode would prevent it from being upconverted, but the waiters would stop it from being freed. We fix this by only setting the mode from the bast call if there is really work to do. This avoids setting the bast for unlocked locks which will let the lock state machine re-acquire them and make forward progress on behalf of the waiters. Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
@@ -281,6 +281,16 @@ static bool lock_modes_match(int granted, int user)
|
||||
(granted == DLM_LOCK_EX && user == DLM_LOCK_PR);
|
||||
}
|
||||
|
||||
/*
|
||||
* This isn't strictly the same as being compatible.. callers
|
||||
* need to be very careful to understand the combinations of
|
||||
* modes that are possible for them to attempt.
|
||||
*/
|
||||
static bool lock_mode_valid_and_greater(int mode, int other)
|
||||
{
|
||||
return mode != DLM_LOCK_IV && mode > other;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if all the actively used modes are satisfied by a lock
|
||||
* of the given granted mode.
|
||||
@@ -616,24 +626,35 @@ static void scoutfs_lock_ast(void *arg)
|
||||
* that we should convert our lock to. We can only either downconvert
|
||||
* to a matching PR or unlock.
|
||||
*
|
||||
* These are truly asynchronous and can arrive multiple times, at any time.
|
||||
* We're careful to only set the bast mode here and let lock processing
|
||||
* sort out the state machine.
|
||||
* These are truly asynchronous and can arrive multiple times, at any
|
||||
* time. We're careful to only set the lock's bast mode here if the
|
||||
* mode that's required conflicts with the lock's current mode, the mode
|
||||
* the work might be converting to, or the next mode from a previous
|
||||
* bast. This stops us from setting the lock's bast mode when it isn't
|
||||
* needed a confusing the state machine, like for a lock that's being
|
||||
* unlocked.
|
||||
*/
|
||||
static void scoutfs_lock_bast(void *arg, int blocked_mode)
|
||||
{
|
||||
struct scoutfs_lock *lock = arg;
|
||||
struct super_block *sb = lock->sb;
|
||||
DECLARE_LOCK_INFO(sb, linfo);
|
||||
int bast_mode;
|
||||
|
||||
scoutfs_inc_counter(sb, lock_bast);
|
||||
|
||||
spin_lock(&linfo->lock);
|
||||
|
||||
if (lock->granted_mode == DLM_LOCK_EX && blocked_mode == DLM_LOCK_PR)
|
||||
lock->bast_mode = DLM_LOCK_PR;
|
||||
bast_mode = DLM_LOCK_PR;
|
||||
else
|
||||
lock->bast_mode = DLM_LOCK_NL;
|
||||
bast_mode = DLM_LOCK_NL;
|
||||
|
||||
/* greater is safe, only try nl < all or pr < ex */
|
||||
if (lock_mode_valid_and_greater(lock->granted_mode, bast_mode) ||
|
||||
lock_mode_valid_and_greater(lock->work_mode, bast_mode) ||
|
||||
lock_mode_valid_and_greater(lock->bast_mode, bast_mode))
|
||||
lock->bast_mode = bast_mode;
|
||||
|
||||
trace_scoutfs_lock_bast(sb, lock);
|
||||
lock_process(linfo, lock);
|
||||
|
||||
Reference in New Issue
Block a user