/*
* kernel/lockdep_proc.c
*
* Runtime locking correctness validator
*
* Started by Ingo Molnar:
*
* Copyright (C) 2006,2007 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
* Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
*
* Code for /proc/lockdep and /proc/lockdep_stats:
*
*/
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/kallsyms.h>
#include <linux/debug_locks.h>
#include <linux/vmalloc.h>
#include <linux/sort.h>
#include <asm/uaccess.h>
#include <asm/div64.h>
#include "lockdep_internals.h"
static void *l_next(struct seq_file *m, void *v, loff_t *pos)
{
struct lock_class *class;
(*pos)++;
if (v == SEQ_START_TOKEN)
class = m->private;
else {
class = v;
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;
}
static void *l_start(struct seq_file *m, loff_t *pos)
{
struct lock_class *class;
loff_t i = 0;
if (*pos == 0)
return SEQ_START_TOKEN;
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 print_name(struct seq_file *m, struct lock_class *class)
{
char str[128];
const char *name = class->name;
if (!name) {
name = __get_key_name(class->key, str);
seq_printf(m, "%s", name);
} else{
seq_printf(m, "%s", name);
if (class->name_version > 1)
seq_printf(m, "#%d", class->name_version);
if (class->subclass)
seq_printf(m, "/%d", class->subclass);
}
}
static int l_show(struct seq_file *m, void *v)
{
struct lock_class *class = v;
struct lock_list *entry;
char usage[LOCK_USAGE_CHARS];
if (v == SEQ_START_TOKEN) {
seq_printf(m, "all lock classes:\n");
return 0;
}
seq_printf(m, "%p", class->key);
#ifdef CONFIG_DEBUG_LOCKDEP
seq_printf(m, " OPS:%8ld", class->ops);
#endif
#ifdef CONFIG_PROVE_LOCKING
seq_printf(m, " FD:%5ld", lockdep_count_forward_deps(class));
seq_printf(m, " BD:%5ld", lockdep_count_backward_deps(class));
#endif
get_usage_chars(class, usage);
seq_printf(m, " %s", usage);
seq_printf(m, ": ");
print_name(m, class);
seq_puts(m, "\n");
list_for_each_entry(entry, &class->locks_after, entry) {
if (entry->distance == 1) {
seq_printf(m, " -> [%p] ", entry->class->key);
print_name(m, entry->class);
seq_puts(m, "\n");
}
}
seq_puts(m, "\n");
return 0;
}
static const struct seq_operations lockdep_ops = {
.start = l_start,
.next = l_next,
.stop = l_stop,
.show = l_show,
};
static int lockdep_open(struct inode *inode, struct file *file)
{
int res = seq_open(file, &lockdep_ops);
if (!res) {
struct seq_file *m = file->private_data;
if (!list_empty(&all_lock_classes))
m->private = list_entry(all_lock_classes.next,
struct lock_class, lock_entry);
else
m->private = NULL;
}
return res;
}
static const struct file_operations proc_lockdep_operations = {
.open = lockdep_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
#ifdef CONFIG_PROVE_LOCKING
static void *lc_next(struct seq_file *m, void *v, loff_t *pos)
{
struct lock_chain *chain;
(*pos)++;
if (v == SEQ_START_TOKEN)
chain = m->private;
else {
chain = v;
if (*pos - 1 < nr_lock_chains)
chain = lock_chains + (*pos - 1);
else
chain = NULL;
}
return chain;
}
static void *lc_start(struct seq_file *m, loff_t *pos)
{
if (*pos == 0)
return SEQ_START_TOKEN;
if (*pos - 1 < nr_lock_chains)
return lock_chains + (*pos - 1);
return NULL;
}
static void lc_stop(struct seq_file *m, void *v)
{
}
static int lc_show(struct seq_file *m, void *v)
{
struct lock_chain *chain = v;
struct lock_class *class;
int i;
if (v == SEQ_START_TOKEN) {
seq_printf(m,<