Commit 07f80d41 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'please-pull-pstore' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux

Pull pstore update from Tony Luck:
 "Miscellaneous fs/pstore fixes"

* tag 'please-pull-pstore' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux:
  pstore: Fix sprintf format specifier in pstore_dump()
  pstore: Add pmsg - user-space accessible pstore object
  pstore: Handle zero-sized prz in series
  pstore: Remove superfluous memory size check
  pstore: Use scnprintf() in pstore_mkfile()
parents 6f83e5bd a6b8978c
...@@ -21,6 +21,16 @@ config PSTORE_CONSOLE ...@@ -21,6 +21,16 @@ config PSTORE_CONSOLE
When the option is enabled, pstore will log all kernel When the option is enabled, pstore will log all kernel
messages, even if no oops or panic happened. messages, even if no oops or panic happened.
config PSTORE_PMSG
bool "Log user space messages"
depends on PSTORE
help
When the option is enabled, pstore will export a character
interface /dev/pmsg0 to log user space messages. On reboot
data can be retrieved from /sys/fs/pstore/pmsg-ramoops-[ID].
If unsure, say N.
config PSTORE_FTRACE config PSTORE_FTRACE
bool "Persistent function tracer" bool "Persistent function tracer"
depends on PSTORE depends on PSTORE
......
...@@ -7,5 +7,7 @@ obj-y += pstore.o ...@@ -7,5 +7,7 @@ obj-y += pstore.o
pstore-objs += inode.o platform.o pstore-objs += inode.o platform.o
obj-$(CONFIG_PSTORE_FTRACE) += ftrace.o obj-$(CONFIG_PSTORE_FTRACE) += ftrace.o
obj-$(CONFIG_PSTORE_PMSG) += pmsg.o
ramoops-objs += ram.o ram_core.o ramoops-objs += ram.o ram_core.o
obj-$(CONFIG_PSTORE_RAM) += ramoops.o obj-$(CONFIG_PSTORE_RAM) += ramoops.o
...@@ -338,32 +338,38 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, ...@@ -338,32 +338,38 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count,
switch (type) { switch (type) {
case PSTORE_TYPE_DMESG: case PSTORE_TYPE_DMESG:
sprintf(name, "dmesg-%s-%lld%s", psname, id, scnprintf(name, sizeof(name), "dmesg-%s-%lld%s",
compressed ? ".enc.z" : ""); psname, id, compressed ? ".enc.z" : "");
break; break;
case PSTORE_TYPE_CONSOLE: case PSTORE_TYPE_CONSOLE:
sprintf(name, "console-%s-%lld", psname, id); scnprintf(name, sizeof(name), "console-%s-%lld", psname, id);
break; break;
case PSTORE_TYPE_FTRACE: case PSTORE_TYPE_FTRACE:
sprintf(name, "ftrace-%s-%lld", psname, id); scnprintf(name, sizeof(name), "ftrace-%s-%lld", psname, id);
break; break;
case PSTORE_TYPE_MCE: case PSTORE_TYPE_MCE:
sprintf(name, "mce-%s-%lld", psname, id); scnprintf(name, sizeof(name), "mce-%s-%lld", psname, id);
break; break;
case PSTORE_TYPE_PPC_RTAS: case PSTORE_TYPE_PPC_RTAS:
sprintf(name, "rtas-%s-%lld", psname, id); scnprintf(name, sizeof(name), "rtas-%s-%lld", psname, id);
break; break;
case PSTORE_TYPE_PPC_OF: case PSTORE_TYPE_PPC_OF:
sprintf(name, "powerpc-ofw-%s-%lld", psname, id); scnprintf(name, sizeof(name), "powerpc-ofw-%s-%lld",
psname, id);
break; break;
case PSTORE_TYPE_PPC_COMMON: case PSTORE_TYPE_PPC_COMMON:
sprintf(name, "powerpc-common-%s-%lld", psname, id); scnprintf(name, sizeof(name), "powerpc-common-%s-%lld",
psname, id);
break;
case PSTORE_TYPE_PMSG:
scnprintf(name, sizeof(name), "pmsg-%s-%lld", psname, id);
break; break;
case PSTORE_TYPE_UNKNOWN: case PSTORE_TYPE_UNKNOWN:
sprintf(name, "unknown-%s-%lld", psname, id); scnprintf(name, sizeof(name), "unknown-%s-%lld", psname, id);
break; break;
default: default:
sprintf(name, "type%d-%s-%lld", type, psname, id); scnprintf(name, sizeof(name), "type%d-%s-%lld",
type, psname, id);
break; break;
} }
......
...@@ -45,6 +45,12 @@ extern void pstore_register_ftrace(void); ...@@ -45,6 +45,12 @@ extern void pstore_register_ftrace(void);
static inline void pstore_register_ftrace(void) {} static inline void pstore_register_ftrace(void) {}
#endif #endif
#ifdef CONFIG_PSTORE_PMSG
extern void pstore_register_pmsg(void);
#else
static inline void pstore_register_pmsg(void) {}
#endif
extern struct pstore_info *psinfo; extern struct pstore_info *psinfo;
extern void pstore_set_kmsg_bytes(int); extern void pstore_set_kmsg_bytes(int);
......
...@@ -301,7 +301,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, ...@@ -301,7 +301,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
if (big_oops_buf) { if (big_oops_buf) {
dst = big_oops_buf; dst = big_oops_buf;
hsize = sprintf(dst, "%s#%d Part%d\n", why, hsize = sprintf(dst, "%s#%d Part%u\n", why,
oopscount, part); oopscount, part);
size = big_oops_buf_sz - hsize; size = big_oops_buf_sz - hsize;
...@@ -321,7 +321,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, ...@@ -321,7 +321,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
} }
} else { } else {
dst = psinfo->buf; dst = psinfo->buf;
hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, hsize = sprintf(dst, "%s#%d Part%u\n", why, oopscount,
part); part);
size = psinfo->bufsize - hsize; size = psinfo->bufsize - hsize;
dst += hsize; dst += hsize;
...@@ -447,6 +447,7 @@ int pstore_register(struct pstore_info *psi) ...@@ -447,6 +447,7 @@ int pstore_register(struct pstore_info *psi)
if ((psi->flags & PSTORE_FLAGS_FRAGILE) == 0) { if ((psi->flags & PSTORE_FLAGS_FRAGILE) == 0) {
pstore_register_console(); pstore_register_console();
pstore_register_ftrace(); pstore_register_ftrace();
pstore_register_pmsg();
} }
if (pstore_update_ms >= 0) { if (pstore_update_ms >= 0) {
......
/*
* Copyright 2014 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include "internal.h"
static DEFINE_MUTEX(pmsg_lock);
#define PMSG_MAX_BOUNCE_BUFFER_SIZE (2*PAGE_SIZE)
static ssize_t write_pmsg(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
size_t i, buffer_size;
char *buffer;
if (!count)
return 0;
if (!access_ok(VERIFY_READ, buf, count))
return -EFAULT;
buffer_size = count;
if (buffer_size > PMSG_MAX_BOUNCE_BUFFER_SIZE)
buffer_size = PMSG_MAX_BOUNCE_BUFFER_SIZE;
buffer = vmalloc(buffer_size);
mutex_lock(&pmsg_lock);
for (i = 0; i < count; ) {
size_t c = min(count - i, buffer_size);
u64 id;
long ret;
ret = __copy_from_user(buffer, buf + i, c);
if (unlikely(ret != 0)) {
mutex_unlock(&pmsg_lock);
vfree(buffer);
return -EFAULT;
}
psinfo->write_buf(PSTORE_TYPE_PMSG, 0, &id, 0, buffer, 0, c,
psinfo);
i += c;
}
mutex_unlock(&pmsg_lock);
vfree(buffer);
return count;
}
static const struct file_operations pmsg_fops = {
.owner = THIS_MODULE,
.llseek = noop_llseek,
.write = write_pmsg,
};
static struct class *pmsg_class;
static int pmsg_major;
#define PMSG_NAME "pmsg"
#undef pr_fmt
#define pr_fmt(fmt) PMSG_NAME ": " fmt
static char *pmsg_devnode(struct device *dev, umode_t *mode)
{
if (mode)
*mode = 0220;
return NULL;
}
void pstore_register_pmsg(void)
{
struct device *pmsg_device;
pmsg_major = register_chrdev(0, PMSG_NAME, &pmsg_fops);
if (pmsg_major < 0) {
pr_err("register_chrdev failed\n");
goto err;
}
pmsg_class = class_create(THIS_MODULE, PMSG_NAME);
if (IS_ERR(pmsg_class)) {
pr_err("device class file already in use\n");
goto err_class;
}
pmsg_class->devnode = pmsg_devnode;
pmsg_device = device_create(pmsg_class, NULL, MKDEV(pmsg_major, 0),
NULL, "%s%d", PMSG_NAME, 0);
if (IS_ERR(pmsg_device)) {
pr_err("failed to create device\n");
goto err_device;
}
return;
err_device:
class_destroy(pmsg_class);
err_class:
unregister_chrdev(pmsg_major, PMSG_NAME);
err:
return;
}
...@@ -51,6 +51,10 @@ static ulong ramoops_ftrace_size = MIN_MEM_SIZE; ...@@ -51,6 +51,10 @@ static ulong ramoops_ftrace_size = MIN_MEM_SIZE;
module_param_named(ftrace_size, ramoops_ftrace_size, ulong, 0400); module_param_named(ftrace_size, ramoops_ftrace_size, ulong, 0400);
MODULE_PARM_DESC(ftrace_size, "size of ftrace log"); MODULE_PARM_DESC(ftrace_size, "size of ftrace log");
static ulong ramoops_pmsg_size = MIN_MEM_SIZE;
module_param_named(pmsg_size, ramoops_pmsg_size, ulong, 0400);
MODULE_PARM_DESC(pmsg_size, "size of user space message log");
static ulong mem_address; static ulong mem_address;
module_param(mem_address, ulong, 0400); module_param(mem_address, ulong, 0400);
MODULE_PARM_DESC(mem_address, MODULE_PARM_DESC(mem_address,
...@@ -82,12 +86,14 @@ struct ramoops_context { ...@@ -82,12 +86,14 @@ struct ramoops_context {
struct persistent_ram_zone **przs; struct persistent_ram_zone **przs;
struct persistent_ram_zone *cprz; struct persistent_ram_zone *cprz;
struct persistent_ram_zone *fprz; struct persistent_ram_zone *fprz;
struct persistent_ram_zone *mprz;
phys_addr_t phys_addr; phys_addr_t phys_addr;
unsigned long size; unsigned long size;
unsigned int memtype; unsigned int memtype;
size_t record_size; size_t record_size;
size_t console_size; size_t console_size;
size_t ftrace_size; size_t ftrace_size;
size_t pmsg_size;
int dump_oops; int dump_oops;
struct persistent_ram_ecc_info ecc_info; struct persistent_ram_ecc_info ecc_info;
unsigned int max_dump_cnt; unsigned int max_dump_cnt;
...@@ -96,6 +102,7 @@ struct ramoops_context { ...@@ -96,6 +102,7 @@ struct ramoops_context {
unsigned int dump_read_cnt; unsigned int dump_read_cnt;
unsigned int console_read_cnt; unsigned int console_read_cnt;
unsigned int ftrace_read_cnt; unsigned int ftrace_read_cnt;
unsigned int pmsg_read_cnt;
struct pstore_info pstore; struct pstore_info pstore;
}; };
...@@ -109,6 +116,7 @@ static int ramoops_pstore_open(struct pstore_info *psi) ...@@ -109,6 +116,7 @@ static int ramoops_pstore_open(struct pstore_info *psi)
cxt->dump_read_cnt = 0; cxt->dump_read_cnt = 0;
cxt->console_read_cnt = 0; cxt->console_read_cnt = 0;
cxt->ftrace_read_cnt = 0; cxt->ftrace_read_cnt = 0;
cxt->pmsg_read_cnt = 0;
return 0; return 0;
} }
...@@ -164,6 +172,12 @@ static int ramoops_read_kmsg_hdr(char *buffer, struct timespec *time, ...@@ -164,6 +172,12 @@ static int ramoops_read_kmsg_hdr(char *buffer, struct timespec *time,
return header_length; return header_length;
} }
static bool prz_ok(struct persistent_ram_zone *prz)
{
return !!prz && !!(persistent_ram_old_size(prz) +
persistent_ram_ecc_string(prz, NULL, 0));
}
static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
int *count, struct timespec *time, int *count, struct timespec *time,
char **buf, bool *compressed, char **buf, bool *compressed,
...@@ -178,13 +192,16 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, ...@@ -178,13 +192,16 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
prz = ramoops_get_next_prz(cxt->przs, &cxt->dump_read_cnt, prz = ramoops_get_next_prz(cxt->przs, &cxt->dump_read_cnt,
cxt->max_dump_cnt, id, type, cxt->max_dump_cnt, id, type,
PSTORE_TYPE_DMESG, 1); PSTORE_TYPE_DMESG, 1);
if (!prz) if (!prz_ok(prz))
prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt, prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt,
1, id, type, PSTORE_TYPE_CONSOLE, 0); 1, id, type, PSTORE_TYPE_CONSOLE, 0);
if (!prz) if (!prz_ok(prz))
prz = ramoops_get_next_prz(&cxt->fprz, &cxt->ftrace_read_cnt, prz = ramoops_get_next_prz(&cxt->fprz, &cxt->ftrace_read_cnt,
1, id, type, PSTORE_TYPE_FTRACE, 0); 1, id, type, PSTORE_TYPE_FTRACE, 0);
if (!prz) if (!prz_ok(prz))
prz = ramoops_get_next_prz(&cxt->mprz, &cxt->pmsg_read_cnt,
1, id, type, PSTORE_TYPE_PMSG, 0);
if (!prz_ok(prz))
return 0; return 0;
if (!persistent_ram_old(prz)) if (!persistent_ram_old(prz))
...@@ -252,6 +269,11 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type, ...@@ -252,6 +269,11 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type,
return -ENOMEM; return -ENOMEM;
persistent_ram_write(cxt->fprz, buf, size); persistent_ram_write(cxt->fprz, buf, size);
return 0; return 0;
} else if (type == PSTORE_TYPE_PMSG) {
if (!cxt->mprz)
return -ENOMEM;
persistent_ram_write(cxt->mprz, buf, size);
return 0;
} }
if (type != PSTORE_TYPE_DMESG) if (type != PSTORE_TYPE_DMESG)
...@@ -309,6 +331,9 @@ static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, int count, ...@@ -309,6 +331,9 @@ static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, int count,
case PSTORE_TYPE_FTRACE: case PSTORE_TYPE_FTRACE:
prz = cxt->fprz; prz = cxt->fprz;
break; break;
case PSTORE_TYPE_PMSG:
prz = cxt->mprz;
break;
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -435,7 +460,7 @@ static int ramoops_probe(struct platform_device *pdev) ...@@ -435,7 +460,7 @@ static int ramoops_probe(struct platform_device *pdev)
goto fail_out; goto fail_out;
if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size && if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size &&
!pdata->ftrace_size)) { !pdata->ftrace_size && !pdata->pmsg_size)) {
pr_err("The memory size and the record/console size must be " pr_err("The memory size and the record/console size must be "
"non-zero\n"); "non-zero\n");
goto fail_out; goto fail_out;
...@@ -447,6 +472,8 @@ static int ramoops_probe(struct platform_device *pdev) ...@@ -447,6 +472,8 @@ static int ramoops_probe(struct platform_device *pdev)
pdata->console_size = rounddown_pow_of_two(pdata->console_size); pdata->console_size = rounddown_pow_of_two(pdata->console_size);
if (pdata->ftrace_size && !is_power_of_2(pdata->ftrace_size)) if (pdata->ftrace_size && !is_power_of_2(pdata->ftrace_size))
pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size); pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size);
if (pdata->pmsg_size && !is_power_of_2(pdata->pmsg_size))
pdata->pmsg_size = rounddown_pow_of_two(pdata->pmsg_size);
cxt->size = pdata->mem_size; cxt->size = pdata->mem_size;
cxt->phys_addr = pdata->mem_address; cxt->phys_addr = pdata->mem_address;
...@@ -454,12 +481,14 @@ static int ramoops_probe(struct platform_device *pdev) ...@@ -454,12 +481,14 @@ static int ramoops_probe(struct platform_device *pdev)
cxt->record_size = pdata->record_size; cxt->record_size = pdata->record_size;
cxt->console_size = pdata->console_size; cxt->console_size = pdata->console_size;
cxt->ftrace_size = pdata->ftrace_size; cxt->ftrace_size = pdata->ftrace_size;
cxt->pmsg_size = pdata->pmsg_size;
cxt->dump_oops = pdata->dump_oops; cxt->dump_oops = pdata->dump_oops;
cxt->ecc_info = pdata->ecc_info; cxt->ecc_info = pdata->ecc_info;
paddr = cxt->phys_addr; paddr = cxt->phys_addr;
dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size; dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size
- cxt->pmsg_size;
err = ramoops_init_przs(dev, cxt, &paddr, dump_mem_sz); err = ramoops_init_przs(dev, cxt, &paddr, dump_mem_sz);
if (err) if (err)
goto fail_out; goto fail_out;
...@@ -474,13 +503,9 @@ static int ramoops_probe(struct platform_device *pdev) ...@@ -474,13 +503,9 @@ static int ramoops_probe(struct platform_device *pdev)
if (err) if (err)
goto fail_init_fprz; goto fail_init_fprz;
if (!cxt->przs && !cxt->cprz && !cxt->fprz) { err = ramoops_init_prz(dev, cxt, &cxt->mprz, &paddr, cxt->pmsg_size, 0);
pr_err("memory size too small, minimum is %zu\n", if (err)
cxt->console_size + cxt->record_size + goto fail_init_mprz;
cxt->ftrace_size);
err = -EINVAL;
goto fail_cnt;
}
cxt->pstore.data = cxt; cxt->pstore.data = cxt;
/* /*
...@@ -525,7 +550,8 @@ static int ramoops_probe(struct platform_device *pdev) ...@@ -525,7 +550,8 @@ static int ramoops_probe(struct platform_device *pdev)
kfree(cxt->pstore.buf); kfree(cxt->pstore.buf);
fail_clear: fail_clear:
cxt->pstore.bufsize = 0; cxt->pstore.bufsize = 0;
fail_cnt: kfree(cxt->mprz);
fail_init_mprz:
kfree(cxt->fprz); kfree(cxt->fprz);
fail_init_fprz: fail_init_fprz:
kfree(cxt->cprz); kfree(cxt->cprz);
...@@ -583,6 +609,7 @@ static void ramoops_register_dummy(void) ...@@ -583,6 +609,7 @@ static void ramoops_register_dummy(void)
dummy_data->record_size = record_size; dummy_data->record_size = record_size;
dummy_data->console_size = ramoops_console_size; dummy_data->console_size = ramoops_console_size;
dummy_data->ftrace_size = ramoops_ftrace_size; dummy_data->ftrace_size = ramoops_ftrace_size;
dummy_data->pmsg_size = ramoops_pmsg_size;
dummy_data->dump_oops = dump_oops; dummy_data->dump_oops = dump_oops;
/* /*
* For backwards compatibility ramoops.ecc=1 means 16 bytes ECC * For backwards compatibility ramoops.ecc=1 means 16 bytes ECC
......
...@@ -39,6 +39,7 @@ enum pstore_type_id { ...@@ -39,6 +39,7 @@ enum pstore_type_id {
PSTORE_TYPE_PPC_RTAS = 4, PSTORE_TYPE_PPC_RTAS = 4,
PSTORE_TYPE_PPC_OF = 5, PSTORE_TYPE_PPC_OF = 5,
PSTORE_TYPE_PPC_COMMON = 6, PSTORE_TYPE_PPC_COMMON = 6,
PSTORE_TYPE_PMSG = 7,
PSTORE_TYPE_UNKNOWN = 255 PSTORE_TYPE_UNKNOWN = 255
}; };
......
...@@ -81,6 +81,7 @@ struct ramoops_platform_data { ...@@ -81,6 +81,7 @@ struct ramoops_platform_data {
unsigned long record_size; unsigned long record_size;
unsigned long console_size; unsigned long console_size;
unsigned long ftrace_size; unsigned long ftrace_size;
unsigned long pmsg_size;
int dump_oops; int dump_oops;
struct persistent_ram_ecc_info ecc_info; struct persistent_ram_ecc_info ecc_info;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment