diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/pstore/Kconfig | 19 | ||||
-rw-r--r-- | fs/pstore/Makefile | 1 | ||||
-rw-r--r-- | fs/pstore/ftrace.c | 35 | ||||
-rw-r--r-- | fs/pstore/inode.c | 114 | ||||
-rw-r--r-- | fs/pstore/internal.h | 45 | ||||
-rw-r--r-- | fs/pstore/platform.c | 66 | ||||
-rw-r--r-- | fs/pstore/ram.c | 368 | ||||
-rw-r--r-- | fs/pstore/ram_core.c | 150 |
8 files changed, 576 insertions, 222 deletions
diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig index 23ade2680a4a..d39bb5cce883 100644 --- a/fs/pstore/Kconfig +++ b/fs/pstore/Kconfig | |||
@@ -12,6 +12,25 @@ config PSTORE | |||
12 | If you don't have a platform persistent store driver, | 12 | If you don't have a platform persistent store driver, |
13 | say N. | 13 | say N. |
14 | 14 | ||
15 | config PSTORE_CONSOLE | ||
16 | bool "Log kernel console messages" | ||
17 | depends on PSTORE | ||
18 | help | ||
19 | When the option is enabled, pstore will log all kernel | ||
20 | messages, even if no oops or panic happened. | ||
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 | |||
15 | config PSTORE_RAM | 34 | config PSTORE_RAM |
16 | tristate "Log panic/oops to a RAM buffer" | 35 | tristate "Log panic/oops to a RAM buffer" |
17 | 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 @@ | |||
5 | obj-y += pstore.o | 5 | obj-y += pstore.o |
6 | 6 | ||
7 | pstore-objs += inode.o platform.o | 7 | pstore-objs += inode.o platform.o |
8 | obj-$(CONFIG_PSTORE_FTRACE) += ftrace.o | ||
8 | 9 | ||
9 | ramoops-objs += ram.o ram_core.o | 10 | ramoops-objs += ram.o ram_core.o |
10 | obj-$(CONFIG_PSTORE_RAM) += ramoops.o | 11 | obj-$(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 | |||
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 | } | ||
diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index 11a2aa2a56c4..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 | ||
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 | static ssize_t pstore_file_read(struct file *file, char __user *userbuf, | 122 | static 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 | ||
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 | static const struct file_operations pstore_file_operations = { | 162 | static 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 | /* |
@@ -212,6 +312,12 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, | |||
212 | case PSTORE_TYPE_DMESG: | 312 | case PSTORE_TYPE_DMESG: |
213 | sprintf(name, "dmesg-%s-%lld", psname, id); | 313 | sprintf(name, "dmesg-%s-%lld", psname, id); |
214 | break; | 314 | break; |
315 | case PSTORE_TYPE_CONSOLE: | ||
316 | sprintf(name, "console-%s", psname); | ||
317 | break; | ||
318 | case PSTORE_TYPE_FTRACE: | ||
319 | sprintf(name, "ftrace-%s", psname); | ||
320 | break; | ||
215 | case PSTORE_TYPE_MCE: | 321 | case PSTORE_TYPE_MCE: |
216 | sprintf(name, "mce-%s-%lld", psname, id); | 322 | sprintf(name, "mce-%s-%lld", psname, id); |
217 | break; | 323 | break; |
diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h index 3bde461c3f34..0d0d3b7d5f12 100644 --- a/fs/pstore/internal.h +++ b/fs/pstore/internal.h | |||
@@ -1,6 +1,51 @@ | |||
1 | #ifndef __PSTORE_INTERNAL_H__ | ||
2 | #define __PSTORE_INTERNAL_H__ | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | #include <linux/time.h> | ||
6 | #include <linux/pstore.h> | ||
7 | |||
8 | #if NR_CPUS <= 2 && defined(CONFIG_ARM_THUMB) | ||
9 | #define PSTORE_CPU_IN_IP 0x1 | ||
10 | #elif NR_CPUS <= 4 && defined(CONFIG_ARM) | ||
11 | #define PSTORE_CPU_IN_IP 0x3 | ||
12 | #endif | ||
13 | |||
14 | struct pstore_ftrace_record { | ||
15 | unsigned long ip; | ||
16 | unsigned long parent_ip; | ||
17 | #ifndef PSTORE_CPU_IN_IP | ||
18 | unsigned int cpu; | ||
19 | #endif | ||
20 | }; | ||
21 | |||
22 | static inline void | ||
23 | pstore_ftrace_encode_cpu(struct pstore_ftrace_record *rec, unsigned int cpu) | ||
24 | { | ||
25 | #ifndef PSTORE_CPU_IN_IP | ||
26 | rec->cpu = cpu; | ||
27 | #else | ||
28 | rec->ip |= cpu; | ||
29 | #endif | ||
30 | } | ||
31 | |||
32 | static inline unsigned int | ||
33 | pstore_ftrace_decode_cpu(struct pstore_ftrace_record *rec) | ||
34 | { | ||
35 | #ifndef PSTORE_CPU_IN_IP | ||
36 | return rec->cpu; | ||
37 | #else | ||
38 | return rec->ip & PSTORE_CPU_IN_IP; | ||
39 | #endif | ||
40 | } | ||
41 | |||
42 | extern struct pstore_info *psinfo; | ||
43 | |||
1 | extern void pstore_set_kmsg_bytes(int); | 44 | extern void pstore_set_kmsg_bytes(int); |
2 | extern void pstore_get_records(int); | 45 | extern void pstore_get_records(int); |
3 | extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id, | 46 | extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id, |
4 | char *data, size_t size, | 47 | char *data, size_t size, |
5 | struct timespec time, struct pstore_info *psi); | 48 | struct timespec time, struct pstore_info *psi); |
6 | extern int pstore_is_mounted(void); | 49 | extern int pstore_is_mounted(void); |
50 | |||
51 | #endif | ||
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 03ce7a9b81cc..29996e8793a7 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c | |||
@@ -1,6 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Persistent Storage - platform driver interface parts. | 2 | * Persistent Storage - platform driver interface parts. |
3 | * | 3 | * |
4 | * Copyright (C) 2007-2008 Google, Inc. | ||
4 | * Copyright (C) 2010 Intel Corporation <tony.luck@intel.com> | 5 | * Copyright (C) 2010 Intel Corporation <tony.luck@intel.com> |
5 | * | 6 | * |
6 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
@@ -22,6 +23,7 @@ | |||
22 | #include <linux/errno.h> | 23 | #include <linux/errno.h> |
23 | #include <linux/init.h> | 24 | #include <linux/init.h> |
24 | #include <linux/kmsg_dump.h> | 25 | #include <linux/kmsg_dump.h> |
26 | #include <linux/console.h> | ||
25 | #include <linux/module.h> | 27 | #include <linux/module.h> |
26 | #include <linux/pstore.h> | 28 | #include <linux/pstore.h> |
27 | #include <linux/string.h> | 29 | #include <linux/string.h> |
@@ -29,6 +31,7 @@ | |||
29 | #include <linux/slab.h> | 31 | #include <linux/slab.h> |
30 | #include <linux/uaccess.h> | 32 | #include <linux/uaccess.h> |
31 | #include <linux/hardirq.h> | 33 | #include <linux/hardirq.h> |
34 | #include <linux/jiffies.h> | ||
32 | #include <linux/workqueue.h> | 35 | #include <linux/workqueue.h> |
33 | 36 | ||
34 | #include "internal.h" | 37 | #include "internal.h" |
@@ -38,7 +41,12 @@ | |||
38 | * whether the system is actually still running well enough | 41 | * whether the system is actually still running well enough |
39 | * to let someone see the entry | 42 | * to let someone see the entry |
40 | */ | 43 | */ |
41 | #define PSTORE_INTERVAL (60 * HZ) | 44 | static int pstore_update_ms = -1; |
45 | module_param_named(update_ms, pstore_update_ms, int, 0600); | ||
46 | MODULE_PARM_DESC(update_ms, "milliseconds before pstore updates its content " | ||
47 | "(default is -1, which means runtime updates are disabled; " | ||
48 | "enabling this option is not safe, it may lead to further " | ||
49 | "corruption on Oopses)"); | ||
42 | 50 | ||
43 | static int pstore_new_entry; | 51 | static int pstore_new_entry; |
44 | 52 | ||
@@ -53,7 +61,7 @@ static DECLARE_WORK(pstore_work, pstore_dowork); | |||
53 | * calls to pstore_register() | 61 | * calls to pstore_register() |
54 | */ | 62 | */ |
55 | static DEFINE_SPINLOCK(pstore_lock); | 63 | static DEFINE_SPINLOCK(pstore_lock); |
56 | static struct pstore_info *psinfo; | 64 | struct pstore_info *psinfo; |
57 | 65 | ||
58 | static char *backend; | 66 | static char *backend; |
59 | 67 | ||
@@ -146,6 +154,48 @@ static struct kmsg_dumper pstore_dumper = { | |||
146 | .dump = pstore_dump, | 154 | .dump = pstore_dump, |
147 | }; | 155 | }; |
148 | 156 | ||
157 | #ifdef CONFIG_PSTORE_CONSOLE | ||
158 | static void pstore_console_write(struct console *con, const char *s, unsigned c) | ||
159 | { | ||
160 | const char *e = s + c; | ||
161 | |||
162 | while (s < e) { | ||
163 | unsigned long flags; | ||
164 | |||
165 | if (c > psinfo->bufsize) | ||
166 | c = psinfo->bufsize; | ||
167 | spin_lock_irqsave(&psinfo->buf_lock, flags); | ||
168 | memcpy(psinfo->buf, s, c); | ||
169 | psinfo->write(PSTORE_TYPE_CONSOLE, 0, NULL, 0, c, psinfo); | ||
170 | spin_unlock_irqrestore(&psinfo->buf_lock, flags); | ||
171 | s += c; | ||
172 | c = e - s; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | static struct console pstore_console = { | ||
177 | .name = "pstore", | ||
178 | .write = pstore_console_write, | ||
179 | .flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME, | ||
180 | .index = -1, | ||
181 | }; | ||
182 | |||
183 | static void pstore_register_console(void) | ||
184 | { | ||
185 | register_console(&pstore_console); | ||
186 | } | ||
187 | #else | ||
188 | static void pstore_register_console(void) {} | ||
189 | #endif | ||
190 | |||
191 | static int pstore_write_compat(enum pstore_type_id type, | ||
192 | enum kmsg_dump_reason reason, | ||
193 | u64 *id, unsigned int part, | ||
194 | size_t size, struct pstore_info *psi) | ||
195 | { | ||
196 | return psi->write_buf(type, reason, id, part, psinfo->buf, size, psi); | ||
197 | } | ||
198 | |||
149 | /* | 199 | /* |
150 | * platform specific persistent storage driver registers with | 200 | * platform specific persistent storage driver registers with |
151 | * us here. If pstore is already mounted, call the platform | 201 | * us here. If pstore is already mounted, call the platform |
@@ -170,6 +220,8 @@ int pstore_register(struct pstore_info *psi) | |||
170 | return -EINVAL; | 220 | return -EINVAL; |
171 | } | 221 | } |
172 | 222 | ||
223 | if (!psi->write) | ||
224 | psi->write = pstore_write_compat; | ||
173 | psinfo = psi; | 225 | psinfo = psi; |
174 | mutex_init(&psinfo->read_mutex); | 226 | mutex_init(&psinfo->read_mutex); |
175 | spin_unlock(&pstore_lock); | 227 | spin_unlock(&pstore_lock); |
@@ -183,9 +235,13 @@ int pstore_register(struct pstore_info *psi) | |||
183 | pstore_get_records(0); | 235 | pstore_get_records(0); |
184 | 236 | ||
185 | kmsg_dump_register(&pstore_dumper); | 237 | kmsg_dump_register(&pstore_dumper); |
238 | pstore_register_console(); | ||
186 | 239 | ||
187 | pstore_timer.expires = jiffies + PSTORE_INTERVAL; | 240 | if (pstore_update_ms >= 0) { |
188 | add_timer(&pstore_timer); | 241 | pstore_timer.expires = jiffies + |
242 | msecs_to_jiffies(pstore_update_ms); | ||
243 | add_timer(&pstore_timer); | ||
244 | } | ||
189 | 245 | ||
190 | return 0; | 246 | return 0; |
191 | } | 247 | } |
@@ -244,7 +300,7 @@ static void pstore_timefunc(unsigned long dummy) | |||
244 | schedule_work(&pstore_work); | 300 | schedule_work(&pstore_work); |
245 | } | 301 | } |
246 | 302 | ||
247 | mod_timer(&pstore_timer, jiffies + PSTORE_INTERVAL); | 303 | mod_timer(&pstore_timer, jiffies + msecs_to_jiffies(pstore_update_ms)); |
248 | } | 304 | } |
249 | 305 | ||
250 | module_param(backend, charp, 0444); | 306 | module_param(backend, charp, 0444); |
diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index 453030f9c5bc..0b311bc18916 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/kernel.h> | 25 | #include <linux/kernel.h> |
26 | #include <linux/err.h> | 26 | #include <linux/err.h> |
27 | #include <linux/module.h> | 27 | #include <linux/module.h> |
28 | #include <linux/version.h> | ||
28 | #include <linux/pstore.h> | 29 | #include <linux/pstore.h> |
29 | #include <linux/time.h> | 30 | #include <linux/time.h> |
30 | #include <linux/io.h> | 31 | #include <linux/io.h> |
@@ -41,6 +42,14 @@ module_param(record_size, ulong, 0400); | |||
41 | MODULE_PARM_DESC(record_size, | 42 | MODULE_PARM_DESC(record_size, |
42 | "size of each dump done on oops/panic"); | 43 | "size of each dump done on oops/panic"); |
43 | 44 | ||
45 | static ulong ramoops_console_size = MIN_MEM_SIZE; | ||
46 | module_param_named(console_size, ramoops_console_size, ulong, 0400); | ||
47 | MODULE_PARM_DESC(console_size, "size of kernel console log"); | ||
48 | |||
49 | static ulong ramoops_ftrace_size = MIN_MEM_SIZE; | ||
50 | module_param_named(ftrace_size, ramoops_ftrace_size, ulong, 0400); | ||
51 | MODULE_PARM_DESC(ftrace_size, "size of ftrace log"); | ||
52 | |||
44 | static ulong mem_address; | 53 | static ulong mem_address; |
45 | module_param(mem_address, ulong, 0400); | 54 | module_param(mem_address, ulong, 0400); |
46 | MODULE_PARM_DESC(mem_address, | 55 | MODULE_PARM_DESC(mem_address, |
@@ -59,18 +68,26 @@ MODULE_PARM_DESC(dump_oops, | |||
59 | static int ramoops_ecc; | 68 | static int ramoops_ecc; |
60 | module_param_named(ecc, ramoops_ecc, int, 0600); | 69 | module_param_named(ecc, ramoops_ecc, int, 0600); |
61 | MODULE_PARM_DESC(ramoops_ecc, | 70 | MODULE_PARM_DESC(ramoops_ecc, |
62 | "set to 1 to enable ECC support"); | 71 | "if non-zero, the option enables ECC support and specifies " |
72 | "ECC buffer size in bytes (1 is a special value, means 16 " | ||
73 | "bytes ECC)"); | ||
63 | 74 | ||
64 | struct ramoops_context { | 75 | struct ramoops_context { |
65 | struct persistent_ram_zone **przs; | 76 | struct persistent_ram_zone **przs; |
77 | struct persistent_ram_zone *cprz; | ||
78 | struct persistent_ram_zone *fprz; | ||
66 | phys_addr_t phys_addr; | 79 | phys_addr_t phys_addr; |
67 | unsigned long size; | 80 | unsigned long size; |
68 | size_t record_size; | 81 | size_t record_size; |
82 | size_t console_size; | ||
83 | size_t ftrace_size; | ||
69 | int dump_oops; | 84 | int dump_oops; |
70 | bool ecc; | 85 | int ecc_size; |
71 | unsigned int count; | 86 | unsigned int max_dump_cnt; |
72 | unsigned int max_count; | 87 | unsigned int dump_write_cnt; |
73 | unsigned int read_count; | 88 | unsigned int dump_read_cnt; |
89 | unsigned int console_read_cnt; | ||
90 | unsigned int ftrace_read_cnt; | ||
74 | struct pstore_info pstore; | 91 | struct pstore_info pstore; |
75 | }; | 92 | }; |
76 | 93 | ||
@@ -81,10 +98,38 @@ static int ramoops_pstore_open(struct pstore_info *psi) | |||
81 | { | 98 | { |
82 | struct ramoops_context *cxt = psi->data; | 99 | struct ramoops_context *cxt = psi->data; |
83 | 100 | ||
84 | cxt->read_count = 0; | 101 | cxt->dump_read_cnt = 0; |
102 | cxt->console_read_cnt = 0; | ||
85 | return 0; | 103 | return 0; |
86 | } | 104 | } |
87 | 105 | ||
106 | static struct persistent_ram_zone * | ||
107 | ramoops_get_next_prz(struct persistent_ram_zone *przs[], uint *c, uint max, | ||
108 | u64 *id, | ||
109 | enum pstore_type_id *typep, enum pstore_type_id type, | ||
110 | bool update) | ||
111 | { | ||
112 | struct persistent_ram_zone *prz; | ||
113 | int i = (*c)++; | ||
114 | |||
115 | if (i >= max) | ||
116 | return NULL; | ||
117 | |||
118 | prz = przs[i]; | ||
119 | |||
120 | if (update) { | ||
121 | /* Update old/shadowed buffer. */ | ||
122 | persistent_ram_save_old(prz); | ||
123 | if (!persistent_ram_old_size(prz)) | ||
124 | return NULL; | ||
125 | } | ||
126 | |||
127 | *typep = type; | ||
128 | *id = i; | ||
129 | |||
130 | return prz; | ||
131 | } | ||
132 | |||
88 | static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, | 133 | static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, |
89 | struct timespec *time, | 134 | struct timespec *time, |
90 | char **buf, | 135 | char **buf, |
@@ -94,20 +139,22 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, | |||
94 | struct ramoops_context *cxt = psi->data; | 139 | struct ramoops_context *cxt = psi->data; |
95 | struct persistent_ram_zone *prz; | 140 | struct persistent_ram_zone *prz; |
96 | 141 | ||
97 | if (cxt->read_count >= cxt->max_count) | 142 | prz = ramoops_get_next_prz(cxt->przs, &cxt->dump_read_cnt, |
98 | return -EINVAL; | 143 | cxt->max_dump_cnt, id, type, |
144 | PSTORE_TYPE_DMESG, 1); | ||
145 | if (!prz) | ||
146 | prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt, | ||
147 | 1, id, type, PSTORE_TYPE_CONSOLE, 0); | ||
148 | if (!prz) | ||
149 | prz = ramoops_get_next_prz(&cxt->fprz, &cxt->ftrace_read_cnt, | ||
150 | 1, id, type, PSTORE_TYPE_FTRACE, 0); | ||
151 | if (!prz) | ||
152 | return 0; | ||
99 | 153 | ||
100 | *id = cxt->read_count++; | ||
101 | prz = cxt->przs[*id]; | ||
102 | |||
103 | /* Only supports dmesg output so far. */ | ||
104 | *type = PSTORE_TYPE_DMESG; | ||
105 | /* TODO(kees): Bogus time for the moment. */ | 154 | /* TODO(kees): Bogus time for the moment. */ |
106 | time->tv_sec = 0; | 155 | time->tv_sec = 0; |
107 | time->tv_nsec = 0; | 156 | time->tv_nsec = 0; |
108 | 157 | ||
109 | /* Update old/shadowed buffer. */ | ||
110 | persistent_ram_save_old(prz); | ||
111 | size = persistent_ram_old_size(prz); | 158 | size = persistent_ram_old_size(prz); |
112 | *buf = kmalloc(size, GFP_KERNEL); | 159 | *buf = kmalloc(size, GFP_KERNEL); |
113 | if (*buf == NULL) | 160 | if (*buf == NULL) |
@@ -134,17 +181,29 @@ static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz) | |||
134 | return len; | 181 | return len; |
135 | } | 182 | } |
136 | 183 | ||
137 | static int ramoops_pstore_write(enum pstore_type_id type, | 184 | |
138 | enum kmsg_dump_reason reason, | 185 | static int ramoops_pstore_write_buf(enum pstore_type_id type, |
139 | u64 *id, | 186 | enum kmsg_dump_reason reason, |
140 | unsigned int part, | 187 | u64 *id, unsigned int part, |
141 | size_t size, struct pstore_info *psi) | 188 | const char *buf, size_t size, |
189 | struct pstore_info *psi) | ||
142 | { | 190 | { |
143 | struct ramoops_context *cxt = psi->data; | 191 | struct ramoops_context *cxt = psi->data; |
144 | struct persistent_ram_zone *prz = cxt->przs[cxt->count]; | 192 | struct persistent_ram_zone *prz = cxt->przs[cxt->dump_write_cnt]; |
145 | size_t hlen; | 193 | size_t hlen; |
146 | 194 | ||
147 | /* Currently ramoops is designed to only store dmesg dumps. */ | 195 | if (type == PSTORE_TYPE_CONSOLE) { |
196 | if (!cxt->cprz) | ||
197 | return -ENOMEM; | ||
198 | persistent_ram_write(cxt->cprz, buf, size); | ||
199 | return 0; | ||
200 | } else if (type == PSTORE_TYPE_FTRACE) { | ||
201 | if (!cxt->fprz) | ||
202 | return -ENOMEM; | ||
203 | persistent_ram_write(cxt->fprz, buf, size); | ||
204 | return 0; | ||
205 | } | ||
206 | |||
148 | if (type != PSTORE_TYPE_DMESG) | 207 | if (type != PSTORE_TYPE_DMESG) |
149 | return -EINVAL; | 208 | return -EINVAL; |
150 | 209 | ||
@@ -170,9 +229,9 @@ static int ramoops_pstore_write(enum pstore_type_id type, | |||
170 | hlen = ramoops_write_kmsg_hdr(prz); | 229 | hlen = ramoops_write_kmsg_hdr(prz); |
171 | if (size + hlen > prz->buffer_size) | 230 | if (size + hlen > prz->buffer_size) |
172 | size = prz->buffer_size - hlen; | 231 | size = prz->buffer_size - hlen; |
173 | persistent_ram_write(prz, cxt->pstore.buf, size); | 232 | persistent_ram_write(prz, buf, size); |
174 | 233 | ||
175 | cxt->count = (cxt->count + 1) % cxt->max_count; | 234 | cxt->dump_write_cnt = (cxt->dump_write_cnt + 1) % cxt->max_dump_cnt; |
176 | 235 | ||
177 | return 0; | 236 | return 0; |
178 | } | 237 | } |
@@ -181,12 +240,26 @@ static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, | |||
181 | struct pstore_info *psi) | 240 | struct pstore_info *psi) |
182 | { | 241 | { |
183 | struct ramoops_context *cxt = psi->data; | 242 | struct ramoops_context *cxt = psi->data; |
243 | struct persistent_ram_zone *prz; | ||
184 | 244 | ||
185 | if (id >= cxt->max_count) | 245 | switch (type) { |
246 | case PSTORE_TYPE_DMESG: | ||
247 | if (id >= cxt->max_dump_cnt) | ||
248 | return -EINVAL; | ||
249 | prz = cxt->przs[id]; | ||
250 | break; | ||
251 | case PSTORE_TYPE_CONSOLE: | ||
252 | prz = cxt->cprz; | ||
253 | break; | ||
254 | case PSTORE_TYPE_FTRACE: | ||
255 | prz = cxt->fprz; | ||
256 | break; | ||
257 | default: | ||
186 | return -EINVAL; | 258 | return -EINVAL; |
259 | } | ||
187 | 260 | ||
188 | persistent_ram_free_old(cxt->przs[id]); | 261 | persistent_ram_free_old(prz); |
189 | persistent_ram_zap(cxt->przs[id]); | 262 | persistent_ram_zap(prz); |
190 | 263 | ||
191 | return 0; | 264 | return 0; |
192 | } | 265 | } |
@@ -197,78 +270,157 @@ static struct ramoops_context oops_cxt = { | |||
197 | .name = "ramoops", | 270 | .name = "ramoops", |
198 | .open = ramoops_pstore_open, | 271 | .open = ramoops_pstore_open, |
199 | .read = ramoops_pstore_read, | 272 | .read = ramoops_pstore_read, |
200 | .write = ramoops_pstore_write, | 273 | .write_buf = ramoops_pstore_write_buf, |
201 | .erase = ramoops_pstore_erase, | 274 | .erase = ramoops_pstore_erase, |
202 | }, | 275 | }, |
203 | }; | 276 | }; |
204 | 277 | ||
205 | static int __init ramoops_probe(struct platform_device *pdev) | 278 | static void ramoops_free_przs(struct ramoops_context *cxt) |
279 | { | ||
280 | int i; | ||
281 | |||
282 | if (!cxt->przs) | ||
283 | return; | ||
284 | |||
285 | for (i = 0; !IS_ERR_OR_NULL(cxt->przs[i]); i++) | ||
286 | persistent_ram_free(cxt->przs[i]); | ||
287 | kfree(cxt->przs); | ||
288 | } | ||
289 | |||
290 | static int ramoops_init_przs(struct device *dev, struct ramoops_context *cxt, | ||
291 | phys_addr_t *paddr, size_t dump_mem_sz) | ||
292 | { | ||
293 | int err = -ENOMEM; | ||
294 | int i; | ||
295 | |||
296 | if (!cxt->record_size) | ||
297 | return 0; | ||
298 | |||
299 | cxt->max_dump_cnt = dump_mem_sz / cxt->record_size; | ||
300 | if (!cxt->max_dump_cnt) | ||
301 | return -ENOMEM; | ||
302 | |||
303 | cxt->przs = kzalloc(sizeof(*cxt->przs) * cxt->max_dump_cnt, | ||
304 | GFP_KERNEL); | ||
305 | if (!cxt->przs) { | ||
306 | dev_err(dev, "failed to initialize a prz array for dumps\n"); | ||
307 | return -ENOMEM; | ||
308 | } | ||
309 | |||
310 | for (i = 0; i < cxt->max_dump_cnt; i++) { | ||
311 | size_t sz = cxt->record_size; | ||
312 | |||
313 | cxt->przs[i] = persistent_ram_new(*paddr, sz, 0, cxt->ecc_size); | ||
314 | if (IS_ERR(cxt->przs[i])) { | ||
315 | err = PTR_ERR(cxt->przs[i]); | ||
316 | dev_err(dev, "failed to request mem region (0x%zx@0x%llx): %d\n", | ||
317 | sz, (unsigned long long)*paddr, err); | ||
318 | goto fail_prz; | ||
319 | } | ||
320 | *paddr += sz; | ||
321 | } | ||
322 | |||
323 | return 0; | ||
324 | fail_prz: | ||
325 | ramoops_free_przs(cxt); | ||
326 | return err; | ||
327 | } | ||
328 | |||
329 | static int ramoops_init_prz(struct device *dev, struct ramoops_context *cxt, | ||
330 | struct persistent_ram_zone **prz, | ||
331 | phys_addr_t *paddr, size_t sz, u32 sig) | ||
332 | { | ||
333 | if (!sz) | ||
334 | return 0; | ||
335 | |||
336 | if (*paddr + sz > *paddr + cxt->size) | ||
337 | return -ENOMEM; | ||
338 | |||
339 | *prz = persistent_ram_new(*paddr, sz, sig, cxt->ecc_size); | ||
340 | if (IS_ERR(*prz)) { | ||
341 | int err = PTR_ERR(*prz); | ||
342 | |||
343 | dev_err(dev, "failed to request mem region (0x%zx@0x%llx): %d\n", | ||
344 | sz, (unsigned long long)*paddr, err); | ||
345 | return err; | ||
346 | } | ||
347 | |||
348 | persistent_ram_zap(*prz); | ||
349 | |||
350 | *paddr += sz; | ||
351 | |||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | static int __devinit ramoops_probe(struct platform_device *pdev) | ||
206 | { | 356 | { |
207 | struct device *dev = &pdev->dev; | 357 | struct device *dev = &pdev->dev; |
208 | struct ramoops_platform_data *pdata = pdev->dev.platform_data; | 358 | struct ramoops_platform_data *pdata = pdev->dev.platform_data; |
209 | struct ramoops_context *cxt = &oops_cxt; | 359 | struct ramoops_context *cxt = &oops_cxt; |
360 | size_t dump_mem_sz; | ||
361 | phys_addr_t paddr; | ||
210 | int err = -EINVAL; | 362 | int err = -EINVAL; |
211 | int i; | ||
212 | 363 | ||
213 | /* Only a single ramoops area allowed at a time, so fail extra | 364 | /* Only a single ramoops area allowed at a time, so fail extra |
214 | * probes. | 365 | * probes. |
215 | */ | 366 | */ |
216 | if (cxt->max_count) | 367 | if (cxt->max_dump_cnt) |
217 | goto fail_out; | 368 | goto fail_out; |
218 | 369 | ||
219 | if (!pdata->mem_size || !pdata->record_size) { | 370 | if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size && |
220 | pr_err("The memory size and the record size must be " | 371 | !pdata->ftrace_size)) { |
372 | pr_err("The memory size and the record/console size must be " | ||
221 | "non-zero\n"); | 373 | "non-zero\n"); |
222 | goto fail_out; | 374 | goto fail_out; |
223 | } | 375 | } |
224 | 376 | ||
225 | pdata->mem_size = rounddown_pow_of_two(pdata->mem_size); | 377 | pdata->mem_size = rounddown_pow_of_two(pdata->mem_size); |
226 | pdata->record_size = rounddown_pow_of_two(pdata->record_size); | 378 | pdata->record_size = rounddown_pow_of_two(pdata->record_size); |
379 | pdata->console_size = rounddown_pow_of_two(pdata->console_size); | ||
380 | pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size); | ||
227 | 381 | ||
228 | /* Check for the minimum memory size */ | 382 | cxt->dump_read_cnt = 0; |
229 | if (pdata->mem_size < MIN_MEM_SIZE && | ||
230 | pdata->record_size < MIN_MEM_SIZE) { | ||
231 | pr_err("memory size too small, minimum is %lu\n", | ||
232 | MIN_MEM_SIZE); | ||
233 | goto fail_out; | ||
234 | } | ||
235 | |||
236 | if (pdata->mem_size < pdata->record_size) { | ||
237 | pr_err("The memory size must be larger than the " | ||
238 | "records size\n"); | ||
239 | goto fail_out; | ||
240 | } | ||
241 | |||
242 | cxt->max_count = pdata->mem_size / pdata->record_size; | ||
243 | cxt->count = 0; | ||
244 | cxt->size = pdata->mem_size; | 383 | cxt->size = pdata->mem_size; |
245 | cxt->phys_addr = pdata->mem_address; | 384 | cxt->phys_addr = pdata->mem_address; |
246 | cxt->record_size = pdata->record_size; | 385 | cxt->record_size = pdata->record_size; |
386 | cxt->console_size = pdata->console_size; | ||
387 | cxt->ftrace_size = pdata->ftrace_size; | ||
247 | cxt->dump_oops = pdata->dump_oops; | 388 | cxt->dump_oops = pdata->dump_oops; |
248 | cxt->ecc = pdata->ecc; | 389 | cxt->ecc_size = pdata->ecc_size; |
249 | 390 | ||
250 | cxt->przs = kzalloc(sizeof(*cxt->przs) * cxt->max_count, GFP_KERNEL); | 391 | paddr = cxt->phys_addr; |
251 | if (!cxt->przs) { | 392 | |
252 | err = -ENOMEM; | 393 | dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size; |
253 | dev_err(dev, "failed to initialize a prz array\n"); | 394 | err = ramoops_init_przs(dev, cxt, &paddr, dump_mem_sz); |
395 | if (err) | ||
254 | goto fail_out; | 396 | goto fail_out; |
255 | } | ||
256 | 397 | ||
257 | for (i = 0; i < cxt->max_count; i++) { | 398 | err = ramoops_init_prz(dev, cxt, &cxt->cprz, &paddr, |
258 | size_t sz = cxt->record_size; | 399 | cxt->console_size, 0); |
259 | phys_addr_t start = cxt->phys_addr + sz * i; | 400 | if (err) |
401 | goto fail_init_cprz; | ||
260 | 402 | ||
261 | cxt->przs[i] = persistent_ram_new(start, sz, cxt->ecc); | 403 | err = ramoops_init_prz(dev, cxt, &cxt->fprz, &paddr, cxt->ftrace_size, |
262 | if (IS_ERR(cxt->przs[i])) { | 404 | LINUX_VERSION_CODE); |
263 | err = PTR_ERR(cxt->przs[i]); | 405 | if (err) |
264 | dev_err(dev, "failed to request mem region (0x%zx@0x%llx): %d\n", | 406 | goto fail_init_fprz; |
265 | sz, (unsigned long long)start, err); | 407 | |
266 | goto fail_przs; | 408 | if (!cxt->przs && !cxt->cprz && !cxt->fprz) { |
267 | } | 409 | pr_err("memory size too small, minimum is %lu\n", |
410 | cxt->console_size + cxt->record_size + | ||
411 | cxt->ftrace_size); | ||
412 | goto fail_cnt; | ||
268 | } | 413 | } |
269 | 414 | ||
270 | cxt->pstore.data = cxt; | 415 | cxt->pstore.data = cxt; |
271 | cxt->pstore.bufsize = cxt->przs[0]->buffer_size; | 416 | /* |
417 | * Console can handle any buffer size, so prefer dumps buffer | ||
418 | * size since usually it is smaller. | ||
419 | */ | ||
420 | if (cxt->przs) | ||
421 | cxt->pstore.bufsize = cxt->przs[0]->buffer_size; | ||
422 | else | ||
423 | cxt->pstore.bufsize = cxt->cprz->buffer_size; | ||
272 | cxt->pstore.buf = kmalloc(cxt->pstore.bufsize, GFP_KERNEL); | 424 | cxt->pstore.buf = kmalloc(cxt->pstore.bufsize, GFP_KERNEL); |
273 | spin_lock_init(&cxt->pstore.buf_lock); | 425 | spin_lock_init(&cxt->pstore.buf_lock); |
274 | if (!cxt->pstore.buf) { | 426 | if (!cxt->pstore.buf) { |
@@ -291,10 +443,9 @@ static int __init ramoops_probe(struct platform_device *pdev) | |||
291 | record_size = pdata->record_size; | 443 | record_size = pdata->record_size; |
292 | dump_oops = pdata->dump_oops; | 444 | dump_oops = pdata->dump_oops; |
293 | 445 | ||
294 | pr_info("attached 0x%lx@0x%llx (%ux0x%zx), ecc: %s\n", | 446 | pr_info("attached 0x%lx@0x%llx, ecc: %d\n", |
295 | cxt->size, (unsigned long long)cxt->phys_addr, | 447 | cxt->size, (unsigned long long)cxt->phys_addr, |
296 | cxt->max_count, cxt->record_size, | 448 | cxt->ecc_size); |
297 | ramoops_ecc ? "on" : "off"); | ||
298 | 449 | ||
299 | return 0; | 450 | return 0; |
300 | 451 | ||
@@ -302,11 +453,13 @@ fail_buf: | |||
302 | kfree(cxt->pstore.buf); | 453 | kfree(cxt->pstore.buf); |
303 | fail_clear: | 454 | fail_clear: |
304 | cxt->pstore.bufsize = 0; | 455 | cxt->pstore.bufsize = 0; |
305 | cxt->max_count = 0; | 456 | cxt->max_dump_cnt = 0; |
306 | fail_przs: | 457 | fail_cnt: |
307 | for (i = 0; cxt->przs[i]; i++) | 458 | kfree(cxt->fprz); |
308 | persistent_ram_free(cxt->przs[i]); | 459 | fail_init_fprz: |
309 | kfree(cxt->przs); | 460 | kfree(cxt->cprz); |
461 | fail_init_cprz: | ||
462 | ramoops_free_przs(cxt); | ||
310 | fail_out: | 463 | fail_out: |
311 | return err; | 464 | return err; |
312 | } | 465 | } |
@@ -321,7 +474,7 @@ static int __exit ramoops_remove(struct platform_device *pdev) | |||
321 | 474 | ||
322 | iounmap(cxt->virt_addr); | 475 | iounmap(cxt->virt_addr); |
323 | release_mem_region(cxt->phys_addr, cxt->size); | 476 | release_mem_region(cxt->phys_addr, cxt->size); |
324 | cxt->max_count = 0; | 477 | cxt->max_dump_cnt = 0; |
325 | 478 | ||
326 | /* TODO(kees): When pstore supports unregistering, call it here. */ | 479 | /* TODO(kees): When pstore supports unregistering, call it here. */ |
327 | kfree(cxt->pstore.buf); | 480 | kfree(cxt->pstore.buf); |
@@ -333,6 +486,7 @@ static int __exit ramoops_remove(struct platform_device *pdev) | |||
333 | } | 486 | } |
334 | 487 | ||
335 | static struct platform_driver ramoops_driver = { | 488 | static struct platform_driver ramoops_driver = { |
489 | .probe = ramoops_probe, | ||
336 | .remove = __exit_p(ramoops_remove), | 490 | .remove = __exit_p(ramoops_remove), |
337 | .driver = { | 491 | .driver = { |
338 | .name = "ramoops", | 492 | .name = "ramoops", |
@@ -340,45 +494,51 @@ static struct platform_driver ramoops_driver = { | |||
340 | }, | 494 | }, |
341 | }; | 495 | }; |
342 | 496 | ||
343 | static int __init ramoops_init(void) | 497 | static void ramoops_register_dummy(void) |
344 | { | 498 | { |
345 | int ret; | 499 | if (!mem_size) |
346 | ret = platform_driver_probe(&ramoops_driver, ramoops_probe); | 500 | return; |
347 | if (ret == -ENODEV) { | 501 | |
348 | /* | 502 | pr_info("using module parameters\n"); |
349 | * If we didn't find a platform device, we use module parameters | 503 | |
350 | * building platform data on the fly. | 504 | dummy_data = kzalloc(sizeof(*dummy_data), GFP_KERNEL); |
351 | */ | 505 | if (!dummy_data) { |
352 | pr_info("platform device not found, using module parameters\n"); | 506 | pr_info("could not allocate pdata\n"); |
353 | dummy_data = kzalloc(sizeof(struct ramoops_platform_data), | 507 | return; |
354 | GFP_KERNEL); | 508 | } |
355 | if (!dummy_data) | 509 | |
356 | return -ENOMEM; | 510 | dummy_data->mem_size = mem_size; |
357 | dummy_data->mem_size = mem_size; | 511 | dummy_data->mem_address = mem_address; |
358 | dummy_data->mem_address = mem_address; | 512 | dummy_data->record_size = record_size; |
359 | dummy_data->record_size = record_size; | 513 | dummy_data->console_size = ramoops_console_size; |
360 | dummy_data->dump_oops = dump_oops; | 514 | dummy_data->ftrace_size = ramoops_ftrace_size; |
361 | dummy_data->ecc = ramoops_ecc; | 515 | dummy_data->dump_oops = dump_oops; |
362 | dummy = platform_create_bundle(&ramoops_driver, ramoops_probe, | 516 | /* |
363 | NULL, 0, dummy_data, | 517 | * For backwards compatibility ramoops.ecc=1 means 16 bytes ECC |
364 | sizeof(struct ramoops_platform_data)); | 518 | * (using 1 byte for ECC isn't much of use anyway). |
365 | 519 | */ | |
366 | if (IS_ERR(dummy)) | 520 | dummy_data->ecc_size = ramoops_ecc == 1 ? 16 : ramoops_ecc; |
367 | ret = PTR_ERR(dummy); | 521 | |
368 | else | 522 | dummy = platform_device_register_data(NULL, "ramoops", -1, |
369 | ret = 0; | 523 | dummy_data, sizeof(struct ramoops_platform_data)); |
524 | if (IS_ERR(dummy)) { | ||
525 | pr_info("could not create platform device: %ld\n", | ||
526 | PTR_ERR(dummy)); | ||
370 | } | 527 | } |
528 | } | ||
371 | 529 | ||
372 | return ret; | 530 | static int __init ramoops_init(void) |
531 | { | ||
532 | ramoops_register_dummy(); | ||
533 | return platform_driver_register(&ramoops_driver); | ||
373 | } | 534 | } |
535 | postcore_initcall(ramoops_init); | ||
374 | 536 | ||
375 | static void __exit ramoops_exit(void) | 537 | static void __exit ramoops_exit(void) |
376 | { | 538 | { |
377 | platform_driver_unregister(&ramoops_driver); | 539 | platform_driver_unregister(&ramoops_driver); |
378 | kfree(dummy_data); | 540 | kfree(dummy_data); |
379 | } | 541 | } |
380 | |||
381 | module_init(ramoops_init); | ||
382 | module_exit(ramoops_exit); | 542 | module_exit(ramoops_exit); |
383 | 543 | ||
384 | MODULE_LICENSE("GPL"); | 544 | MODULE_LICENSE("GPL"); |
diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c index c5fbdbbf81ac..eecd2a8a84dd 100644 --- a/fs/pstore/ram_core.c +++ b/fs/pstore/ram_core.c | |||
@@ -35,8 +35,6 @@ struct persistent_ram_buffer { | |||
35 | 35 | ||
36 | #define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */ | 36 | #define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */ |
37 | 37 | ||
38 | static __initdata LIST_HEAD(persistent_ram_list); | ||
39 | |||
40 | static inline size_t buffer_size(struct persistent_ram_zone *prz) | 38 | static inline size_t buffer_size(struct persistent_ram_zone *prz) |
41 | { | 39 | { |
42 | return atomic_read(&prz->buffer->size); | 40 | return atomic_read(&prz->buffer->size); |
@@ -116,7 +114,7 @@ static void notrace persistent_ram_update_ecc(struct persistent_ram_zone *prz, | |||
116 | int ecc_size = prz->ecc_size; | 114 | int ecc_size = prz->ecc_size; |
117 | int size = prz->ecc_block_size; | 115 | int size = prz->ecc_block_size; |
118 | 116 | ||
119 | if (!prz->ecc) | 117 | if (!prz->ecc_size) |
120 | return; | 118 | return; |
121 | 119 | ||
122 | block = buffer->data + (start & ~(ecc_block_size - 1)); | 120 | block = buffer->data + (start & ~(ecc_block_size - 1)); |
@@ -135,7 +133,7 @@ static void persistent_ram_update_header_ecc(struct persistent_ram_zone *prz) | |||
135 | { | 133 | { |
136 | struct persistent_ram_buffer *buffer = prz->buffer; | 134 | struct persistent_ram_buffer *buffer = prz->buffer; |
137 | 135 | ||
138 | if (!prz->ecc) | 136 | if (!prz->ecc_size) |
139 | return; | 137 | return; |
140 | 138 | ||
141 | persistent_ram_encode_rs8(prz, (uint8_t *)buffer, sizeof(*buffer), | 139 | persistent_ram_encode_rs8(prz, (uint8_t *)buffer, sizeof(*buffer), |
@@ -148,7 +146,7 @@ static void persistent_ram_ecc_old(struct persistent_ram_zone *prz) | |||
148 | uint8_t *block; | 146 | uint8_t *block; |
149 | uint8_t *par; | 147 | uint8_t *par; |
150 | 148 | ||
151 | if (!prz->ecc) | 149 | if (!prz->ecc_size) |
152 | return; | 150 | return; |
153 | 151 | ||
154 | block = buffer->data; | 152 | block = buffer->data; |
@@ -174,29 +172,30 @@ static void persistent_ram_ecc_old(struct persistent_ram_zone *prz) | |||
174 | } | 172 | } |
175 | 173 | ||
176 | static int persistent_ram_init_ecc(struct persistent_ram_zone *prz, | 174 | static int persistent_ram_init_ecc(struct persistent_ram_zone *prz, |
177 | size_t buffer_size) | 175 | int ecc_size) |
178 | { | 176 | { |
179 | int numerr; | 177 | int numerr; |
180 | struct persistent_ram_buffer *buffer = prz->buffer; | 178 | struct persistent_ram_buffer *buffer = prz->buffer; |
181 | int ecc_blocks; | 179 | int ecc_blocks; |
180 | size_t ecc_total; | ||
181 | int ecc_symsize = 8; | ||
182 | int ecc_poly = 0x11d; | ||
182 | 183 | ||
183 | if (!prz->ecc) | 184 | if (!ecc_size) |
184 | return 0; | 185 | return 0; |
185 | 186 | ||
186 | prz->ecc_block_size = 128; | 187 | prz->ecc_block_size = 128; |
187 | prz->ecc_size = 16; | 188 | prz->ecc_size = ecc_size; |
188 | prz->ecc_symsize = 8; | ||
189 | prz->ecc_poly = 0x11d; | ||
190 | 189 | ||
191 | ecc_blocks = DIV_ROUND_UP(prz->buffer_size, prz->ecc_block_size); | 190 | ecc_blocks = DIV_ROUND_UP(prz->buffer_size, prz->ecc_block_size); |
192 | prz->buffer_size -= (ecc_blocks + 1) * prz->ecc_size; | 191 | ecc_total = (ecc_blocks + 1) * prz->ecc_size; |
193 | 192 | if (ecc_total >= prz->buffer_size) { | |
194 | if (prz->buffer_size > buffer_size) { | 193 | pr_err("%s: invalid ecc_size %u (total %zu, buffer size %zu)\n", |
195 | pr_err("persistent_ram: invalid size %zu, non-ecc datasize %zu\n", | 194 | __func__, prz->ecc_size, ecc_total, prz->buffer_size); |
196 | buffer_size, prz->buffer_size); | ||
197 | return -EINVAL; | 195 | return -EINVAL; |
198 | } | 196 | } |
199 | 197 | ||
198 | prz->buffer_size -= ecc_total; | ||
200 | prz->par_buffer = buffer->data + prz->buffer_size; | 199 | prz->par_buffer = buffer->data + prz->buffer_size; |
201 | prz->par_header = prz->par_buffer + ecc_blocks * prz->ecc_size; | 200 | prz->par_header = prz->par_buffer + ecc_blocks * prz->ecc_size; |
202 | 201 | ||
@@ -204,8 +203,7 @@ static int persistent_ram_init_ecc(struct persistent_ram_zone *prz, | |||
204 | * first consecutive root is 0 | 203 | * first consecutive root is 0 |
205 | * primitive element to generate roots = 1 | 204 | * primitive element to generate roots = 1 |
206 | */ | 205 | */ |
207 | prz->rs_decoder = init_rs(prz->ecc_symsize, prz->ecc_poly, 0, 1, | 206 | prz->rs_decoder = init_rs(ecc_symsize, ecc_poly, 0, 1, prz->ecc_size); |
208 | prz->ecc_size); | ||
209 | if (prz->rs_decoder == NULL) { | 207 | if (prz->rs_decoder == NULL) { |
210 | pr_info("persistent_ram: init_rs failed\n"); | 208 | pr_info("persistent_ram: init_rs failed\n"); |
211 | return -EINVAL; | 209 | return -EINVAL; |
@@ -392,35 +390,36 @@ static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size, | |||
392 | return 0; | 390 | return 0; |
393 | } | 391 | } |
394 | 392 | ||
395 | static int __init persistent_ram_post_init(struct persistent_ram_zone *prz, bool ecc) | 393 | static int __devinit persistent_ram_post_init(struct persistent_ram_zone *prz, |
394 | u32 sig, int ecc_size) | ||
396 | { | 395 | { |
397 | int ret; | 396 | int ret; |
398 | 397 | ||
399 | prz->ecc = ecc; | 398 | ret = persistent_ram_init_ecc(prz, ecc_size); |
400 | |||
401 | ret = persistent_ram_init_ecc(prz, prz->buffer_size); | ||
402 | if (ret) | 399 | if (ret) |
403 | return ret; | 400 | return ret; |
404 | 401 | ||
405 | if (prz->buffer->sig == PERSISTENT_RAM_SIG) { | 402 | sig ^= PERSISTENT_RAM_SIG; |
403 | |||
404 | if (prz->buffer->sig == sig) { | ||
406 | if (buffer_size(prz) > prz->buffer_size || | 405 | if (buffer_size(prz) > prz->buffer_size || |
407 | buffer_start(prz) > buffer_size(prz)) | 406 | buffer_start(prz) > buffer_size(prz)) |
408 | pr_info("persistent_ram: found existing invalid buffer," | 407 | pr_info("persistent_ram: found existing invalid buffer," |
409 | " size %zu, start %zu\n", | 408 | " size %zu, start %zu\n", |
410 | buffer_size(prz), buffer_start(prz)); | 409 | buffer_size(prz), buffer_start(prz)); |
411 | else { | 410 | else { |
412 | pr_info("persistent_ram: found existing buffer," | 411 | pr_debug("persistent_ram: found existing buffer," |
413 | " size %zu, start %zu\n", | 412 | " size %zu, start %zu\n", |
414 | buffer_size(prz), buffer_start(prz)); | 413 | buffer_size(prz), buffer_start(prz)); |
415 | persistent_ram_save_old(prz); | 414 | persistent_ram_save_old(prz); |
416 | return 0; | 415 | return 0; |
417 | } | 416 | } |
418 | } else { | 417 | } else { |
419 | pr_info("persistent_ram: no valid data in buffer" | 418 | pr_debug("persistent_ram: no valid data in buffer" |
420 | " (sig = 0x%08x)\n", prz->buffer->sig); | 419 | " (sig = 0x%08x)\n", prz->buffer->sig); |
421 | } | 420 | } |
422 | 421 | ||
423 | prz->buffer->sig = PERSISTENT_RAM_SIG; | 422 | prz->buffer->sig = sig; |
424 | persistent_ram_zap(prz); | 423 | persistent_ram_zap(prz); |
425 | 424 | ||
426 | return 0; | 425 | return 0; |
@@ -428,19 +427,25 @@ static int __init persistent_ram_post_init(struct persistent_ram_zone *prz, bool | |||
428 | 427 | ||
429 | void persistent_ram_free(struct persistent_ram_zone *prz) | 428 | void persistent_ram_free(struct persistent_ram_zone *prz) |
430 | { | 429 | { |
431 | if (pfn_valid(prz->paddr >> PAGE_SHIFT)) { | 430 | if (!prz) |
432 | vunmap(prz->vaddr); | 431 | return; |
433 | } else { | 432 | |
434 | iounmap(prz->vaddr); | 433 | if (prz->vaddr) { |
435 | release_mem_region(prz->paddr, prz->size); | 434 | if (pfn_valid(prz->paddr >> PAGE_SHIFT)) { |
435 | vunmap(prz->vaddr); | ||
436 | } else { | ||
437 | iounmap(prz->vaddr); | ||
438 | release_mem_region(prz->paddr, prz->size); | ||
439 | } | ||
440 | prz->vaddr = NULL; | ||
436 | } | 441 | } |
437 | persistent_ram_free_old(prz); | 442 | persistent_ram_free_old(prz); |
438 | kfree(prz); | 443 | kfree(prz); |
439 | } | 444 | } |
440 | 445 | ||
441 | struct persistent_ram_zone * __init persistent_ram_new(phys_addr_t start, | 446 | struct persistent_ram_zone * __devinit persistent_ram_new(phys_addr_t start, |
442 | size_t size, | 447 | size_t size, u32 sig, |
443 | bool ecc) | 448 | int ecc_size) |
444 | { | 449 | { |
445 | struct persistent_ram_zone *prz; | 450 | struct persistent_ram_zone *prz; |
446 | int ret = -ENOMEM; | 451 | int ret = -ENOMEM; |
@@ -455,85 +460,12 @@ struct persistent_ram_zone * __init persistent_ram_new(phys_addr_t start, | |||
455 | if (ret) | 460 | if (ret) |
456 | goto err; | 461 | goto err; |
457 | 462 | ||
458 | persistent_ram_post_init(prz, ecc); | 463 | ret = persistent_ram_post_init(prz, sig, ecc_size); |
459 | 464 | if (ret) | |
460 | return prz; | ||
461 | err: | ||
462 | kfree(prz); | ||
463 | return ERR_PTR(ret); | ||
464 | } | ||
465 | |||
466 | #ifndef MODULE | ||
467 | static int __init persistent_ram_buffer_init(const char *name, | ||
468 | struct persistent_ram_zone *prz) | ||
469 | { | ||
470 | int i; | ||
471 | struct persistent_ram *ram; | ||
472 | struct persistent_ram_descriptor *desc; | ||
473 | phys_addr_t start; | ||
474 | |||
475 | list_for_each_entry(ram, &persistent_ram_list, node) { | ||
476 | start = ram->start; | ||
477 | for (i = 0; i < ram->num_descs; i++) { | ||
478 | desc = &ram->descs[i]; | ||
479 | if (!strcmp(desc->name, name)) | ||
480 | return persistent_ram_buffer_map(start, | ||
481 | desc->size, prz); | ||
482 | start += desc->size; | ||
483 | } | ||
484 | } | ||
485 | |||
486 | return -EINVAL; | ||
487 | } | ||
488 | |||
489 | static __init | ||
490 | struct persistent_ram_zone *__persistent_ram_init(struct device *dev, bool ecc) | ||
491 | { | ||
492 | struct persistent_ram_zone *prz; | ||
493 | int ret = -ENOMEM; | ||
494 | |||
495 | prz = kzalloc(sizeof(struct persistent_ram_zone), GFP_KERNEL); | ||
496 | if (!prz) { | ||
497 | pr_err("persistent_ram: failed to allocate persistent ram zone\n"); | ||
498 | goto err; | ||
499 | } | ||
500 | |||
501 | ret = persistent_ram_buffer_init(dev_name(dev), prz); | ||
502 | if (ret) { | ||
503 | pr_err("persistent_ram: failed to initialize buffer\n"); | ||
504 | goto err; | 465 | goto err; |
505 | } | ||
506 | |||
507 | persistent_ram_post_init(prz, ecc); | ||
508 | 466 | ||
509 | return prz; | 467 | return prz; |
510 | err: | 468 | err: |
511 | kfree(prz); | 469 | persistent_ram_free(prz); |
512 | return ERR_PTR(ret); | 470 | return ERR_PTR(ret); |
513 | } | 471 | } |
514 | |||
515 | struct persistent_ram_zone * __init | ||
516 | persistent_ram_init_ringbuffer(struct device *dev, bool ecc) | ||
517 | { | ||
518 | return __persistent_ram_init(dev, ecc); | ||
519 | } | ||
520 | |||
521 | int __init persistent_ram_early_init(struct persistent_ram *ram) | ||
522 | { | ||
523 | int ret; | ||
524 | |||
525 | ret = memblock_reserve(ram->start, ram->size); | ||
526 | if (ret) { | ||
527 | pr_err("Failed to reserve persistent memory from %08lx-%08lx\n", | ||
528 | (long)ram->start, (long)(ram->start + ram->size - 1)); | ||
529 | return ret; | ||
530 | } | ||
531 | |||
532 | list_add_tail(&ram->node, &persistent_ram_list); | ||
533 | |||
534 | pr_info("Initialized persistent memory from %08lx-%08lx\n", | ||
535 | (long)ram->start, (long)(ram->start + ram->size - 1)); | ||
536 | |||
537 | return 0; | ||
538 | } | ||
539 | #endif | ||