aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorImre Deak <imre.deak@intel.com>2019-05-24 16:15:08 -0400
committerIngo Molnar <mingo@kernel.org>2019-06-03 06:32:29 -0400
commit8c8889d8eaf4501ae4aaf870b6f8f55db5d5109a (patch)
treeba45b926b4a3480b687e13c2192519bea04b7b0b
parentbf998b98f5bce4ebc97b3980016f54fabb7a4958 (diff)
locking/lockdep: Fix OOO unlock when hlocks need merging
The sequence static DEFINE_WW_CLASS(test_ww_class); struct ww_acquire_ctx ww_ctx; struct ww_mutex ww_lock_a; struct ww_mutex ww_lock_b; struct mutex lock_c; struct mutex lock_d; ww_acquire_init(&ww_ctx, &test_ww_class); ww_mutex_init(&ww_lock_a, &test_ww_class); ww_mutex_init(&ww_lock_b, &test_ww_class); mutex_init(&lock_c); ww_mutex_lock(&ww_lock_a, &ww_ctx); mutex_lock(&lock_c); ww_mutex_lock(&ww_lock_b, &ww_ctx); mutex_unlock(&lock_c); (*) ww_mutex_unlock(&ww_lock_b); ww_mutex_unlock(&ww_lock_a); ww_acquire_fini(&ww_ctx); triggers the following WARN in __lock_release() when doing the unlock at *: DEBUG_LOCKS_WARN_ON(curr->lockdep_depth != depth - 1); The problem is that the WARN check doesn't take into account the merging of ww_lock_a and ww_lock_b which results in decreasing curr->lockdep_depth by 2 not only 1. Note that the following sequence doesn't trigger the WARN, since there won't be any hlock merging. ww_acquire_init(&ww_ctx, &test_ww_class); ww_mutex_init(&ww_lock_a, &test_ww_class); ww_mutex_init(&ww_lock_b, &test_ww_class); mutex_init(&lock_c); mutex_init(&lock_d); ww_mutex_lock(&ww_lock_a, &ww_ctx); mutex_lock(&lock_c); mutex_lock(&lock_d); ww_mutex_lock(&ww_lock_b, &ww_ctx); mutex_unlock(&lock_d); ww_mutex_unlock(&ww_lock_b); ww_mutex_unlock(&ww_lock_a); mutex_unlock(&lock_c); ww_acquire_fini(&ww_ctx); In general both of the above two sequences are valid and shouldn't trigger any lockdep warning. Fix this by taking the decrement due to the hlock merging into account during lock release and hlock class re-setting. Merging can't happen during lock downgrading since there won't be a new possibility to merge hlocks in that case, so add a WARN if merging still happens then. Signed-off-by: Imre Deak <imre.deak@intel.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Will Deacon <will.deacon@arm.com> Cc: ville.syrjala@linux.intel.com Link: https://lkml.kernel.org/r/20190524201509.9199-1-imre.deak@intel.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--kernel/locking/lockdep.c41
1 files changed, 29 insertions, 12 deletions
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 2168e94715b9..6c97f67ec321 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -3808,7 +3808,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
3808 hlock->references = 2; 3808 hlock->references = 2;
3809 } 3809 }
3810 3810
3811 return 1; 3811 return 2;
3812 } 3812 }
3813 } 3813 }
3814 3814
@@ -4011,22 +4011,33 @@ out:
4011} 4011}
4012 4012
4013static int reacquire_held_locks(struct task_struct *curr, unsigned int depth, 4013static int reacquire_held_locks(struct task_struct *curr, unsigned int depth,
4014 int idx) 4014 int idx, unsigned int *merged)
4015{ 4015{
4016 struct held_lock *hlock; 4016 struct held_lock *hlock;
4017 int first_idx = idx;
4017 4018
4018 if (DEBUG_LOCKS_WARN_ON(!irqs_disabled())) 4019 if (DEBUG_LOCKS_WARN_ON(!irqs_disabled()))
4019 return 0; 4020 return 0;
4020 4021
4021 for (hlock = curr->held_locks + idx; idx < depth; idx++, hlock++) { 4022 for (hlock = curr->held_locks + idx; idx < depth; idx++, hlock++) {
4022 if (!__lock_acquire(hlock->instance, 4023 switch (__lock_acquire(hlock->instance,
4023 hlock_class(hlock)->subclass, 4024 hlock_class(hlock)->subclass,
4024 hlock->trylock, 4025 hlock->trylock,
4025 hlock->read, hlock->check, 4026 hlock->read, hlock->check,
4026 hlock->hardirqs_off, 4027 hlock->hardirqs_off,
4027 hlock->nest_lock, hlock->acquire_ip, 4028 hlock->nest_lock, hlock->acquire_ip,
4028 hlock->references, hlock->pin_count)) 4029 hlock->references, hlock->pin_count)) {
4030 case 0:
4029 return 1; 4031 return 1;
4032 case 1:
4033 break;
4034 case 2:
4035 *merged += (idx == first_idx);
4036 break;
4037 default:
4038 WARN_ON(1);
4039 return 0;
4040 }
4030 } 4041 }
4031 return 0; 4042 return 0;
4032} 4043}
@@ -4037,9 +4048,9 @@ __lock_set_class(struct lockdep_map *lock, const char *name,
4037 unsigned long ip) 4048 unsigned long ip)
4038{ 4049{
4039 struct task_struct *curr = current; 4050 struct task_struct *curr = current;
4051 unsigned int depth, merged = 0;
4040 struct held_lock *hlock; 4052 struct held_lock *hlock;
4041 struct lock_class *class; 4053 struct lock_class *class;
4042 unsigned int depth;
4043 int i; 4054 int i;
4044 4055
4045 if (unlikely(!debug_locks)) 4056 if (unlikely(!debug_locks))
@@ -4066,14 +4077,14 @@ __lock_set_class(struct lockdep_map *lock, const char *name,
4066 curr->lockdep_depth = i; 4077 curr->lockdep_depth = i;
4067 curr->curr_chain_key = hlock->prev_chain_key; 4078 curr->curr_chain_key = hlock->prev_chain_key;
4068 4079
4069 if (reacquire_held_locks(curr, depth, i)) 4080 if (reacquire_held_locks(curr, depth, i, &merged))
4070 return 0; 4081 return 0;
4071 4082
4072 /* 4083 /*
4073 * I took it apart and put it back together again, except now I have 4084 * I took it apart and put it back together again, except now I have
4074 * these 'spare' parts.. where shall I put them. 4085 * these 'spare' parts.. where shall I put them.
4075 */ 4086 */
4076 if (DEBUG_LOCKS_WARN_ON(curr->lockdep_depth != depth)) 4087 if (DEBUG_LOCKS_WARN_ON(curr->lockdep_depth != depth - merged))
4077 return 0; 4088 return 0;
4078 return 1; 4089 return 1;
4079} 4090}
@@ -4081,8 +4092,8 @@ __lock_set_class(struct lockdep_map *lock, const char *name,
4081static int __lock_downgrade(struct lockdep_map *lock, unsigned long ip) 4092static int __lock_downgrade(struct lockdep_map *lock, unsigned long ip)
4082{ 4093{
4083 struct task_struct *curr = current; 4094 struct task_struct *curr = current;
4095 unsigned int depth, merged = 0;
4084 struct held_lock *hlock; 4096 struct held_lock *hlock;
4085 unsigned int depth;
4086 int i; 4097 int i;
4087 4098
4088 if (unlikely(!debug_locks)) 4099 if (unlikely(!debug_locks))
@@ -4109,7 +4120,11 @@ static int __lock_downgrade(struct lockdep_map *lock, unsigned long ip)
4109 hlock->read = 1; 4120 hlock->read = 1;
4110 hlock->acquire_ip = ip; 4121 hlock->acquire_ip = ip;
4111 4122
4112 if (reacquire_held_locks(curr, depth, i)) 4123 if (reacquire_held_locks(curr, depth, i, &merged))
4124 return 0;
4125
4126 /* Merging can't happen with unchanged classes.. */
4127 if (DEBUG_LOCKS_WARN_ON(merged))
4113 return 0; 4128 return 0;
4114 4129
4115 /* 4130 /*
@@ -4118,6 +4133,7 @@ static int __lock_downgrade(struct lockdep_map *lock, unsigned long ip)
4118 */ 4133 */
4119 if (DEBUG_LOCKS_WARN_ON(curr->lockdep_depth != depth)) 4134 if (DEBUG_LOCKS_WARN_ON(curr->lockdep_depth != depth))
4120 return 0; 4135 return 0;
4136
4121 return 1; 4137 return 1;
4122} 4138}
4123 4139
@@ -4132,8 +4148,8 @@ static int
4132__lock_release(struct lockdep_map *lock, unsigned long ip) 4148__lock_release(struct lockdep_map *lock, unsigned long ip)
4133{ 4149{
4134 struct task_struct *curr = current; 4150 struct task_struct *curr = current;
4151 unsigned int depth, merged = 1;
4135 struct held_lock *hlock; 4152 struct held_lock *hlock;
4136 unsigned int depth;
4137 int i; 4153 int i;
4138 4154
4139 if (unlikely(!debug_locks)) 4155 if (unlikely(!debug_locks))
@@ -4192,14 +4208,15 @@ __lock_release(struct lockdep_map *lock, unsigned long ip)
4192 if (i == depth-1) 4208 if (i == depth-1)
4193 return 1; 4209 return 1;
4194 4210
4195 if (reacquire_held_locks(curr, depth, i + 1)) 4211 if (reacquire_held_locks(curr, depth, i + 1, &merged))
4196 return 0; 4212 return 0;
4197 4213
4198 /* 4214 /*
4199 * We had N bottles of beer on the wall, we drank one, but now 4215 * We had N bottles of beer on the wall, we drank one, but now
4200 * there's not N-1 bottles of beer left on the wall... 4216 * there's not N-1 bottles of beer left on the wall...
4217 * Pouring two of the bottles together is acceptable.
4201 */ 4218 */
4202 DEBUG_LOCKS_WARN_ON(curr->lockdep_depth != depth-1); 4219 DEBUG_LOCKS_WARN_ON(curr->lockdep_depth != depth - merged);
4203 4220
4204 /* 4221 /*
4205 * Since reacquire_held_locks() would have called check_chain_key() 4222 * Since reacquire_held_locks() would have called check_chain_key()