]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
pstore: Add pmsg - user-space accessible pstore object
authorMark Salyzyn <salyzyn@android.com>
Sat, 17 Jan 2015 00:01:10 +0000 (16:01 -0800)
committerTony Luck <tony.luck@intel.com>
Sat, 17 Jan 2015 00:01:10 +0000 (16:01 -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>
Acked-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Tony Luck <tony.luck@intel.com>
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 983d9510becca65778e395817d563e793a266741..916b8e23d9684b7836b333e25cea49c85cc0ba26 100644 (file)
@@ -21,6 +21,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 d69586f09ffde0e03f92b4d0e767db8f6f0a5212..b32ce53d24ee5d7e07af71ee35873b5f7cb27ad4 100644 (file)
@@ -361,6 +361,9 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count,
                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;
        case PSTORE_TYPE_UNKNOWN:
                scnprintf(name, sizeof(name), "unknown-%s-%lld", psname, id);
                break;
index 3b3d305277c44eabaa3eae3c7aa95b28a9826a61..c36ba2cd0b5de2ee65a5acdc9531f294c84d9a06 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 0a9b72cdfecaba84bfc91d9d0453022b005a1c3a..15ee78c5020b24a0ca5c9f9b815157fea4bee96a 100644 (file)
@@ -447,6 +447,7 @@ int pstore_register(struct pstore_info *psi)
        if ((psi->flags & PSTORE_FLAGS_FRAGILE) == 0) {
                pstore_register_console();
                pstore_register_ftrace();
+               pstore_register_pmsg();
        }
 
        if (pstore_update_ms >= 0) {
diff --git a/fs/pstore/pmsg.c b/fs/pstore/pmsg.c
new file mode 100644 (file)
index 0000000..feb5dd2
--- /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, 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;
+}
index 6150e54eed30bc5f585c3afc25485d055ee3a0a3..39d1373128e94a6e2e0f91d53e2d5838e855211b 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,
@@ -82,12 +86,14 @@ 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;
        unsigned int memtype;
        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;
@@ -96,6 +102,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;
 };
 
@@ -109,6 +116,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;
 }
 
@@ -190,6 +198,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;
 
@@ -258,6 +269,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)
@@ -315,6 +331,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;
        }
@@ -441,7 +460,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;
@@ -453,6 +472,8 @@ static int ramoops_probe(struct platform_device *pdev)
                pdata->console_size = rounddown_pow_of_two(pdata->console_size);
        if (pdata->ftrace_size && !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;
@@ -460,12 +481,14 @@ static int ramoops_probe(struct platform_device *pdev)
        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;
@@ -480,6 +503,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
@@ -523,6 +550,8 @@ fail_buf:
        kfree(cxt->pstore.buf);
 fail_clear:
        cxt->pstore.bufsize = 0;
+       kfree(cxt->mprz);
+fail_init_mprz:
        kfree(cxt->fprz);
 fail_init_fprz:
        kfree(cxt->cprz);
@@ -580,6 +609,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 ece0c6bbfcc5617f394092ec027f39ce3abc629d..8884f6e507f7c7797b8cce98349e45f3b732a9b8 100644 (file)
@@ -39,6 +39,7 @@ enum pstore_type_id {
        PSTORE_TYPE_PPC_RTAS    = 4,
        PSTORE_TYPE_PPC_OF      = 5,
        PSTORE_TYPE_PPC_COMMON  = 6,
+       PSTORE_TYPE_PMSG        = 7,
        PSTORE_TYPE_UNKNOWN     = 255
 };
 
index 4af3fdc85b01cb6e36cd7bdae0783eb598c24843..9c9d6c154c8e926207856a749e2948a7d7346b69 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;
 };