Commit b526b2e3 authored by Bart Van Assche's avatar Bart Van Assche Committed by Ingo Molnar

locking/lockdep: Check data structure consistency

Debugging lockdep data structure inconsistencies is challenging. Add
code that verifies data structure consistency at runtime. That code is
disabled by default because it is very CPU intensive.
Signed-off-by: default avatarBart Van Assche <bvanassche@acm.org>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Johannes Berg <johannes@sipsolutions.net>
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: Waiman Long <longman@redhat.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: johannes.berg@intel.com
Cc: tj@kernel.org
Link: https://lkml.kernel.org/r/20190214230058.196511-17-bvanassche@acm.orgSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent de4643a7
...@@ -74,6 +74,8 @@ module_param(lock_stat, int, 0644); ...@@ -74,6 +74,8 @@ module_param(lock_stat, int, 0644);
#define lock_stat 0 #define lock_stat 0
#endif #endif
static bool check_data_structure_consistency;
/* /*
* lockdep_lock: protects the lockdep graph, the hashes and the * lockdep_lock: protects the lockdep graph, the hashes and the
* class/list/hash allocators. * class/list/hash allocators.
...@@ -775,6 +777,168 @@ static bool assign_lock_key(struct lockdep_map *lock) ...@@ -775,6 +777,168 @@ static bool assign_lock_key(struct lockdep_map *lock)
return true; return true;
} }
/* Check whether element @e occurs in list @h */
static bool in_list(struct list_head *e, struct list_head *h)
{
struct list_head *f;
list_for_each(f, h) {
if (e == f)
return true;
}
return false;
}
/*
* Check whether entry @e occurs in any of the locks_after or locks_before
* lists.
*/
static bool in_any_class_list(struct list_head *e)
{
struct lock_class *class;
int i;
for (i = 0; i < ARRAY_SIZE(lock_classes); i++) {
class = &lock_classes[i];
if (in_list(e, &class->locks_after) ||
in_list(e, &class->locks_before))
return true;
}
return false;
}
static bool class_lock_list_valid(struct lock_class *c, struct list_head *h)
{
struct lock_list *e;
list_for_each_entry(e, h, entry) {
if (e->links_to != c) {
printk(KERN_INFO "class %s: mismatch for lock entry %ld; class %s <> %s",
c->name ? : "(?)",
(unsigned long)(e - list_entries),
e->links_to && e->links_to->name ?
e->links_to->name : "(?)",
e->class && e->class->name ? e->class->name :
"(?)");
return false;
}
}
return true;
}
static u16 chain_hlocks[];
static bool check_lock_chain_key(struct lock_chain *chain)
{
#ifdef CONFIG_PROVE_LOCKING
u64 chain_key = 0;
int i;
for (i = chain->base; i < chain->base + chain->depth; i++)
chain_key = iterate_chain_key(chain_key, chain_hlocks[i] + 1);
/*
* The 'unsigned long long' casts avoid that a compiler warning
* is reported when building tools/lib/lockdep.
*/
if (chain->chain_key != chain_key)
printk(KERN_INFO "chain %lld: key %#llx <> %#llx\n",
(unsigned long long)(chain - lock_chains),
(unsigned long long)chain->chain_key,
(unsigned long long)chain_key);
return chain->chain_key == chain_key;
#else
return true;
#endif
}
static bool in_any_zapped_class_list(struct lock_class *class)
{
struct pending_free *pf;
int i;
for (i = 0, pf = delayed_free.pf; i < ARRAY_SIZE(delayed_free.pf);
i++, pf++)
if (in_list(&class->lock_entry, &pf->zapped))
return true;
return false;
}
static bool check_data_structures(void)
{
struct lock_class *class;
struct lock_chain *chain;
struct hlist_head *head;
struct lock_list *e;
int i;
/* Check whether all classes occur in a lock list. */
for (i = 0; i < ARRAY_SIZE(lock_classes); i++) {
class = &lock_classes[i];
if (!in_list(&class->lock_entry, &all_lock_classes) &&
!in_list(&class->lock_entry, &free_lock_classes) &&
!in_any_zapped_class_list(class)) {
printk(KERN_INFO "class %px/%s is not in any class list\n",
class, class->name ? : "(?)");
return false;
return false;
}
}
/* Check whether all classes have valid lock lists. */
for (i = 0; i < ARRAY_SIZE(lock_classes); i++) {
class = &lock_classes[i];
if (!class_lock_list_valid(class, &class->locks_before))
return false;
if (!class_lock_list_valid(class, &class->locks_after))
return false;
}
/* Check the chain_key of all lock chains. */
for (i = 0; i < ARRAY_SIZE(chainhash_table); i++) {
head = chainhash_table + i;
hlist_for_each_entry_rcu(chain, head, entry) {
if (!check_lock_chain_key(chain))
return false;
}
}
/*
* Check whether all list entries that are in use occur in a class
* lock list.
*/
for_each_set_bit(i, list_entries_in_use, ARRAY_SIZE(list_entries)) {
e = list_entries + i;
if (!in_any_class_list(&e->entry)) {
printk(KERN_INFO "list entry %d is not in any class list; class %s <> %s\n",
(unsigned int)(e - list_entries),
e->class->name ? : "(?)",
e->links_to->name ? : "(?)");
return false;
}
}
/*
* Check whether all list entries that are not in use do not occur in
* a class lock list.
*/
for_each_clear_bit(i, list_entries_in_use, ARRAY_SIZE(list_entries)) {
e = list_entries + i;
if (in_any_class_list(&e->entry)) {
printk(KERN_INFO "list entry %d occurs in a class list; class %s <> %s\n",
(unsigned int)(e - list_entries),
e->class && e->class->name ? e->class->name :
"(?)",
e->links_to && e->links_to->name ?
e->links_to->name : "(?)");
return false;
}
}
return true;
}
/* /*
* Initialize the lock_classes[] array elements, the free_lock_classes list * Initialize the lock_classes[] array elements, the free_lock_classes list
* and also the delayed_free structure. * and also the delayed_free structure.
...@@ -4389,6 +4553,9 @@ static void __free_zapped_classes(struct pending_free *pf) ...@@ -4389,6 +4553,9 @@ static void __free_zapped_classes(struct pending_free *pf)
{ {
struct lock_class *class; struct lock_class *class;
if (check_data_structure_consistency)
WARN_ON_ONCE(!check_data_structures());
list_for_each_entry(class, &pf->zapped, lock_entry) list_for_each_entry(class, &pf->zapped, lock_entry)
reinit_class(class); reinit_class(class);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment