History

locking/mutex: implement mutex_trylock_nested

Despite the fact that several lockdep-related checks are skipped when calling trylock* versions of the locking primitives, for example mutex_trylock, each time the mutex is acquired, a held_lock is still placed onto the lockdep stack by __lock_acquire() which is called regardless of whether the trylock* or regular locking API was used. This means that if the caller successfully acquires more than MAX_LOCK_DEPTH locks of the same class, even when using mutex_trylock, lockdep will still complain that the maximum depth of the held lock stack has been reached and disable itself. For example, the following error currently occurs in the ARM version of KVM, once the code tries to lock all vCPUs of a VM configured with more than MAX_LOCK_DEPTH vCPUs, a situation that can easily happen on modern systems, where having more than 48 CPUs is common, and it's also common to run VMs that have vCPU counts approaching that number: [ 328.171264] BUG: MAX_LOCK_DEPTH too low! [ 328.175227] turning off the locking correctness validator. [ 328.180726] Please attach the output of /proc/lock_stat to the bug report [ 328.187531] depth: 48 max: 48! [ 328.190678] 48 locks held by qemu-kvm/11664: [ 328.194957] #0: ffff800086de5ba0 (&kvm->lock){+.+.}-{3:3}, at: kvm_ioctl_create_device+0x174/0x5b0 [ 328.204048] #1: ffff0800e78800b8 (&vcpu->mutex){+.+.}-{3:3}, at: lock_all_vcpus+0x16c/0x2a0 [ 328.212521] #2: ffff07ffeee51e98 (&vcpu->mutex){+.+.}-{3:3}, at: lock_all_vcpus+0x16c/0x2a0 [ 328.220991] #3: ffff0800dc7d80b8 (&vcpu->mutex){+.+.}-{3:3}, at: lock_all_vcpus+0x16c/0x2a0 [ 328.229463] #4: ffff07ffe0c980b8 (&vcpu->mutex){+.+.}-{3:3}, at: lock_all_vcpus+0x16c/0x2a0 [ 328.237934] #5: ffff0800a3883c78 (&vcpu->mutex){+.+.}-{3:3}, at: lock_all_vcpus+0x16c/0x2a0 [ 328.246405] #6: ffff07fffbe480b8 (&vcpu->mutex){+.+.}-{3:3}, at: lock_all_vcpus+0x16c/0x2a0 Luckily, in all instances that require locking all vCPUs, the 'kvm->lock' is taken a priori, and that fact makes it possible to use the little known feature of lockdep, called a 'nest_lock', to avoid this warning and subsequent lockdep self-disablement. The action of 'nested lock' being provided to lockdep's lock_acquire(), causes the lockdep to detect that the top of the held lock stack contains a lock of the same class and then increment its reference counter instead of pushing a new held_lock item onto that stack. See __lock_acquire for more information.

Maxim Levitsky committed 1 year ago

Commit c5b6aba1 parent e9f17032 files · +26 −3Browse files

Showing the changes to kernel/locking (1 of 2 files). Show all

Unified · Split

kernel/locking/mutex.c+11−3
@@ -1062,6 +1062,7 @@ __ww_mutex_lock_interruptible_slowpath(struct ww_mutex *lock, · 1061 unmodified lines
1062 1062
1063#endif1063#endif
1064 1064
1065#ifndef CONFIG_DEBUG_LOCK_ALLOC
1065/**1066/**
1066 * mutex_trylock - try to acquire the mutex, without waiting1067 * mutex_trylock - try to acquire the mutex, without waiting
1067 * @lock: the mutex to be acquired1068 * @lock: the mutex to be acquired
@@ -1077,18 +1078,25 @@ __ww_mutex_lock_interruptible_slowpath(struct ww_mutex *lock, · 9 unmodified lines
1077 * mutex must be released by the same task that acquired it.1078 * mutex must be released by the same task that acquired it.
1078 */1079 */
1079int __sched mutex_trylock(struct mutex *lock)1080int __sched mutex_trylock(struct mutex *lock)
1081{
1082 MUTEX_WARN_ON(lock->magic != lock);
1083 return __mutex_trylock(lock);
1084}
1085EXPORT_SYMBOL(mutex_trylock);
1086#else
1087int __sched _mutex_trylock_nest_lock(struct mutex *lock, struct lockdep_map *nest_lock)
1080{1088{
1081 bool locked;1089 bool locked;
1082 1090
1083 MUTEX_WARN_ON(lock->magic != lock);1091 MUTEX_WARN_ON(lock->magic != lock);
1084
1085 locked = __mutex_trylock(lock);1092 locked = __mutex_trylock(lock);
1086 if (locked)1093 if (locked)
1087 mutex_acquire(&lock->dep_map, 0, 1, _RET_IP_);1094 mutex_acquire_nest(&lock->dep_map, 0, 1, nest_lock, _RET_IP_);
1088 1095
1089 return locked;1096 return locked;
1090}1097}
1091EXPORT_SYMBOL(mutex_trylock);1098EXPORT_SYMBOL(_mutex_trylock_nest_lock);
1099#endif
1092 1100
1093#ifndef CONFIG_DEBUG_LOCK_ALLOC1101#ifndef CONFIG_DEBUG_LOCK_ALLOC
1094int __sched1102int __sched