From: Andrew Perepechko Date: Fri, 22 Nov 2024 17:05:36 +0000 (+0300) Subject: LU-18483 ldlm: serialize parallel lock res change requests X-Git-Tag: 2.16.51~39 X-Git-Url: https://git.whamcloud.com/?a=commitdiff_plain;h=92b34022d51c7cf33bf244db7684fefe18a87429;p=fs%2Flustre-release.git LU-18483 ldlm: serialize parallel lock res change requests If parallel ldlm_lock_change_resource() calls race in a certain way, the old resource can be released multiple times leading to memory corruption. The fix detects such race condtions after locking and restarts the replace procedure if needed. Change-Id: I742a8dde0ab4c63517c02c2e8e1bdfe402b331b4 Signed-off-by: Andrew Perepechko HPE-bug-id: LUS-12212 Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/57110 Tested-by: jenkins Tested-by: Maloo Reviewed-by: Alexander Zarochentsev Reviewed-by: Yang Sheng Reviewed-by: Patrick Farrell Reviewed-by: Oleg Drokin --- diff --git a/lustre/ldlm/ldlm_lock.c b/lustre/ldlm/ldlm_lock.c index 06d0c3e..001bc1a 100644 --- a/lustre/ldlm/ldlm_lock.c +++ b/lustre/ldlm/ldlm_lock.c @@ -548,23 +548,30 @@ int ldlm_lock_change_resource(struct ldlm_namespace *ns, struct ldlm_lock *lock, * To flip the lock from the old to the new resource, oldres * and newres have to be locked. Resource spin-locks are taken * in the memory address order to avoid dead-locks. - * As this is the only circumstance where ->l_resource - * can change, and this cannot race with itself, it is safe - * to access lock->l_resource without being careful about locking. */ - oldres = lock->l_resource; - if (oldres < newres) { - lock_res(oldres); - lock_res_nested(newres, LRT_NEW); - } else { - lock_res(newres); - lock_res_nested(oldres, LRT_NEW); - } - LASSERT(memcmp(new_resid, &oldres->lr_name, - sizeof(oldres->lr_name)) != 0); - rcu_assign_pointer(lock->l_resource, newres); - unlock_res(oldres); - unlock_res(newres); + rcu_read_lock(); + while (1) { + oldres = rcu_dereference(lock->l_resource); + if (oldres == newres) + break; + + if (oldres < newres) { + lock_res(oldres); + lock_res_nested(newres, LRT_NEW); + } else { + lock_res(newres); + lock_res_nested(oldres, LRT_NEW); + } + if (lock->l_resource == oldres) { + rcu_assign_pointer(lock->l_resource, newres); + unlock_res(oldres); + unlock_res(newres); + break; + } + unlock_res(oldres); + unlock_res(newres); + } + rcu_read_unlock(); /* ...and the flowers are still standing! */ ldlm_resource_putref(oldres);