[PATCH v4 4/5] pstore: add pmsg
authorMark Salyzyn <salyzyn@google.com>
Mon, 8 Dec 2014 23:06:52 +0000 (15:06 -0800)
committerMark Salyzyn <salyzyn@google.com>
Wed, 14 Jan 2015 00:22:01 +0000 (16:22 -0800)
A secured user-space accessible pstore object. Writes
to /dev/pmsg0 are appended to the buffer, on reboot
the persistent contents are available in
/sys/fs/pstore/pmsg-ramoops-[ID].

One possible use is syslogd, or other daemon, can
write messages, then on reboot provides a means to
triage user-space activities leading up to a panic
as a companion to the pstore dmesg or console logs.

Signed-off-by: Mark Salyzyn <salyzyn@android.com>
v2: switch from snprintf to scnprintf
v3: split out prz_ok checking into PATCH 3/5
    replace pmsg_lseek with noop_llseek
    use pr_fmt() macro
    make write atomic and use a vmalloc'd bounce buffer
v4: use mutex_lock instead of spin_lock.

Change-Id: I82a2a9a989d7583c5fcb65ff520027dc3a034a4c

fs/pstore/Kconfig
fs/pstore/Makefile
fs/pstore/inode.c
fs/pstore/internal.h
fs/pstore/platform.c
fs/pstore/pmsg.c [new file with mode: 0644]
fs/pstore/ram.c
include/linux/pstore.h
include/linux/pstore_ram.h

index ca71db69da07a000814837c20cf76bac049b3ca1..da12fd4e3d471e3475990320c78e94aa7e4ad4aa 100644 (file)
@@ -19,6 +19,16 @@ config PSTORE_CONSOLE
          When the option is enabled, pstore will log all kernel
          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
        bool "Persistent function tracer"
        depends on PSTORE
index 4c9095c2781e17c2d944081f20f9e2c639a9a6b8..e647d8e81712f7478cc6d5a0698fe27b7518d589 100644 (file)
@@ -7,5 +7,7 @@ obj-y += pstore.o
 pstore-objs += inode.o platform.o
 obj-$(CONFIG_PSTORE_FTRACE)    += ftrace.o
 
+obj-$(CONFIG_PSTORE_PMSG)      += pmsg.o
+
 ramoops-objs += ram.o ram_core.o
 obj-$(CONFIG_PSTORE_RAM)       += ramoops.o
index ae977dd0e426f9b4dfa9168972144a77e24a41ff..57bbfeaab66388c288fa8f51e0b0dd0380d9cd2b 100644 (file)
@@ -325,6 +325,9 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count,
        case PSTORE_TYPE_MCE:
                scnprintf(name, sizeof(name), "mce-%s-%lld", psname, id);
                break;
+       case PSTORE_TYPE_PMSG:
+               scnprintf(name, sizeof(name), "pmsg-%s-%lld", psname, id);
+               break;
        case PSTORE_TYPE_UNKNOWN:
                scnprintf(name, sizeof(name), "unknown-%s-%lld", psname, id);
                break;
index 937d820f273c2b17c5b47aa365f230970b19cc32..fd8d248c285ce0491d7c2a73fd2c93a73c616669 100644 (file)
@@ -45,6 +45,12 @@ extern void pstore_register_ftrace(void);
 static inline void pstore_register_ftrace(void) {}
 #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 void    pstore_set_kmsg_bytes(int);
index 86d1038b5a1292b464c561ba7086518cdd7eaf80..4f11f2382e2440f6edf1e5523b27beb2a10704ce 100644 (file)
@@ -267,6 +267,7 @@ int pstore_register(struct pstore_info *psi)
        kmsg_dump_register(&pstore_dumper);
        pstore_register_console();
        pstore_register_ftrace();
