lockdep: fix combinatorial explosion in lock subgraph traversal

When we traverse the graph, either forwards or backwards, we
are interested in whether a certain property exists somewhere
in a node reachable in the graph.

Therefore it is never necessary to traverse through a node more
than once to get a correct answer to the given query.

Take advantage of this property using a global ID counter so that we
need not clear all the markers in all the lock_class entries before
doing a traversal.  A new ID is choosen when we start to traverse, and
we continue through a lock_class only if it's ID hasn't been marked
with the new value yet.

This short-circuiting is essential especially for high CPU count
systems.  The scheduler has a runqueue per cpu, and needs to take
two runqueue locks at a time, which leads to long chains of
backwards and forwards subgraphs from these runqueue lock nodes.
Without the short-circuit implemented here, a graph traversal on
a runqueue lock can take up to (1 << (N - 1)) checks on a system
with N cpus.

For anything more than 16 cpus or so, lockdep will eventually bring
the machine to a complete standstill.

Signed-off-by: David S. Miller <davem@davemloft.net>
Acked-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
David Miller 2008-07-29 21:45:03 -07:00 committed by Ingo Molnar
parent 6e86841d05
commit 419ca3f135
4 changed files with 93 additions and 31 deletions

View file

@ -63,34 +63,6 @@ static void l_stop(struct seq_file *m, void *v)
{
}
static unsigned long count_forward_deps(struct lock_class *class)
{
struct lock_list *entry;
unsigned long ret = 1;
/*
* Recurse this class's dependency list:
*/
list_for_each_entry(entry, &class->locks_after, entry)
ret += count_forward_deps(entry->class);
return ret;
}
static unsigned long count_backward_deps(struct lock_class *class)
{
struct lock_list *entry;
unsigned long ret = 1;
/*
* Recurse this class's dependency list:
*/
list_for_each_entry(entry, &class->locks_before, entry)
ret += count_backward_deps(entry->class);
return ret;
}
static void print_name(struct seq_file *m, struct lock_class *class)
{
char str[128];
@ -124,10 +96,10 @@ static int l_show(struct seq_file *m, void *v)
#ifdef CONFIG_DEBUG_LOCKDEP
seq_printf(m, " OPS:%8ld", class->ops);
#endif
nr_forward_deps = count_forward_deps(class);
nr_forward_deps = lockdep_count_forward_deps(class);
seq_printf(m, " FD:%5ld", nr_forward_deps);
nr_backward_deps = count_backward_deps(class);
nr_backward_deps = lockdep_count_backward_deps(class);
seq_printf(m, " BD:%5ld", nr_backward_deps);
get_usage_chars(class, &c1, &c2, &c3, &c4);
@ -350,7 +322,7 @@ static int lockdep_stats_show(struct seq_file *m, void *v)
if (class->usage_mask & LOCKF_ENABLED_HARDIRQS_READ)
nr_hardirq_read_unsafe++;
sum_forward_deps += count_forward_deps(class);
sum_forward_deps += lockdep_count_forward_deps(class);
}
#ifdef CONFIG_DEBUG_LOCKDEP
DEBUG_LOCKS_WARN_ON(debug_atomic_read(&nr_unused_locks) != nr_unused);