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>
This commit is contained in:
parent
512e67f991
commit
94c61c0aef
|
@ -25,28 +25,38 @@
|
||||||
|
|
||||||
static void *l_next(struct seq_file *m, void *v, loff_t *pos)
|
static void *l_next(struct seq_file *m, void *v, loff_t *pos)
|
||||||
{
|
{
|
||||||
struct lock_class *class = v;
|
struct lock_class *class;
|
||||||
|
|
||||||
(*pos)++;
|
(*pos)++;
|
||||||
|
|
||||||
if (class->lock_entry.next != &all_lock_classes)
|
if (v == SEQ_START_TOKEN)
|
||||||
class = list_entry(class->lock_entry.next, struct lock_class,
|
class = m->private;
|
||||||
lock_entry);
|
else {
|
||||||
else
|
class = v;
|
||||||
class = NULL;
|
|
||||||
m->private = class;
|
if (class->lock_entry.next != &all_lock_classes)
|
||||||
|
class = list_entry(class->lock_entry.next,
|
||||||
|
struct lock_class, lock_entry);
|
||||||
|
else
|
||||||
|
class = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return class;
|
return class;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *l_start(struct seq_file *m, loff_t *pos)
|
static void *l_start(struct seq_file *m, loff_t *pos)
|
||||||
{
|
{
|
||||||
struct lock_class *class = m->private;
|
struct lock_class *class;
|
||||||
|
loff_t i = 0;
|
||||||
|
|
||||||
if (&class->lock_entry == all_lock_classes.next)
|
if (*pos == 0)
|
||||||
seq_printf(m, "all lock classes:\n");
|
return SEQ_START_TOKEN;
|
||||||
|
|
||||||
return class;
|
list_for_each_entry(class, &all_lock_classes, lock_entry) {
|
||||||
|
if (++i == *pos)
|
||||||
|
return class;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void l_stop(struct seq_file *m, void *v)
|
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)
|
||||||
static int l_show(struct seq_file *m, void *v)
|
static int l_show(struct seq_file *m, void *v)
|
||||||
{
|
{
|
||||||
unsigned long nr_forward_deps, nr_backward_deps;
|
unsigned long nr_forward_deps, nr_backward_deps;
|
||||||
struct lock_class *class = m->private;
|
struct lock_class *class = v;
|
||||||
struct lock_list *entry;
|
struct lock_list *entry;
|
||||||
char c1, c2, c3, c4;
|
char c1, c2, c3, c4;
|
||||||
|
|
||||||
|
if (v == SEQ_START_TOKEN) {
|
||||||
|
seq_printf(m, "all lock classes:\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
seq_printf(m, "%p", class->key);
|
seq_printf(m, "%p", class->key);
|
||||||
#ifdef CONFIG_DEBUG_LOCKDEP
|
#ifdef CONFIG_DEBUG_LOCKDEP
|
||||||
seq_printf(m, " OPS:%8ld", class->ops);
|
seq_printf(m, " OPS:%8ld", class->ops);
|
||||||
|
@ -523,10 +538,11 @@ static void *ls_start(struct seq_file *m, loff_t *pos)
|
||||||
{
|
{
|
||||||
struct lock_stat_seq *data = m->private;
|
struct lock_stat_seq *data = m->private;
|
||||||
|
|
||||||
if (data->iter == data->stats)
|
if (*pos == 0)
|
||||||
seq_header(m);
|
return SEQ_START_TOKEN;
|
||||||
|
|
||||||
if (data->iter == data->iter_end)
|
data->iter = data->stats + *pos;
|
||||||
|
if (data->iter >= data->iter_end)
|
||||||
data->iter = NULL;
|
data->iter = NULL;
|
||||||
|
|
||||||
return data->iter;
|
return data->iter;
|
||||||
|
@ -538,8 +554,13 @@ static void *ls_next(struct seq_file *m, void *v, loff_t *pos)
|
||||||
|
|
||||||
(*pos)++;
|
(*pos)++;
|
||||||
|
|
||||||
data->iter = v;
|
if (v == SEQ_START_TOKEN)
|
||||||
data->iter++;
|
data->iter = data->stats;
|
||||||
|
else {
|
||||||
|
data->iter = v;
|
||||||
|
data->iter++;
|
||||||
|
}
|
||||||
|
|
||||||
if (data->iter == data->iter_end)
|
if (data->iter == data->iter_end)
|
||||||
data->iter = NULL;
|
data->iter = NULL;
|
||||||
|
|
||||||
|
@ -552,9 +573,11 @@ static void ls_stop(struct seq_file *m, void *v)
|
||||||
|
|
||||||
static int ls_show(struct seq_file *m, void *v)
|
static int ls_show(struct seq_file *m, void *v)
|
||||||
{
|
{
|
||||||
struct lock_stat_seq *data = m->private;
|
if (v == SEQ_START_TOKEN)
|
||||||
|
seq_header(m);
|
||||||
|
else
|
||||||
|
seq_stats(m, v);
|
||||||
|
|
||||||
seq_stats(m, data->iter);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue