locking/lockdep: Detect chain_key collisions
Add detection for chain_key collision under CONFIG_DEBUG_LOCKDEP. When a collision is detected the problem is reported and all lock debugging is turned off. Tested using liblockdep and the added tests before and after applying the fix, confirming both that the code added for the detection correctly reports the problem and that the fix actually fixes it. Tested tweaking lockdep to generate false collisions and verified that the problem is reported and that lock debugging is turned off. Also tested with lockdep's test suite after applying the patch: [ 0.000000] Good, all 253 testcases passed! | Signed-off-by: Alfredo Alvarez Fernandez <alfredoalvarezernandez@gmail.com> Cc: Alfredo Alvarez Fernandez <alfredoalvarezfernandez@gmail.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: sasha.levin@oracle.com Link: http://lkml.kernel.org/r/1455864533-7536-4-git-send-email-alfredoalvarezernandez@gmail.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Родитель
5f18ab5c6b
Коммит
9e4e7554e7
|
@ -1981,6 +1981,53 @@ struct lock_class *lock_chain_get_class(struct lock_chain *chain, int i)
|
|||
return lock_classes + chain_hlocks[chain->base + i];
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the index of the first held_lock of the current chain
|
||||
*/
|
||||
static inline int get_first_held_lock(struct task_struct *curr,
|
||||
struct held_lock *hlock)
|
||||
{
|
||||
int i;
|
||||
struct held_lock *hlock_curr;
|
||||
|
||||
for (i = curr->lockdep_depth - 1; i >= 0; i--) {
|
||||
hlock_curr = curr->held_locks + i;
|
||||
if (hlock_curr->irq_context != hlock->irq_context)
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return ++i;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks whether the chain and the current held locks are consistent
|
||||
* in depth and also in content. If they are not it most likely means
|
||||
* that there was a collision during the calculation of the chain_key.
|
||||
* Returns: 0 not passed, 1 passed
|
||||
*/
|
||||
static int check_no_collision(struct task_struct *curr,
|
||||
struct held_lock *hlock,
|
||||
struct lock_chain *chain)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_LOCKDEP
|
||||
int i, j, id;
|
||||
|
||||
i = get_first_held_lock(curr, hlock);
|
||||
|
||||
if (DEBUG_LOCKS_WARN_ON(chain->depth != curr->lockdep_depth - (i - 1)))
|
||||
return 0;
|
||||
|
||||
for (j = 0; j < chain->depth - 1; j++, i++) {
|
||||
id = curr->held_locks[i].class_idx - 1;
|
||||
|
||||
if (DEBUG_LOCKS_WARN_ON(chain_hlocks[chain->base + j] != id))
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up a dependency chain. If the key is not present yet then
|
||||
* add it and return 1 - in this case the new dependency chain is
|
||||
|
@ -1994,7 +2041,6 @@ static inline int lookup_chain_cache(struct task_struct *curr,
|
|||
struct lock_class *class = hlock_class(hlock);
|
||||
struct hlist_head *hash_head = chainhashentry(chain_key);
|
||||
struct lock_chain *chain;
|
||||
struct held_lock *hlock_curr;
|
||||
int i, j;
|
||||
|
||||
/*
|
||||
|
@ -2012,6 +2058,9 @@ static inline int lookup_chain_cache(struct task_struct *curr,
|
|||
if (chain->chain_key == chain_key) {
|
||||
cache_hit:
|
||||
debug_atomic_inc(chain_lookup_hits);
|
||||
if (!check_no_collision(curr, hlock, chain))
|
||||
return 0;
|
||||
|
||||
if (very_verbose(class))
|
||||
printk("\nhash chain already cached, key: "
|
||||
"%016Lx tail class: [%p] %s\n",
|
||||
|
@ -2049,13 +2098,7 @@ cache_hit:
|
|||
chain = lock_chains + nr_lock_chains++;
|
||||
chain->chain_key = chain_key;
|
||||
chain->irq_context = hlock->irq_context;
|
||||
/* Find the first held_lock of current chain */
|
||||
for (i = curr->lockdep_depth - 1; i >= 0; i--) {
|
||||
hlock_curr = curr->held_locks + i;
|
||||
if (hlock_curr->irq_context != hlock->irq_context)
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
i = get_first_held_lock(curr, hlock);
|
||||
chain->depth = curr->lockdep_depth + 1 - i;
|
||||
if (likely(nr_chain_hlocks + chain->depth <= MAX_LOCKDEP_CHAIN_HLOCKS)) {
|
||||
chain->base = nr_chain_hlocks;
|
||||
|
|
Загрузка…
Ссылка в новой задаче