Commit 060287b8c467bf49a594d8d669e1986c6d8d76b0

Authored by Anton Vorontsov
Committed by Greg Kroah-Hartman
1 parent 897dba0274

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>

Showing 7 changed files with 208 additions and 5 deletions Side-by-side Diff

... ... @@ -19,6 +19,18 @@
19 19 When the option is enabled, pstore will log all kernel
20 20 messages, even if no oops or panic happened.
21 21  
  22 +config 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 +
22 34 config PSTORE_RAM
23 35 tristate "Log panic/oops to a RAM buffer"
24 36 depends on PSTORE
... ... @@ -5,6 +5,7 @@
5 5 obj-y += pstore.o
6 6  
7 7 pstore-objs += inode.o platform.o
  8 +obj-$(CONFIG_PSTORE_FTRACE) += ftrace.o
8 9  
9 10 ramoops-objs += ram.o ram_core.o
10 11 obj-$(CONFIG_PSTORE_RAM) += ramoops.o
  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 +
  23 +void 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 +}
... ... @@ -27,6 +27,7 @@
27 27 #include <linux/list.h>
28 28 #include <linux/string.h>
29 29 #include <linux/mount.h>
  30 +#include <linux/seq_file.h>
30 31 #include <linux/ramfs.h>
31 32 #include <linux/parser.h>
32 33 #include <linux/sched.h>
33 34  
34 35  
35 36  
36 37  
... ... @@ -52,18 +53,117 @@
52 53 char data[];
53 54 };
54 55  
  56 +struct 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 +
  64 +static 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 +
  84 +static void pstore_ftrace_seq_stop(struct seq_file *s, void *v)
  85 +{
  86 + kfree(v);
  87 +}
  88 +
  89 +static 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 +
  102 +static 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 +
  115 +static 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 +
55 122 static ssize_t pstore_file_read(struct file *file, char __user *userbuf,
56 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 130 return simple_read_from_buffer(userbuf, count, ppos, ps->data, ps->size);
61 131 }
62 132  
  133 +static 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 +
  153 +static 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 +
63 162 static const struct file_operations pstore_file_operations = {
64   - .open = simple_open,
65   - .read = pstore_file_read,
66   - .llseek = default_llseek,
  163 + .open = pstore_file_open,
  164 + .read = pstore_file_read,
  165 + .llseek = pstore_file_llseek,
  166 + .release = seq_release,
67 167 };
68 168  
69 169 /*
... ... @@ -214,6 +314,9 @@
214 314 break;
215 315 case PSTORE_TYPE_CONSOLE:
216 316 sprintf(name, "console-%s", psname);
  317 + break;
  318 + case PSTORE_TYPE_FTRACE:
  319 + sprintf(name, "ftrace-%s", psname);
217 320 break;
218 321 case PSTORE_TYPE_MCE:
219 322 sprintf(name, "mce-%s-%lld", psname, id);
fs/pstore/internal.h
  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 +
  12 +struct 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 +
  20 +static inline void
  21 +pstore_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 +
  30 +static inline unsigned int
  31 +pstore_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 +
  40 +extern struct pstore_info *psinfo;
  41 +
1 42 extern void pstore_set_kmsg_bytes(int);
2 43 extern void pstore_get_records(int);
3 44 extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id,
4 45 char *data, size_t size,
5 46 struct timespec time, struct pstore_info *psi);
6 47 extern int pstore_is_mounted(void);
  48 +
  49 +#endif
fs/pstore/platform.c
... ... @@ -61,7 +61,7 @@
61 61 * calls to pstore_register()
62 62 */
63 63 static DEFINE_SPINLOCK(pstore_lock);
64   -static struct pstore_info *psinfo;
  64 +struct pstore_info *psinfo;
65 65  
66 66 static char *backend;
67 67  
include/linux/pstore.h
... ... @@ -30,6 +30,7 @@
30 30 PSTORE_TYPE_DMESG = 0,
31 31 PSTORE_TYPE_MCE = 1,
32 32 PSTORE_TYPE_CONSOLE = 2,
  33 + PSTORE_TYPE_FTRACE = 3,
33 34 PSTORE_TYPE_UNKNOWN = 255
34 35 };
35 36  
... ... @@ -56,6 +57,14 @@
56 57 struct pstore_info *psi);
57 58 void *data;
58 59 };
  60 +
  61 +
  62 +#ifdef CONFIG_PSTORE_FTRACE
  63 +extern void pstore_ftrace_call(unsigned long ip, unsigned long parent_ip);
  64 +#else
  65 +static inline void pstore_ftrace_call(unsigned long ip, unsigned long parent_ip)
  66 +{ }
  67 +#endif
59 68  
60 69 #ifdef CONFIG_PSTORE
61 70 extern int pstore_register(struct pstore_info *);