aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Vorontsov <anton.vorontsov@linaro.org>2012-07-09 20:10:41 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-07-17 13:05:52 -0400
commit060287b8c467bf49a594d8d669e1986c6d8d76b0 (patch)
treef869d1cc6d09875a0dc4f42b230f144617fc3bfc
parent897dba027445be93f40e5caf550556ca38c48c51 (diff)
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>
-rw-r--r--fs/pstore/Kconfig12
-rw-r--r--fs/pstore/Makefile1
-rw-r--r--fs/pstore/ftrace.c35
-rw-r--r--fs/pstore/inode.c111
-rw-r--r--fs/pstore/internal.h43
-rw-r--r--fs/pstore/platform.c2
-rw-r--r--include/linux/pstore.h9
7 files changed, 208 insertions, 5 deletions
diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig
index d044de6ee308..d39bb5cce883 100644
--- a/fs/pstore/Kconfig
+++ b/fs/pstore/Kconfig
@@ -19,6 +19,18 @@ config PSTORE_CONSOLE
19 When the option is enabled, pstore will log all kernel 19 When the option is enabled, pstore will log all kernel
20 messages, even if no oops or panic happened. 20 messages, even if no oops or panic happened.
21 21
22config PSTORE_FTRACE
23 bool "Persistent function tracer"
24 depends on PSTORE
25 depends on FUNCTION_TRACER
26 help
27 With this option kernel traces function calls into a persistent
28 ram buffer that can be decoded and dumped after reboot through
29 pstore filesystem. It can be used to determine what function
30 was last called before a reset or panic.
31
32 If unsure, say N.
33
22config PSTORE_RAM 34config PSTORE_RAM
23 tristate "Log panic/oops to a RAM buffer" 35 tristate "Log panic/oops to a RAM buffer"
24 depends on PSTORE 36 depends on PSTORE
diff --git a/fs/pstore/Makefile b/fs/pstore/Makefile
index 278a44e0d4e1..4c9095c2781e 100644
--- a/fs/pstore/Makefile
+++ b/fs/pstore/Makefile
@@ -5,6 +5,7 @@
5obj-y += pstore.o 5obj-y += pstore.o
6 6
7pstore-objs += inode.o platform.o 7pstore-objs += inode.o platform.o
8obj-$(CONFIG_PSTORE_FTRACE) += ftrace.o
8 9
9ramoops-objs += ram.o ram_core.o 10ramoops-objs += ram.o ram_core.o
10obj-$(CONFIG_PSTORE_RAM) += ramoops.o 11obj-$(CONFIG_PSTORE_RAM) += ramoops.o
diff --git a/fs/pstore/ftrace.c b/fs/pstore/ftrace.c
new file mode 100644
index 000000000000..a130d484b7d3
--- /dev/null
+++ b/fs/pstore/ftrace.c
@@ -0,0 +1,35 @@
1/*
2 * Copyright 2012 Google, Inc.
3 *
4 * This software is licensed under the terms of the GNU General Public
5 * License version 2, as published by the Free Software Foundation, and
6 * may be copied, distributed, and modified under those terms.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/kernel.h>
15#include <linux/compiler.h>
16#include <linux/irqflags.h>
17#include <linux/percpu.h>
18#include <linux/smp.h>
19#include <linux/atomic.h>
20#include <asm/barrier.h>
21#include "internal.h"
22
23void notrace pstore_ftrace_call(unsigned long ip, unsigned long parent_ip)
24{
25 struct pstore_ftrace_record rec = {};
26
27 if (unlikely(oops_in_progress))
28 return;
29
30 rec.ip = ip;
31 rec.parent_ip = parent_ip;
32 pstore_ftrace_encode_cpu(&rec, raw_smp_processor_id());
33 psinfo->write_buf(PSTORE_TYPE_FTRACE, 0, NULL, 0, (void *)&rec,
34 sizeof(rec), psinfo);
35}
diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c
index 45bff5441b04..4ab572e6d277 100644
--- a/fs/pstore/inode.c
+++ b/fs/pstore/inode.c
@@ -27,6 +27,7 @@
27#include <linux/list.h> 27#include <linux/list.h>
28#include <linux/string.h> 28#include <linux/string.h>
29#include <linux/mount.h> 29#include <linux/mount.h>
30#include <linux/seq_file.h>
30#include <linux/ramfs.h> 31#include <linux/ramfs.h>
31#include <linux/parser.h> 32#include <linux/parser.h>
32#include <linux/sched.h> 33#include <linux/sched.h>
@@ -52,18 +53,117 @@ struct pstore_private {
52 char data[]; 53 char data[];
53}; 54};
54 55
56struct pstore_ftrace_seq_data {
57 const void *ptr;
58 size_t off;
59 size_t size;
60};
61
62#define REC_SIZE sizeof(struct pstore_ftrace_record)
63
64static void *pstore_ftrace_seq_start(struct seq_file *s, loff_t *pos)
65{
66 struct pstore_private *ps = s->private;
67 struct pstore_ftrace_seq_data *data;
68
69 data = kzalloc(sizeof(*data), GFP_KERNEL);
70 if (!data)
71 return NULL;
72
73 data->off = ps->size % REC_SIZE;
74 data->off += *pos * REC_SIZE;
75 if (data->off + REC_SIZE > ps->size) {
76 kfree(data);
77 return NULL;
78 }
79
80 return data;
81
82}
83
84static void pstore_ftrace_seq_stop(struct seq_file *s, void *v)
85{
86 kfree(v);
87}
88
89static void *pstore_ftrace_seq_next(struct seq_file *s, void *v, loff_t *pos)
90{
91 struct pstore_private *ps = s->private;
92 struct pstore_ftrace_seq_data *data = v;
93
94 data->off += REC_SIZE;
95 if (data->off + REC_SIZE > ps->size)
96 return NULL;
97
98 (*pos)++;
99 return data;
100}
101
102static int pstore_ftrace_seq_show(struct seq_file *s, void *v)
103{
104 struct pstore_private *ps = s->private;
105 struct pstore_ftrace_seq_data *data = v;
106 struct pstore_ftrace_record *rec = (void *)(ps->data + data->off);
107
108 seq_printf(s, "%d %08lx %08lx %pf <- %pF\n",
109 pstore_ftrace_decode_cpu(rec), rec->ip, rec->parent_ip,
110 (void *)rec->ip, (void *)rec->parent_ip);
111
112 return 0;
113}
114
115static const struct seq_operations pstore_ftrace_seq_ops = {
116 .start = pstore_ftrace_seq_start,
117 .next = pstore_ftrace_seq_next,
118 .stop = pstore_ftrace_seq_stop,
119 .show = pstore_ftrace_seq_show,
120};
121
55static ssize_t pstore_file_read(struct file *file, char __user *userbuf, 122static ssize_t pstore_file_read(struct file *file, char __user *userbuf,
56 size_t count, loff_t *ppos) 123 size_t count, loff_t *ppos)
57{ 124{
58 struct pstore_private *ps = file->private_data; 125 struct seq_file *sf = file->private_data;
126 struct pstore_private *ps = sf->private;
59 127
128 if (ps->type == PSTORE_TYPE_FTRACE)
129 return seq_read(file, userbuf, count, ppos);
60 return simple_read_from_buffer(userbuf, count, ppos, ps->data, ps->size); 130 return simple_read_from_buffer(userbuf, count, ppos, ps->data, ps->size);
61} 131}
62 132
133static int pstore_file_open(struct inode *inode, struct file *file)
134{
135 struct pstore_private *ps = inode->i_private;
136 struct seq_file *sf;
137 int err;
138 const struct seq_operations *sops = NULL;
139
140 if (ps->type == PSTORE_TYPE_FTRACE)
141 sops = &pstore_ftrace_seq_ops;
142
143 err = seq_open(file, sops);
144 if (err < 0)
145 return err;
146
147 sf = file->private_data;
148 sf->private = ps;
149
150 return 0;
151}
152
153static loff_t pstore_file_llseek(struct file *file, loff_t off, int origin)
154{
155 struct seq_file *sf = file->private_data;
156
157 if (sf->op)
158 return seq_lseek(file, off, origin);
159 return default_llseek(file, off, origin);
160}
161
63static const struct file_operations pstore_file_operations = { 162static const struct file_operations pstore_file_operations = {
64 .open = simple_open, 163 .open = pstore_file_open,
65 .read = pstore_file_read, 164 .read = pstore_file_read,
66 .llseek = default_llseek, 165 .llseek = pstore_file_llseek,
166 .release = seq_release,
67}; 167};
68 168
69/* 169/*
@@ -215,6 +315,9 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id,
215 case PSTORE_TYPE_CONSOLE: 315 case PSTORE_TYPE_CONSOLE:
216 sprintf(name, "console-%s", psname); 316 sprintf(name, "console-%s", psname);
217 break; 317 break;
318 case PSTORE_TYPE_FTRACE:
319 sprintf(name, "ftrace-%s", psname);
320 break;
218 case PSTORE_TYPE_MCE: 321 case PSTORE_TYPE_MCE:
219 sprintf(name, "mce-%s-%lld", psname, id); 322 sprintf(name, "mce-%s-%lld", psname, id);
220 break; 323 break;
diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h
index 3bde461c3f34..958c48d8905c 100644
--- a/fs/pstore/internal.h
+++ b/fs/pstore/internal.h
@@ -1,6 +1,49 @@
1#ifndef __PSTORE_INTERNAL_H__
2#define __PSTORE_INTERNAL_H__
3
4#include <linux/pstore.h>
5
6#if NR_CPUS <= 2 && defined(CONFIG_ARM_THUMB)
7#define PSTORE_CPU_IN_IP 0x1
8#elif NR_CPUS <= 4 && defined(CONFIG_ARM)
9#define PSTORE_CPU_IN_IP 0x3
10#endif
11
12struct pstore_ftrace_record {
13 unsigned long ip;
14 unsigned long parent_ip;
15#ifndef PSTORE_CPU_IN_IP
16 unsigned int cpu;
17#endif
18};
19
20static inline void
21pstore_ftrace_encode_cpu(struct pstore_ftrace_record *rec, unsigned int cpu)
22{
23#ifndef PSTORE_CPU_IN_IP
24 rec->cpu = cpu;
25#else
26 rec->ip |= cpu;
27#endif
28}
29
30static inline unsigned int
31pstore_ftrace_decode_cpu(struct pstore_ftrace_record *rec)
32{
33#ifndef PSTORE_CPU_IN_IP
34 return rec->cpu;
35#else
36 return rec->ip & PSTORE_CPU_IN_IP;
37#endif
38}
39
40extern struct pstore_info *psinfo;
41
1extern void pstore_set_kmsg_bytes(int); 42extern void pstore_set_kmsg_bytes(int);
2extern void pstore_get_records(int); 43extern void pstore_get_records(int);
3extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id, 44extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id,
4 char *data, size_t size, 45 char *data, size_t size,
5 struct timespec time, struct pstore_info *psi); 46 struct timespec time, struct pstore_info *psi);
6extern int pstore_is_mounted(void); 47extern int pstore_is_mounted(void);
48
49#endif
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index ef5ca8a0255c..29996e8793a7 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -61,7 +61,7 @@ static DECLARE_WORK(pstore_work, pstore_dowork);
61 * calls to pstore_register() 61 * calls to pstore_register()
62 */ 62 */
63static DEFINE_SPINLOCK(pstore_lock); 63static DEFINE_SPINLOCK(pstore_lock);
64static struct pstore_info *psinfo; 64struct pstore_info *psinfo;
65 65
66static char *backend; 66static char *backend;
67 67
diff --git a/include/linux/pstore.h b/include/linux/pstore.h
index b107484192fc..120443b0fda5 100644
--- a/include/linux/pstore.h
+++ b/include/linux/pstore.h
@@ -30,6 +30,7 @@ enum pstore_type_id {
30 PSTORE_TYPE_DMESG = 0, 30 PSTORE_TYPE_DMESG = 0,
31 PSTORE_TYPE_MCE = 1, 31 PSTORE_TYPE_MCE = 1,
32 PSTORE_TYPE_CONSOLE = 2, 32 PSTORE_TYPE_CONSOLE = 2,
33 PSTORE_TYPE_FTRACE = 3,
33 PSTORE_TYPE_UNKNOWN = 255 34 PSTORE_TYPE_UNKNOWN = 255
34}; 35};
35 36
@@ -57,6 +58,14 @@ struct pstore_info {
57 void *data; 58 void *data;
58}; 59};
59 60
61
62#ifdef CONFIG_PSTORE_FTRACE
63extern void pstore_ftrace_call(unsigned long ip, unsigned long parent_ip);
64#else
65static inline void pstore_ftrace_call(unsigned long ip, unsigned long parent_ip)
66{ }
67#endif
68
60#ifdef CONFIG_PSTORE 69#ifdef CONFIG_PSTORE
61extern int pstore_register(struct pstore_info *); 70extern int pstore_register(struct pstore_info *);
62#else 71#else