diff options
author | Tim Pepper <lnxninja@linux.vnet.ibm.com> | 2007-10-11 16:11:11 -0400 |
---|---|---|
committer | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2007-10-11 16:11:11 -0400 |
commit | 94c61c0aeffe64452e742087cf803d0347ef8418 (patch) | |
tree | 95d836151d8239bb0303d4903d31b4c5180aa40b /kernel/lockdep_proc.c | |
parent | 512e67f991c8886de75a65b854d7c19a55fb5b8a (diff) |
lockdep: Avoid /proc/lockdep & lock_stat infinite output
Both /proc/lockdep and /proc/lock_stat output may loop infinitely.
When a read() requests an amount of data smaller than the amount of data
that the seq_file's foo_show() outputs, the output starts looping and
outputs the "stuck" element's data infinitely. There may be multiple
sequential calls to foo_start(), foo_next()/foo_show(), and foo_stop()
for a single open with sequential read of the file. The _start() does not
have to start with the 0th element and _show() might be called multiple
times in a row for the same element for a given open/read of the seq_file.
Also header output should not be happening in _start(). All output should
be in _show(), which SEQ_START_TOKEN is meant to help. Having output in
_start() may also negatively impact seq_file's seq_read() and traverse()
accounting.
Signed-off-by: Tim Pepper <lnxninja@linux.vnet.ibm.com>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Al Viro <viro@ftp.linux.org.uk>
Diffstat (limited to 'kernel/lockdep_proc.c')
-rw-r--r-- | kernel/lockdep_proc.c | 61 |
1 files changed, 42 insertions, 19 deletions
diff --git a/kernel/lockdep_proc.c b/kernel/lockdep_proc.c index c851b2dcc685..8a135bd163c2 100644 --- a/kernel/lockdep_proc.c +++ b/kernel/lockdep_proc.c | |||
@@ -25,28 +25,38 @@ | |||
25 | 25 | ||
26 | static void *l_next(struct seq_file *m, void *v, loff_t *pos) | 26 | static void *l_next(struct seq_file *m, void *v, loff_t *pos) |
27 | { | 27 | { |
28 | struct lock_class *class = v; | 28 | struct lock_class *class; |
29 | 29 | ||
30 | (*pos)++; | 30 | (*pos)++; |
31 | 31 | ||
32 | if (class->lock_entry.next != &all_lock_classes) | 32 | if (v == SEQ_START_TOKEN) |
33 | class = list_entry(class->lock_entry.next, struct lock_class, | 33 | class = m->private; |
34 | lock_entry); | 34 | else { |
35 | else | 35 | class = v; |
36 | class = NULL; | 36 | |
37 | m->private = class; | 37 | if (class->lock_entry.next != &all_lock_classes) |
38 | class = list_entry(class->lock_entry.next, | ||
39 | struct lock_class, lock_entry); | ||
40 | else | ||
41 | class = NULL; | ||
42 | } | ||
38 | 43 | ||
39 | return class; | 44 | return class; |
40 | } | 45 | } |
41 | 46 | ||
42 | static void *l_start(struct seq_file *m, loff_t *pos) | 47 | static void *l_start(struct seq_file *m, loff_t *pos) |
43 | { | 48 | { |
44 | struct lock_class *class = m->private; | 49 | struct lock_class *class; |
50 | loff_t i = 0; | ||
45 | 51 | ||
46 | if (&class->lock_entry == all_lock_classes.next) | 52 | if (*pos == 0) |
47 | seq_printf(m, "all lock classes:\n"); | 53 | return SEQ_START_TOKEN; |
48 | 54 | ||
49 | return class; | 55 | list_for_each_entry(class, &all_lock_classes, lock_entry) { |
56 | if (++i == *pos) | ||
57 | return class; | ||
58 | } | ||
59 | return NULL; | ||
50 | } | 60 | } |
51 | 61 | ||
52 | static void l_stop(struct seq_file *m, void *v) | 62 | static void l_stop(struct seq_file *m, void *v) |
@@ -101,10 +111,15 @@ static void print_name(struct seq_file *m, struct lock_class *class) | |||
101 | static int l_show(struct seq_file *m, void *v) | 111 | static int l_show(struct seq_file *m, void *v) |
102 | { | 112 | { |
103 | unsigned long nr_forward_deps, nr_backward_deps; | 113 | unsigned long nr_forward_deps, nr_backward_deps; |
104 | struct lock_class *class = m->private; | 114 | struct lock_class *class = v; |
105 | struct lock_list *entry; | 115 | struct lock_list *entry; |
106 | char c1, c2, c3, c4; | 116 | char c1, c2, c3, c4; |
107 | 117 | ||
118 | if (v == SEQ_START_TOKEN) { | ||
119 | seq_printf(m, "all lock classes:\n"); | ||
120 | return 0; | ||
121 | } | ||
122 | |||
108 | seq_printf(m, "%p", class->key); | 123 | seq_printf(m, "%p", class->key); |
109 | #ifdef CONFIG_DEBUG_LOCKDEP | 124 | #ifdef CONFIG_DEBUG_LOCKDEP |
110 | seq_printf(m, " OPS:%8ld", class->ops); | 125 | seq_printf(m, " OPS:%8ld", class->ops); |
@@ -523,10 +538,11 @@ static void *ls_start(struct seq_file *m, loff_t *pos) | |||
523 | { | 538 | { |
524 | struct lock_stat_seq *data = m->private; | 539 | struct lock_stat_seq *data = m->private; |
525 | 540 | ||
526 | if (data->iter == data->stats) | 541 | if (*pos == 0) |
527 | seq_header(m); | 542 | return SEQ_START_TOKEN; |
528 | 543 | ||
529 | if (data->iter == data->iter_end) | 544 | data->iter = data->stats + *pos; |
545 | if (data->iter >= data->iter_end) | ||
530 | data->iter = NULL; | 546 | data->iter = NULL; |
531 | 547 | ||
532 | return data->iter; | 548 | return data->iter; |
@@ -538,8 +554,13 @@ static void *ls_next(struct seq_file *m, void *v, loff_t *pos) | |||
538 | 554 | ||
539 | (*pos)++; | 555 | (*pos)++; |
540 | 556 | ||
541 | data->iter = v; | 557 | if (v == SEQ_START_TOKEN) |
542 | data->iter++; | 558 | data->iter = data->stats; |
559 | else { | ||
560 | data->iter = v; | ||
561 | data->iter++; | ||
562 | } | ||
563 | |||
543 | if (data->iter == data->iter_end) | 564 | if (data->iter == data->iter_end) |
544 | data->iter = NULL; | 565 | data->iter = NULL; |
545 | 566 | ||
@@ -552,9 +573,11 @@ static void ls_stop(struct seq_file *m, void *v) | |||
552 | 573 | ||
553 | static int ls_show(struct seq_file *m, void *v) | 574 | static int ls_show(struct seq_file *m, void *v) |
554 | { | 575 | { |
555 | struct lock_stat_seq *data = m->private; | 576 | if (v == SEQ_START_TOKEN) |
577 | seq_header(m); | ||
578 | else | ||
579 | seq_stats(m, v); | ||
556 | 580 | ||
557 | seq_stats(m, data->iter); | ||
558 | return 0; | 581 | return 0; |
559 | } | 582 | } |
560 | 583 | ||