diff options
-rw-r--r-- | fs/pstore/Kconfig | 10 | ||||
-rw-r--r-- | fs/pstore/Makefile | 2 | ||||
-rw-r--r-- | fs/pstore/inode.c | 3 | ||||
-rw-r--r-- | fs/pstore/internal.h | 6 | ||||
-rw-r--r-- | fs/pstore/platform.c | 1 | ||||
-rw-r--r-- | fs/pstore/pmsg.c | 114 | ||||
-rw-r--r-- | fs/pstore/ram.c | 34 | ||||
-rw-r--r-- | include/linux/pstore.h | 1 | ||||
-rw-r--r-- | include/linux/pstore_ram.h | 1 |
9 files changed, 170 insertions, 2 deletions
diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig index 983d9510becc..916b8e23d968 100644 --- a/fs/pstore/Kconfig +++ b/fs/pstore/Kconfig | |||
@@ -21,6 +21,16 @@ config PSTORE_CONSOLE | |||
21 | When the option is enabled, pstore will log all kernel | 21 | When the option is enabled, pstore will log all kernel |
22 | messages, even if no oops or panic happened. | 22 | messages, even if no oops or panic happened. |
23 | 23 | ||
24 | config PSTORE_PMSG | ||
25 | bool "Log user space messages" | ||
26 | depends on PSTORE | ||
27 | help | ||
28 | When the option is enabled, pstore will export a character | ||
29 | interface /dev/pmsg0 to log user space messages. On reboot | ||
30 | data can be retrieved from /sys/fs/pstore/pmsg-ramoops-[ID]. | ||
31 | |||
32 | If unsure, say N. | ||
33 | |||
24 | config PSTORE_FTRACE | 34 | config PSTORE_FTRACE |
25 | bool "Persistent function tracer" | 35 | bool "Persistent function tracer" |
26 | depends on PSTORE | 36 | depends on PSTORE |
diff --git a/fs/pstore/Makefile b/fs/pstore/Makefile index 4c9095c2781e..e647d8e81712 100644 --- a/fs/pstore/Makefile +++ b/fs/pstore/Makefile | |||
@@ -7,5 +7,7 @@ obj-y += pstore.o | |||
7 | pstore-objs += inode.o platform.o | 7 | pstore-objs += inode.o platform.o |
8 | obj-$(CONFIG_PSTORE_FTRACE) += ftrace.o | 8 | obj-$(CONFIG_PSTORE_FTRACE) += ftrace.o |
9 | 9 | ||
10 | obj-$(CONFIG_PSTORE_PMSG) += pmsg.o | ||
11 | |||
10 | ramoops-objs += ram.o ram_core.o | 12 | ramoops-objs += ram.o ram_core.o |
11 | obj-$(CONFIG_PSTORE_RAM) += ramoops.o | 13 | obj-$(CONFIG_PSTORE_RAM) += ramoops.o |
diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index d69586f09ffd..b32ce53d24ee 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c | |||
@@ -361,6 +361,9 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, | |||
361 | scnprintf(name, sizeof(name), "powerpc-common-%s-%lld", | 361 | scnprintf(name, sizeof(name), "powerpc-common-%s-%lld", |
362 | psname, id); | 362 | psname, id); |
363 | break; | 363 | break; |
364 | case PSTORE_TYPE_PMSG: | ||
365 | scnprintf(name, sizeof(name), "pmsg-%s-%lld", psname, id); | ||
366 | break; | ||
364 | case PSTORE_TYPE_UNKNOWN: | 367 | case PSTORE_TYPE_UNKNOWN: |
365 | scnprintf(name, sizeof(name), "unknown-%s-%lld", psname, id); | 368 | scnprintf(name, sizeof(name), "unknown-%s-%lld", psname, id); |
366 | break; | 369 | break; |
diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h index 3b3d305277c4..c36ba2cd0b5d 100644 --- a/fs/pstore/internal.h +++ b/fs/pstore/internal.h | |||
@@ -45,6 +45,12 @@ extern void pstore_register_ftrace(void); | |||
45 | static inline void pstore_register_ftrace(void) {} | 45 | static inline void pstore_register_ftrace(void) {} |
46 | #endif | 46 | #endif |
47 | 47 | ||
48 | #ifdef CONFIG_PSTORE_PMSG | ||
49 | extern void pstore_register_pmsg(void); | ||
50 | #else | ||
51 | static inline void pstore_register_pmsg(void) {} | ||
52 | #endif | ||
53 | |||
48 | extern struct pstore_info *psinfo; | 54 | extern struct pstore_info *psinfo; |
49 | 55 | ||
50 | extern void pstore_set_kmsg_bytes(int); | 56 | extern void pstore_set_kmsg_bytes(int); |
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 0a9b72cdfeca..15ee78c5020b 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c | |||
@@ -447,6 +447,7 @@ int pstore_register(struct pstore_info *psi) | |||
447 | if ((psi->flags & PSTORE_FLAGS_FRAGILE) == 0) { | 447 | if ((psi->flags & PSTORE_FLAGS_FRAGILE) == 0) { |
448 | pstore_register_console(); | 448 | pstore_register_console(); |
449 | pstore_register_ftrace(); | 449 | pstore_register_ftrace(); |
450 | pstore_register_pmsg(); | ||
450 | } | 451 | } |
451 | 452 | ||
452 | if (pstore_update_ms >= 0) { | 453 | if (pstore_update_ms >= 0) { |
diff --git a/fs/pstore/pmsg.c b/fs/pstore/pmsg.c new file mode 100644 index 000000000000..feb5dd2948b4 --- /dev/null +++ b/fs/pstore/pmsg.c | |||
@@ -0,0 +1,114 @@ | |||
1 | /* | ||
2 | * Copyright 2014 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/cdev.h> | ||
15 | #include <linux/device.h> | ||
16 | #include <linux/fs.h> | ||
17 | #include <linux/uaccess.h> | ||
18 | #include <linux/vmalloc.h> | ||
19 | #include "internal.h" | ||
20 | |||
21 | static DEFINE_MUTEX(pmsg_lock); | ||
22 | #define PMSG_MAX_BOUNCE_BUFFER_SIZE (2*PAGE_SIZE) | ||
23 | |||
24 | static ssize_t write_pmsg(struct file *file, const char __user *buf, | ||
25 | size_t count, loff_t *ppos) | ||
26 | { | ||
27 | size_t i, buffer_size; | ||
28 | char *buffer; | ||
29 | |||
30 | if (!count) | ||
31 | return 0; | ||
32 | |||
33 | if (!access_ok(VERIFY_READ, buf, count)) | ||
34 | return -EFAULT; | ||
35 | |||
36 | buffer_size = count; | ||
37 | if (buffer_size > PMSG_MAX_BOUNCE_BUFFER_SIZE) | ||
38 | buffer_size = PMSG_MAX_BOUNCE_BUFFER_SIZE; | ||
39 | buffer = vmalloc(buffer_size); | ||
40 | |||
41 | mutex_lock(&pmsg_lock); | ||
42 | for (i = 0; i < count; ) { | ||
43 | size_t c = min(count - i, buffer_size); | ||
44 | u64 id; | ||
45 | long ret; | ||
46 | |||
47 | ret = __copy_from_user(buffer, buf + i, c); | ||
48 | if (unlikely(ret != 0)) { | ||
49 | mutex_unlock(&pmsg_lock); | ||
50 | vfree(buffer); | ||
51 | return -EFAULT; | ||
52 | } | ||
53 | psinfo->write_buf(PSTORE_TYPE_PMSG, 0, &id, 0, buffer, 0, c, | ||
54 | psinfo); | ||
55 | |||
56 | i += c; | ||
57 | } | ||
58 | |||
59 | mutex_unlock(&pmsg_lock); | ||
60 | vfree(buffer); | ||
61 | return count; | ||
62 | } | ||
63 | |||
64 | static const struct file_operations pmsg_fops = { | ||
65 | .owner = THIS_MODULE, | ||
66 | .llseek = noop_llseek, | ||
67 | .write = write_pmsg, | ||
68 | }; | ||
69 | |||
70 | static struct class *pmsg_class; | ||
71 | static int pmsg_major; | ||
72 | #define PMSG_NAME "pmsg" | ||
73 | #undef pr_fmt | ||
74 | #define pr_fmt(fmt) PMSG_NAME ": " fmt | ||
75 | |||
76 | static char *pmsg_devnode(struct device *dev, umode_t *mode) | ||
77 | { | ||
78 | if (mode) | ||
79 | *mode = 0220; | ||
80 | return NULL; | ||
81 | } | ||
82 | |||
83 | void pstore_register_pmsg(void) | ||
84 | { | ||
85 | struct device *pmsg_device; | ||
86 | |||
87 | pmsg_major = register_chrdev(0, PMSG_NAME, &pmsg_fops); | ||
88 | if (pmsg_major < 0) { | ||
89 | pr_err("register_chrdev failed\n"); | ||
90 | goto err; | ||
91 | } | ||
92 | |||
93 | pmsg_class = class_create(THIS_MODULE, PMSG_NAME); | ||
94 | if (IS_ERR(pmsg_class)) { | ||
95 | pr_err("device class file already in use\n"); | ||
96 | goto err_class; | ||
97 | } | ||
98 | pmsg_class->devnode = pmsg_devnode; | ||
99 | |||
100 | pmsg_device = device_create(pmsg_class, NULL, MKDEV(pmsg_major, 0), | ||
101 | NULL, "%s%d", PMSG_NAME, 0); | ||
102 | if (IS_ERR(pmsg_device)) { | ||
103 | pr_err("failed to create device\n"); | ||
104 | goto err_device; | ||
105 | } | ||
106 | return; | ||
107 | |||
108 | err_device: | ||
109 | class_destroy(pmsg_class); | ||
110 | err_class: | ||
111 | unregister_chrdev(pmsg_major, PMSG_NAME); | ||
112 | err: | ||
113 | return; | ||
114 | } | ||
diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index 6150e54eed30..39d1373128e9 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c | |||
@@ -51,6 +51,10 @@ static ulong ramoops_ftrace_size = MIN_MEM_SIZE; | |||
51 | module_param_named(ftrace_size, ramoops_ftrace_size, ulong, 0400); | 51 | module_param_named(ftrace_size, ramoops_ftrace_size, ulong, 0400); |
52 | MODULE_PARM_DESC(ftrace_size, "size of ftrace log"); | 52 | MODULE_PARM_DESC(ftrace_size, "size of ftrace log"); |
53 | 53 | ||
54 | static ulong ramoops_pmsg_size = MIN_MEM_SIZE; | ||
55 | module_param_named(pmsg_size, ramoops_pmsg_size, ulong, 0400); | ||
56 | MODULE_PARM_DESC(pmsg_size, "size of user space message log"); | ||
57 | |||
54 | static ulong mem_address; | 58 | static ulong mem_address; |
55 | module_param(mem_address, ulong, 0400); | 59 | module_param(mem_address, ulong, 0400); |
56 | MODULE_PARM_DESC(mem_address, | 60 | MODULE_PARM_DESC(mem_address, |
@@ -82,12 +86,14 @@ struct ramoops_context { | |||
82 | struct persistent_ram_zone **przs; | 86 | struct persistent_ram_zone **przs; |
83 | struct persistent_ram_zone *cprz; | 87 | struct persistent_ram_zone *cprz; |
84 | struct persistent_ram_zone *fprz; | 88 | struct persistent_ram_zone *fprz; |
89 | struct persistent_ram_zone *mprz; | ||
85 | phys_addr_t phys_addr; | 90 | phys_addr_t phys_addr; |
86 | unsigned long size; | 91 | unsigned long size; |
87 | unsigned int memtype; | 92 | unsigned int memtype; |
88 | size_t record_size; | 93 | size_t record_size; |
89 | size_t console_size; | 94 | size_t console_size; |
90 | size_t ftrace_size; | 95 | size_t ftrace_size; |
96 | size_t pmsg_size; | ||
91 | int dump_oops; | 97 | int dump_oops; |
92 | struct persistent_ram_ecc_info ecc_info; | 98 | struct persistent_ram_ecc_info ecc_info; |
93 | unsigned int max_dump_cnt; | 99 | unsigned int max_dump_cnt; |
@@ -96,6 +102,7 @@ struct ramoops_context { | |||
96 | unsigned int dump_read_cnt; | 102 | unsigned int dump_read_cnt; |
97 | unsigned int console_read_cnt; | 103 | unsigned int console_read_cnt; |
98 | unsigned int ftrace_read_cnt; | 104 | unsigned int ftrace_read_cnt; |
105 | unsigned int pmsg_read_cnt; | ||
99 | struct pstore_info pstore; | 106 | struct pstore_info pstore; |
100 | }; | 107 | }; |
101 | 108 | ||
@@ -109,6 +116,7 @@ static int ramoops_pstore_open(struct pstore_info *psi) | |||
109 | cxt->dump_read_cnt = 0; | 116 | cxt->dump_read_cnt = 0; |
110 | cxt->console_read_cnt = 0; | 117 | cxt->console_read_cnt = 0; |
111 | cxt->ftrace_read_cnt = 0; | 118 | cxt->ftrace_read_cnt = 0; |
119 | cxt->pmsg_read_cnt = 0; | ||
112 | return 0; | 120 | return 0; |
113 | } | 121 | } |
114 | 122 | ||
@@ -191,6 +199,9 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, | |||
191 | prz = ramoops_get_next_prz(&cxt->fprz, &cxt->ftrace_read_cnt, | 199 | prz = ramoops_get_next_prz(&cxt->fprz, &cxt->ftrace_read_cnt, |
192 | 1, id, type, PSTORE_TYPE_FTRACE, 0); | 200 | 1, id, type, PSTORE_TYPE_FTRACE, 0); |
193 | if (!prz_ok(prz)) | 201 | if (!prz_ok(prz)) |
202 | prz = ramoops_get_next_prz(&cxt->mprz, &cxt->pmsg_read_cnt, | ||
203 | 1, id, type, PSTORE_TYPE_PMSG, 0); | ||
204 | if (!prz_ok(prz)) | ||
194 | return 0; | 205 | return 0; |
195 | 206 | ||
196 | if (!persistent_ram_old(prz)) | 207 | if (!persistent_ram_old(prz)) |
@@ -258,6 +269,11 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type, | |||
258 | return -ENOMEM; | 269 | return -ENOMEM; |
259 | persistent_ram_write(cxt->fprz, buf, size); | 270 | persistent_ram_write(cxt->fprz, buf, size); |
260 | return 0; | 271 | return 0; |
272 | } else if (type == PSTORE_TYPE_PMSG) { | ||
273 | if (!cxt->mprz) | ||
274 | return -ENOMEM; | ||
275 | persistent_ram_write(cxt->mprz, buf, size); | ||
276 | return 0; | ||
261 | } | 277 | } |
262 | 278 | ||
263 | if (type != PSTORE_TYPE_DMESG) | 279 | if (type != PSTORE_TYPE_DMESG) |
@@ -315,6 +331,9 @@ static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, int count, | |||
315 | case PSTORE_TYPE_FTRACE: | 331 | case PSTORE_TYPE_FTRACE: |
316 | prz = cxt->fprz; | 332 | prz = cxt->fprz; |
317 | break; | 333 | break; |
334 | case PSTORE_TYPE_PMSG: | ||
335 | prz = cxt->mprz; | ||
336 | break; | ||
318 | default: | 337 | default: |
319 | return -EINVAL; | 338 | return -EINVAL; |
320 | } | 339 | } |
@@ -441,7 +460,7 @@ static int ramoops_probe(struct platform_device *pdev) | |||
441 | goto fail_out; | 460 | goto fail_out; |
442 | 461 | ||
443 | if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size && | 462 | if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size && |
444 | !pdata->ftrace_size)) { | 463 | !pdata->ftrace_size && !pdata->pmsg_size)) { |
445 | pr_err("The memory size and the record/console size must be " | 464 | pr_err("The memory size and the record/console size must be " |
446 | "non-zero\n"); | 465 | "non-zero\n"); |
447 | goto fail_out; | 466 | goto fail_out; |
@@ -453,6 +472,8 @@ static int ramoops_probe(struct platform_device *pdev) | |||
453 | pdata->console_size = rounddown_pow_of_two(pdata->console_size); | 472 | pdata->console_size = rounddown_pow_of_two(pdata->console_size); |
454 | if (pdata->ftrace_size && !is_power_of_2(pdata->ftrace_size)) | 473 | if (pdata->ftrace_size && !is_power_of_2(pdata->ftrace_size)) |
455 | pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size); | 474 | pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size); |
475 | if (pdata->pmsg_size && !is_power_of_2(pdata->pmsg_size)) | ||
476 | pdata->pmsg_size = rounddown_pow_of_two(pdata->pmsg_size); | ||
456 | 477 | ||
457 | cxt->size = pdata->mem_size; | 478 | cxt->size = pdata->mem_size; |
458 | cxt->phys_addr = pdata->mem_address; | 479 | cxt->phys_addr = pdata->mem_address; |
@@ -460,12 +481,14 @@ static int ramoops_probe(struct platform_device *pdev) | |||
460 | cxt->record_size = pdata->record_size; | 481 | cxt->record_size = pdata->record_size; |
461 | cxt->console_size = pdata->console_size; | 482 | cxt->console_size = pdata->console_size; |
462 | cxt->ftrace_size = pdata->ftrace_size; | 483 | cxt->ftrace_size = pdata->ftrace_size; |
484 | cxt->pmsg_size = pdata->pmsg_size; | ||
463 | cxt->dump_oops = pdata->dump_oops; | 485 | cxt->dump_oops = pdata->dump_oops; |
464 | cxt->ecc_info = pdata->ecc_info; | 486 | cxt->ecc_info = pdata->ecc_info; |
465 | 487 | ||
466 | paddr = cxt->phys_addr; | 488 | paddr = cxt->phys_addr; |
467 | 489 | ||
468 | dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size; | 490 | dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size |
491 | - cxt->pmsg_size; | ||
469 | err = ramoops_init_przs(dev, cxt, &paddr, dump_mem_sz); | 492 | err = ramoops_init_przs(dev, cxt, &paddr, dump_mem_sz); |
470 | if (err) | 493 | if (err) |
471 | goto fail_out; | 494 | goto fail_out; |
@@ -480,6 +503,10 @@ static int ramoops_probe(struct platform_device *pdev) | |||
480 | if (err) | 503 | if (err) |
481 | goto fail_init_fprz; | 504 | goto fail_init_fprz; |
482 | 505 | ||
506 | err = ramoops_init_prz(dev, cxt, &cxt->mprz, &paddr, cxt->pmsg_size, 0); | ||
507 | if (err) | ||
508 | goto fail_init_mprz; | ||
509 | |||
483 | cxt->pstore.data = cxt; | 510 | cxt->pstore.data = cxt; |
484 | /* | 511 | /* |
485 | * Console can handle any buffer size, so prefer LOG_LINE_MAX. If we | 512 | * Console can handle any buffer size, so prefer LOG_LINE_MAX. If we |
@@ -523,6 +550,8 @@ fail_buf: | |||
523 | kfree(cxt->pstore.buf); | 550 | kfree(cxt->pstore.buf); |
524 | fail_clear: | 551 | fail_clear: |
525 | cxt->pstore.bufsize = 0; | 552 | cxt->pstore.bufsize = 0; |
553 | kfree(cxt->mprz); | ||
554 | fail_init_mprz: | ||
526 | kfree(cxt->fprz); | 555 | kfree(cxt->fprz); |
527 | fail_init_fprz: | 556 | fail_init_fprz: |
528 | kfree(cxt->cprz); | 557 | kfree(cxt->cprz); |
@@ -580,6 +609,7 @@ static void ramoops_register_dummy(void) | |||
580 | dummy_data->record_size = record_size; | 609 | dummy_data->record_size = record_size; |
581 | dummy_data->console_size = ramoops_console_size; | 610 | dummy_data->console_size = ramoops_console_size; |
582 | dummy_data->ftrace_size = ramoops_ftrace_size; | 611 | dummy_data->ftrace_size = ramoops_ftrace_size; |
612 | dummy_data->pmsg_size = ramoops_pmsg_size; | ||
583 | dummy_data->dump_oops = dump_oops; | 613 | dummy_data->dump_oops = dump_oops; |
584 | /* | 614 | /* |
585 | * For backwards compatibility ramoops.ecc=1 means 16 bytes ECC | 615 | * For backwards compatibility ramoops.ecc=1 means 16 bytes ECC |
diff --git a/include/linux/pstore.h b/include/linux/pstore.h index ece0c6bbfcc5..8884f6e507f7 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h | |||
@@ -39,6 +39,7 @@ enum pstore_type_id { | |||
39 | PSTORE_TYPE_PPC_RTAS = 4, | 39 | PSTORE_TYPE_PPC_RTAS = 4, |
40 | PSTORE_TYPE_PPC_OF = 5, | 40 | PSTORE_TYPE_PPC_OF = 5, |
41 | PSTORE_TYPE_PPC_COMMON = 6, | 41 | PSTORE_TYPE_PPC_COMMON = 6, |
42 | PSTORE_TYPE_PMSG = 7, | ||
42 | PSTORE_TYPE_UNKNOWN = 255 | 43 | PSTORE_TYPE_UNKNOWN = 255 |
43 | }; | 44 | }; |
44 | 45 | ||
diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h index 4af3fdc85b01..9c9d6c154c8e 100644 --- a/include/linux/pstore_ram.h +++ b/include/linux/pstore_ram.h | |||
@@ -81,6 +81,7 @@ struct ramoops_platform_data { | |||
81 | unsigned long record_size; | 81 | unsigned long record_size; |
82 | unsigned long console_size; | 82 | unsigned long console_size; |
83 | unsigned long ftrace_size; | 83 | unsigned long ftrace_size; |
84 | unsigned long pmsg_size; | ||
84 | int dump_oops; | 85 | int dump_oops; |
85 | struct persistent_ram_ecc_info ecc_info; | 86 | struct persistent_ram_ecc_info ecc_info; |
86 | }; | 87 | }; |