diff options
author | David Miller <davem@davemloft.net> | 2008-07-30 00:45:03 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-07-31 12:38:28 -0400 |
commit | 419ca3f13532793b81aff09f80c60af3eacbb43d (patch) | |
tree | eb2d82e52917ebccff269a51e90868ce229336b2 /kernel/lockdep_proc.c | |
parent | 6e86841d05f371b5b9b86ce76c02aaee83352298 (diff) |
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>
Diffstat (limited to 'kernel/lockdep_proc.c')
-rw-r--r-- | kernel/lockdep_proc.c | 34 |
1 files changed, 3 insertions, 31 deletions
diff --git a/kernel/lockdep_proc.c b/kernel/lockdep_proc.c index 9b0e940e2545..6252ff799d19 100644 --- a/kernel/lockdep_proc.c +++ b/kernel/lockdep_proc.c | |||
@@ -63,34 +63,6 @@ static void l_stop(struct seq_file *m, void *v) | |||
63 | { | 63 | { |
64 | } | 64 | } |
65 | 65 | ||
66 | static unsigned long count_forward_deps(struct lock_class *class) | ||
67 | { | ||
68 | struct lock_list *entry; | ||
69 | unsigned long ret = 1; | ||
70 | |||
71 | /* | ||
72 | * Recurse this class's dependency list: | ||
73 | */ | ||
74 | list_for_each_entry(entry, &class->locks_after, entry) | ||
75 | ret += count_forward_deps(entry->class); | ||
76 | |||
77 | return ret; | ||
78 | } | ||
79 | |||
80 | static unsigned long count_backward_deps(struct lock_class *class) | ||
81 | { | ||
82 | struct lock_list *entry; | ||
83 | unsigned long ret = 1; | ||
84 | |||
85 | /* | ||
86 | * Recurse this class's dependency list: | ||
87 | */ | ||
88 | list_for_each_entry(entry, &class->locks_before, entry) | ||
89 | ret += count_backward_deps(entry->class); | ||
90 | |||
91 | return ret; | ||
92 | } | ||
93 | |||
94 | static void print_name(struct seq_file *m, struct lock_class *class) | 66 | static void print_name(struct seq_file *m, struct lock_class *class) |
95 | { | 67 | { |
96 | char str[128]; | 68 | char str[128]; |
@@ -124,10 +96,10 @@ static int l_show(struct seq_file *m, void *v) | |||
124 | #ifdef CONFIG_DEBUG_LOCKDEP | 96 | #ifdef CONFIG_DEBUG_LOCKDEP |
125 | seq_printf(m, " OPS:%8ld", class->ops); | 97 | seq_printf(m, " OPS:%8ld", class->ops); |
126 | #endif | 98 | #endif |
127 | nr_forward_deps = count_forward_deps(class); | 99 | nr_forward_deps = lockdep_count_forward_deps(class); |
128 | seq_printf(m, " FD:%5ld", nr_forward_deps); | 100 | seq_printf(m, " FD:%5ld", nr_forward_deps); |
129 | 101 | ||
130 | nr_backward_deps = count_backward_deps(class); | 102 | nr_backward_deps = lockdep_count_backward_deps(class); |
131 | seq_printf(m, " BD:%5ld", nr_backward_deps); | 103 | seq_printf(m, " BD:%5ld", nr_backward_deps); |
132 | 104 | ||
133 | get_usage_chars(class, &c1, &c2, &c3, &c4); | 105 | get_usage_chars(class, &c1, &c2, &c3, &c4); |
@@ -350,7 +322,7 @@ static int lockdep_stats_show(struct seq_file *m, void *v) | |||
350 | if (class->usage_mask & LOCKF_ENABLED_HARDIRQS_READ) | 322 | if (class->usage_mask & LOCKF_ENABLED_HARDIRQS_READ) |
351 | nr_hardirq_read_unsafe++; | 323 | nr_hardirq_read_unsafe++; |
352 | 324 | ||
353 | sum_forward_deps += count_forward_deps(class); | 325 | sum_forward_deps += lockdep_count_forward_deps(class); |
354 | } | 326 | } |
355 | #ifdef CONFIG_DEBUG_LOCKDEP | 327 | #ifdef CONFIG_DEBUG_LOCKDEP |
356 | DEBUG_LOCKS_WARN_ON(debug_atomic_read(&nr_unused_locks) != nr_unused); | 328 | DEBUG_LOCKS_WARN_ON(debug_atomic_read(&nr_unused_locks) != nr_unused); |