+       pstore_register_pmsg();
 
        if (pstore_update_ms >= 0) {
                pstore_timer.expires = jiffies +
diff --git a/fs/pstore/pmsg.c b/fs/pstore/pmsg.c
new file mode 100644 (file)
index 0000000..db47810
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * 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, 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;
+}
index 585422d705811c21b314bf9a34ca291ba4e99d5d..3a28d468b458f5e87c97e582421917861d5024ad 100644 (file)
@@ -51,6 +51,10 @@ static ulong ramoops_ftrace_size = MIN_MEM_SIZE;
 module_param_named(ftrace_size, ramoops_ftrace_size, ulong, 0400);
 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;
 module_param(mem_address, ulong, 0400);
 MODULE_PARM_DESC(mem_address,
@@ -77,11 +81,13 @@ struct ramoops_context {
        struct persistent_ram_zone **przs;
        struct persistent_ram_zone *cprz;
        struct persistent_ram_zone *fprz;
+       struct persistent_ram_zone *mprz;
        phys_addr_t phys_addr;
        unsigned long size;
        size_t record_size;
        size_t console_size;
        size_t ftrace_size;
+       size_t pmsg_size;
        int dump_oops;
        struct persistent_ram_ecc_info ecc_info;
        unsigned int max_dump_cnt;
@@ -90,6 +96,7 @@ struct ramoops_context {
        unsigned int dump_read_cnt;
        unsigned int console_read_cnt;
        unsigned int ftrace_read_cnt;
+       unsigned int pmsg_read_cnt;
        struct pstore_info pstore;
 };
 
@@ -103,6 +110,7 @@ static int ramoops_pstore_open(struct pstore_info *psi)
        cxt->dump_read_cnt = 0;
        cxt->console_read_cnt = 0;
        cxt->ftrace_read_cnt = 0;
+       cxt->pmsg_read_cnt = 0;
        return 0;
 }
 
@@ -157,6 +165,9 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
        if (!prz_ok(prz))
                prz = ramoops_get_next_prz(&cxt->fprz, &cxt->ftrace_read_cnt,
                                           1, id, type, PSTORE_TYPE_FTRACE, 0);
+       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;
 
@@ -220,6 +231,11 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type,
                        return -ENOMEM;
                persistent_ram_write(cxt->fprz, buf, size);
                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)
@@ -277,6 +293,9 @@ static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, int count,
        case PSTORE_TYPE_FTRACE:
                prz = cxt->fprz;
                break;
+       case PSTORE_TYPE_PMSG:
+               prz = cxt->mprz;
+               break;
        default:
                return -EINVAL;
        }
@@ -407,7 +426,7 @@ static int ramoops_probe(struct platform_device *pdev)
                goto fail_out;
 
        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 "
                        "non-zero\n");
                goto fail_out;
@@ -421,18 +440,22 @@ static int ramoops_probe(struct platform_device *pdev)
                pdata->console_size = rounddown_pow_of_two(pdata->console_size);
        if (!is_power_of_2(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->phys_addr = pdata->mem_address;
        cxt->record_size = pdata->record_size;
        cxt->console_size = pdata->console_size;
        cxt->ftrace_size = pdata->ftrace_size;
+       cxt->pmsg_size = pdata->pmsg_size;
        cxt->dump_oops = pdata->dump_oops;
        cxt->ecc_info = pdata->ecc_info;
 
        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);
        if (err)
                goto fail_out;
@@ -447,6 +470,10 @@ static int ramoops_probe(struct platform_device *pdev)
        if (err)
                goto fail_init_fprz;
 
+       err = ramoops_init_prz(dev, cxt, &cxt->mprz, &paddr, cxt->pmsg_size, 0);
+       if (err)
+               goto fail_init_mprz;
+
        cxt->pstore.data = cxt;
        /*
         * Console can handle any buffer size, so prefer LOG_LINE_MAX. If we
@@ -491,6 +518,8 @@ fail_buf:
 fail_clear:
        cxt->pstore.bufsize = 0;
        cxt->max_dump_cnt = 0;
+       kfree(cxt->mprz);
+fail_init_mprz:
        kfree(cxt->fprz);
 fail_init_fprz:
        kfree(cxt->cprz);
@@ -548,6 +577,7 @@ static void ramoops_register_dummy(void)
        dummy_data->record_size = record_size;
        dummy_data->console_size = ramoops_console_size;
        dummy_data->ftrace_size = ramoops_ftrace_size;
+       dummy_data->pmsg_size = ramoops_pmsg_size;
        dummy_data->dump_oops = dump_oops;
        /*
         * For backwards compatibility ramoops.ecc=1 means 16 bytes ECC
index 75d01760c91197e0a44066773b52af5bc063349b..289884121d30eba768b5167110c70aa8eaaf5bb5 100644 (file)
@@ -35,6 +35,7 @@ enum pstore_type_id {
        PSTORE_TYPE_MCE         = 1,
        PSTORE_TYPE_CONSOLE     = 2,
        PSTORE_TYPE_FTRACE      = 3,
+       PSTORE_TYPE_PMSG        = 4, /* Backport: 7 in upstream 3.19.0-rc3 */
        PSTORE_TYPE_UNKNOWN     = 255
 };
 
index 9e370618352a1bd86ccc6ced2d5ae3af0cc371cb..2c8e5dd7a762b82f709c3e23036c7f034225e3e4 100644 (file)
@@ -81,6 +81,7 @@ struct ramoops_platform_data {
        unsigned long   record_size;
        unsigned long   console_size;
        unsigned long   ftrace_size;
+       unsigned long   pmsg_size;
        int             dump_oops;
        struct persistent_ram_ecc_info ecc_info;
 };