pstore: Add persistent function tracing
With this support kernel can save function call chain log into a persistent ram buffer that can be decoded and dumped after reboot through pstore filesystem. It can be used to determine what function was last called before a reset or panic. We store the log in a binary format and then decode it at read time. p.s. Mostly the code comes from trace_persistent.c driver found in the Android git tree, written by Colin Cross <ccross@android.com> (according to sign-off history). I reworked the driver a little bit, and ported it to pstore. Signed-off-by: Anton Vorontsov <anton.vorontsov@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
这个提交包含在:
@@ -27,6 +27,7 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/ramfs.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/sched.h>
|
||||
@@ -52,18 +53,117 @@ struct pstore_private {
|
||||
char data[];
|
||||
};
|
||||
|
||||
struct pstore_ftrace_seq_data {
|
||||
const void *ptr;
|
||||
size_t off;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
#define REC_SIZE sizeof(struct pstore_ftrace_record)
|
||||
|
||||
static void *pstore_ftrace_seq_start(struct seq_file *s, loff_t *pos)
|
||||
{
|
||||
struct pstore_private *ps = s->private;
|
||||
struct pstore_ftrace_seq_data *data;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
data->off = ps->size % REC_SIZE;
|
||||
data->off += *pos * REC_SIZE;
|
||||
if (data->off + REC_SIZE > ps->size) {
|
||||
kfree(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
static void pstore_ftrace_seq_stop(struct seq_file *s, void *v)
|
||||
{
|
||||
kfree(v);
|
||||
}
|
||||
|
||||
static void *pstore_ftrace_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
struct pstore_private *ps = s->private;
|
||||
struct pstore_ftrace_seq_data *data = v;
|
||||
|
||||
data->off += REC_SIZE;
|
||||
if (data->off + REC_SIZE > ps->size)
|
||||
return NULL;
|
||||
|
||||
(*pos)++;
|
||||
return data;
|
||||
}
|
||||
|
||||
static int pstore_ftrace_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct pstore_private *ps = s->private;
|
||||
struct pstore_ftrace_seq_data *data = v;
|
||||
struct pstore_ftrace_record *rec = (void *)(ps->data + data->off);
|
||||
|
||||
seq_printf(s, "%d %08lx %08lx %pf <- %pF\n",
|
||||
pstore_ftrace_decode_cpu(rec), rec->ip, rec->parent_ip,
|
||||
(void *)rec->ip, (void *)rec->parent_ip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct seq_operations pstore_ftrace_seq_ops = {
|
||||
.start = pstore_ftrace_seq_start,
|
||||
.next = pstore_ftrace_seq_next,
|
||||
.stop = pstore_ftrace_seq_stop,
|
||||
.show = pstore_ftrace_seq_show,
|
||||
};
|
||||
|
||||
static ssize_t pstore_file_read(struct file *file, char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct pstore_private *ps = file->private_data;
|
||||
struct seq_file *sf = file->private_data;
|
||||
struct pstore_private *ps = sf->private;
|
||||
|
||||
if (ps->type == PSTORE_TYPE_FTRACE)
|
||||
return seq_read(file, userbuf, count, ppos);
|
||||
return simple_read_from_buffer(userbuf, count, ppos, ps->data, ps->size);
|
||||
}
|
||||
|
||||
static int pstore_file_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct pstore_private *ps = inode->i_private;
|
||||
struct seq_file *sf;
|
||||
int err;
|
||||
const struct seq_operations *sops = NULL;
|
||||
|
||||
if (ps->type == PSTORE_TYPE_FTRACE)
|
||||
sops = &pstore_ftrace_seq_ops;
|
||||
|
||||
err = seq_open(file, sops);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
sf = file->private_data;
|
||||
sf->private = ps;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static loff_t pstore_file_llseek(struct file *file, loff_t off, int origin)
|
||||
{
|
||||
struct seq_file *sf = file->private_data;
|
||||
|
||||
if (sf->op)
|
||||
return seq_lseek(file, off, origin);
|
||||
return default_llseek(file, off, origin);
|
||||
}
|
||||
|
||||
static const struct file_operations pstore_file_operations = {
|
||||
.open = simple_open,
|
||||
.read = pstore_file_read,
|
||||
.llseek = default_llseek,
|
||||
.open = pstore_file_open,
|
||||
.read = pstore_file_read,
|
||||
.llseek = pstore_file_llseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -215,6 +315,9 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id,
|
||||
case PSTORE_TYPE_CONSOLE:
|
||||
sprintf(name, "console-%s", psname);
|
||||
break;
|
||||
case PSTORE_TYPE_FTRACE:
|
||||
sprintf(name, "ftrace-%s", psname);
|
||||
break;
|
||||
case PSTORE_TYPE_MCE:
|
||||
sprintf(name, "mce-%s-%lld", psname, id);
|
||||
break;
|
||||
|
在新工单中引用
屏蔽一个用户