fs:pstore:ramoops: Add annotation support to pstore
authorzhaoxp3 <zhaoxp3@motorola.com>
Thu, 2 Aug 2018 06:51:07 +0000 (14:51 +0800)
committerlingsen1 <lingsen1@lenovo.com>
Sun, 7 Feb 2021 09:36:46 +0000 (17:36 +0800)
Add annotation support to the pstore. This is used by bootinfo
Ported from: https://gerrit.mot.com/1102744

Change-Id: Id81064efc3a2550e21b73fe57a3f2db5c552e331
Signed-off-by: zhaoxp3 <zhaoxp3@motorola.com>
Reviewed-on: https://gerrit.mot.com/1220328
SLTApproved: Slta Waiver
SME-Granted: SME Approvals Granted
Tested-by: Jira Key
Submit-Approved: Jira Key

fs/pstore/Kconfig
fs/pstore/inode.c
fs/pstore/platform.c
fs/pstore/ram.c
fs/pstore/ram_core.c
include/linux/pstore.h
include/linux/pstore_ram.h
lib/debug-snapshot-pstore.c

index b42e5bd6d8ffdd124cdadbbaa05c267510cbca09..825d559c33a9ad7b3051001d0d5a351d64284f47 100644 (file)
@@ -87,3 +87,17 @@ config PSTORE_RAM
          "ramoops.ko".
 
          For more information, see Documentation/admin-guide/ramoops.rst.
+
+config PSTORE_RAM_ANNOTATION_APPEND
+       bool "Allow appending messages to pstore ram annotation"
+       default n
+       depends on PSTORE_RAM
+       help
+         Existing pstore ram annotation stores messages and exports them
+         after reset. Sometimes only after reset can we do some postmortem
+         analysis. This adds function which can be used to append the
+         analysis info to pstore ram annotation. The function should only
+         be used before pstore ram annotation is exported. It's safe to use
+         the function before pstore is initialized. Content will be buffered
+         and merged to annotation-ramoops file.
+         If unsure, say N.
index d814723fb27dcdfc80168bf97a5c10ab20fcc0c2..0fff750e9bb67ac6602bcb60f052e7fb33714438 100644 (file)
@@ -143,6 +143,42 @@ static ssize_t pstore_file_read(struct file *file, char __user *userbuf,
                                       ps->record->buf, ps->total_size);
 }
 
+#define PSTORE_ANNOTATE_MAX_SIZE 0x100
+static ssize_t pstore_file_write(struct file *file, const char __user *userbuf,
+                                                size_t count, loff_t *ppos)
+{
+       struct seq_file *sf = file->private_data;
+       struct pstore_private *ps = sf->private;
+       char *buffer;
+       ssize_t ret = 0;
+       ssize_t saved_count = count;
+
+       if (!count || !userbuf)
+               return 0;
+
+       if (ps->record->type != PSTORE_TYPE_ANNOTATE)
+               return count;
+
+       if (count > PSTORE_ANNOTATE_MAX_SIZE)
+               count = PSTORE_ANNOTATE_MAX_SIZE;
+
+       buffer = kmalloc(count + 1, GFP_KERNEL);
+       if (!buffer)
+               return -ENOMEM;
+
+       if (copy_from_user(buffer, userbuf, count)) {
+               ret = -EFAULT;
+               goto write_out;
+       }
+
+       buffer[count] = '\0';
+       pstore_annotate(buffer);
+       ret = saved_count;
+write_out:
+       kfree(buffer);
+       return ret;
+}
+
 static int pstore_file_open(struct inode *inode, struct file *file)
 {
        struct pstore_private *ps = inode->i_private;
@@ -175,6 +211,7 @@ static loff_t pstore_file_llseek(struct file *file, loff_t off, int whence)
 static const struct file_operations pstore_file_operations = {
        .open           = pstore_file_open,
        .read           = pstore_file_read,
+       .write          = pstore_file_write,
        .llseek         = pstore_file_llseek,
        .release        = seq_release,
 };
@@ -373,6 +410,9 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)
                scnprintf(name, sizeof(name), "powerpc-opal-%s-%llu",
                          record->psi->name, record->id);
                break;
+       case PSTORE_TYPE_ANNOTATE:
+               scnprintf(name, sizeof(name), "annotate-%s-%lld", record->psi->name, record->id);
+               break;
        case PSTORE_TYPE_UNKNOWN:
                scnprintf(name, sizeof(name), "unknown-%s-%llu",
                          record->psi->name, record->id);
index d86199761748fc365d9ffad17aaa7459dbf68a36..171f3621d1f830571b64df99b58ddb9dc1109933 100644 (file)
@@ -430,6 +430,7 @@ static int pstore_decompress(void *in, void *out, size_t inlen, size_t outlen)
                return -EIO;
 }
 
+#if 0
 static void allocate_buf_for_compression(void)
 {
        if (zbackend) {
@@ -439,6 +440,7 @@ static void allocate_buf_for_compression(void)
                pr_err("allocate compression buffer error!\n");
        }
 }
+#endif
 
 static void free_buf_for_compression(void)
 {
@@ -594,6 +596,53 @@ static void pstore_unregister_kmsg(void)
        kmsg_dump_unregister(&pstore_dumper);
 }
 
+
+/* Export function to save device specific power up
+ * data
+ *
+ */
+
+int  pstore_annotate(const char *buf)
+{
+       unsigned cnt = strlen(buf);
+       const char *end = buf + cnt;
+
+       if (!psinfo) {
+               pr_warn("device not present!\n");
+               return -ENODEV;
+       }
+       while (buf < end) {
+               unsigned long flags;
+               int ret;
+               struct pstore_record record;
+
+        pstore_record_init(&record, psinfo);
+        record.type = PSTORE_TYPE_ANNOTATE;
+
+               if (cnt > psinfo->bufsize)
+                       cnt = psinfo->bufsize;
+
+               if (oops_in_progress) {
+                       if (!spin_trylock_irqsave(&psinfo->buf_lock, flags))
+                               break;
+               } else {
+                       spin_lock_irqsave(&psinfo->buf_lock, flags);
+               }
+               record.buf = (char *)buf;
+               record.size = cnt;
+               ret = psinfo->write(&record);
+               spin_unlock_irqrestore(&psinfo->buf_lock, flags);
+
+               pr_debug("ret %d wrote bytes %d\n", ret, cnt);
+               buf += cnt;
+               cnt = end - buf;
+       }
+       return 0;
+
+}
+EXPORT_SYMBOL_GPL(pstore_annotate);
+
+
 #ifdef CONFIG_PSTORE_CONSOLE
 static void pstore_console_write(struct console *con, const char *s, unsigned c)
 {
@@ -720,7 +769,12 @@ int pstore_register(struct pstore_info *psi)
                return -EINVAL;
        }
 
+       // FIX ME!
+       // Disable pstore compression/decompression for ramdom decompression fail
+       // will enable it when DDR is stable
+#if 0
        allocate_buf_for_compression();
+#endif
 
        if (pstore_is_mounted())
                pstore_get_records(0);
index 0db2b696a434b5b60cb6528384a8ce7b2a66f02c..42ac02ee4fe3c4ef4a0c6ddf0ddcb679ccf078f8 100644 (file)
@@ -56,6 +56,10 @@ 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 ramoops_annotate_size = MIN_MEM_SIZE;
+module_param_named(annotate_size, ramoops_annotate_size, ulong, 0400);
+MODULE_PARM_DESC(annotate_size, "size of annotation");
+
 static unsigned long long mem_address;
 module_param_hw(mem_address, ullong, other, 0400);
 MODULE_PARM_DESC(mem_address,
@@ -88,6 +92,7 @@ struct ramoops_context {
        struct persistent_ram_zone *cprz;       /* Console zone */
        struct persistent_ram_zone **fprzs;     /* Ftrace zones */
        struct persistent_ram_zone *mprz;       /* PMSG zone */
+       struct persistent_ram_zone *aprz;
        phys_addr_t phys_addr;
        unsigned long size;
        unsigned int memtype;
@@ -95,6 +100,7 @@ struct ramoops_context {
        size_t console_size;
        size_t ftrace_size;
        size_t pmsg_size;
+       size_t annotate_size;
        int dump_oops;
        u32 flags;
        struct persistent_ram_ecc_info ecc_info;
@@ -106,6 +112,7 @@ struct ramoops_context {
        unsigned int max_ftrace_cnt;
        unsigned int ftrace_read_cnt;
        unsigned int pmsg_read_cnt;
+       unsigned int annotate_read_cnt;
        struct pstore_info pstore;
 };
 
@@ -120,6 +127,7 @@ static int ramoops_pstore_open(struct pstore_info *psi)
        cxt->console_read_cnt = 0;
        cxt->ftrace_read_cnt = 0;
        cxt->pmsg_read_cnt = 0;
+       cxt->annotate_read_cnt = 0;
        return 0;
 }
 
@@ -279,6 +287,11 @@ static ssize_t ramoops_pstore_read(struct pstore_record *record)
                prz = ramoops_get_next_prz(&cxt->mprz, &cxt->pmsg_read_cnt,
                                           1, &record->id, &record->type,
                                           PSTORE_TYPE_PMSG, 0);
+       if (!prz_ok(prz)) {
+               prz = ramoops_get_next_prz(&cxt->aprz, &cxt->annotate_read_cnt,
+                                       1, &record->id, &record->type, PSTORE_TYPE_ANNOTATE, 0);
+               persistent_ram_annotation_merge(prz);
+       }
 
        /* ftrace is last since it may want to dynamically allocate memory. */
        if (!prz_ok(prz)) {
@@ -402,6 +415,11 @@ static int notrace ramoops_pstore_write(struct pstore_record *record)
        } else if (record->type == PSTORE_TYPE_PMSG) {
                pr_warn_ratelimited("PMSG shouldn't call %s\n", __func__);
                return -EINVAL;
+       } else if (record->type == PSTORE_TYPE_ANNOTATE) {
+               if (!cxt->aprz)
+                       return -ENOMEM;
+               persistent_ram_write(cxt->aprz, record->buf, record->size);
+               return 0;
        }
 
        if (record->type != PSTORE_TYPE_DMESG)
@@ -411,8 +429,7 @@ static int notrace ramoops_pstore_write(struct pstore_record *record)
         * Out of the various dmesg dump types, ramoops is currently designed
         * to only store crash logs, rather than storing general kernel logs.
         */
-       if (record->reason != KMSG_DUMP_OOPS &&
-           record->reason != KMSG_DUMP_PANIC)
+       if (record->reason != KMSG_DUMP_PANIC)
                return -EINVAL;
 
        /* Skip Oopes when configured to do so. */
@@ -481,6 +498,9 @@ static int ramoops_pstore_erase(struct pstore_record *record)
        case PSTORE_TYPE_PMSG:
                prz = cxt->mprz;
                break;
+       case PSTORE_TYPE_ANNOTATE:
+               prz = cxt->aprz;
+               break;
        default:
                return -EINVAL;
        }
@@ -701,6 +721,7 @@ static int ramoops_parse_dt(struct platform_device *pdev,
        parse_size("pmsg-size", pdata->pmsg_size);
        parse_size("ecc-size", pdata->ecc_info.ecc_size);
        parse_size("flags", pdata->flags);
+       parse_size("ramoops-annotate-size", pdata->annotate_size);
 
 #undef parse_size
 
@@ -731,7 +752,7 @@ static int ramoops_probe(struct platform_device *pdev)
                if (err < 0)
                        goto fail_out;
        }
-
+       
        /*
         * Only a single ramoops area allowed at a time, so fail extra
         * probes.
@@ -748,7 +769,8 @@ static int ramoops_probe(struct platform_device *pdev)
        }
 
        if (!pdata->mem_size || (!pdata->record_size && !pdata->console_size &&
-                       !pdata->ftrace_size && !pdata->pmsg_size)) {
+                       !pdata->ftrace_size && !pdata->pmsg_size &&
+                       !pdata->annotate_size)) {
                pr_err("The memory size and the record/console size must be "
                        "non-zero\n");
                goto fail_out;
@@ -762,6 +784,13 @@ static int ramoops_probe(struct platform_device *pdev)
                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);
+       if (pdata->annotate_size && !is_power_of_2(pdata->annotate_size))
+               pdata->annotate_size =
+                       rounddown_pow_of_two(pdata->annotate_size);
+
+       pr_debug("All %#lx record %#lx console %#lx ftrace %#lx annotate %#lx\n",
+               pdata->mem_size, pdata->record_size, pdata->console_size,
+               pdata->ftrace_size, pdata->annotate_size);
 
        cxt->size = pdata->mem_size;
        cxt->phys_addr = pdata->mem_address;
@@ -770,6 +799,7 @@ static int ramoops_probe(struct platform_device *pdev)
        cxt->console_size = pdata->console_size;
        cxt->ftrace_size = pdata->ftrace_size;
        cxt->pmsg_size = pdata->pmsg_size;
+       cxt->annotate_size = pdata->annotate_size;
        cxt->dump_oops = pdata->dump_oops;
        cxt->flags = pdata->flags;
        cxt->ecc_info = pdata->ecc_info;
@@ -777,7 +807,7 @@ static int ramoops_probe(struct platform_device *pdev)
        paddr = cxt->phys_addr;
 
        dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size
-                       - cxt->pmsg_size;
+                       - cxt->pmsg_size - cxt->annotate_size;
        err = ramoops_init_przs("dump", dev, cxt, &cxt->dprzs, &paddr,
                                dump_mem_sz, cxt->record_size,
                                &cxt->max_dump_cnt, 0, 0);
@@ -805,6 +835,13 @@ static int ramoops_probe(struct platform_device *pdev)
        if (err)
                goto fail_init_mprz;
 
+        err = ramoops_init_prz("annotate", dev, cxt, &cxt->aprz, &paddr,
+                               cxt->annotate_size, 0);
+
+       if (err) {
+               goto fail_init_aprz;
+       }
+
        cxt->pstore.data = cxt;
        /*
         * Since bufsize is only used for dmesg crash dumps, it
@@ -827,6 +864,8 @@ static int ramoops_probe(struct platform_device *pdev)
                cxt->pstore.flags |= PSTORE_FLAGS_FTRACE;
        if (cxt->pmsg_size)
                cxt->pstore.flags |= PSTORE_FLAGS_PMSG;
+       if (cxt->annotate_size)
+               cxt->pstore.flags |= PSTORE_FLAGS_ANNOTATE;
 
        err = pstore_register(&cxt->pstore);
        if (err) {
@@ -856,7 +895,10 @@ fail_buf:
        kfree(cxt->pstore.buf);
 fail_clear:
        cxt->pstore.bufsize = 0;
+       cxt->max_dump_cnt = 0;
        persistent_ram_free(cxt->mprz);
+fail_init_aprz:
+       persistent_ram_free(cxt->aprz);
 fail_init_mprz:
 fail_init_fprz:
        persistent_ram_free(cxt->cprz);
index e57f98e8ce41602dcd72f77723ce27210aef0849..bd24f1a69e7883fc9fbebb88d270f4f1fc47c18d 100644 (file)
@@ -283,6 +283,147 @@ static int notrace persistent_ram_update_user(struct persistent_ram_zone *prz,
        return ret;
 }
 
+#ifdef CONFIG_PSTORE_RAM_ANNOTATION_APPEND
+struct praa_buf {
+       struct list_head list;
+       int size, space;
+       char data[];
+};
+
+struct persistent_ram_annotation_append_buffer {
+       spinlock_t lock;        /* protect list and buf */
+       struct list_head list;
+       struct praa_buf *buf;
+       int total_size;
+       int stop;
+} praa_buffer = {
+       .lock   = __SPIN_LOCK_UNLOCKED(praa_buffer.lock),
+       .list   = LIST_HEAD_INIT(praa_buffer.list),
+       .stop = 0,
+       .buf = NULL,
+       .total_size = 0,
+};
+
+int persistent_ram_annotation_append(const char *fmt, ...)
+{
+       va_list args;
+       unsigned long flags;
+       int len = 0;
+       char line_buf[512];
+       struct praa_buf *buf;
+
+       va_start(args, fmt);
+       len += vsnprintf(line_buf + len, sizeof(line_buf) - len, fmt, args);
+       va_end(args);
+
+       spin_lock_irqsave(&praa_buffer.lock, flags);
+       if (praa_buffer.stop) {
+               spin_unlock_irqrestore(&praa_buffer.lock, flags);
+               pr_err("%s() called too late by %pf()\n", __func__,
+                               __builtin_return_address(0));
+               return 0;
+       }
+
+       while (1) {
+               if (!praa_buffer.buf) {
+                       buf = (struct praa_buf *) __get_free_page(GFP_ATOMIC);
+                       if (buf) {
+                               buf->size = 0;
+                               buf->space = PAGE_SIZE - 1 -
+                                       offsetof(struct praa_buf, data);
+                               praa_buffer.buf = buf;
+                       } else {
+                               pr_err("%s NOMEM\n", __func__);
+                               len = 0;
+                               break;
+                       }
+               }
+               buf = praa_buffer.buf;
+               if (len + 1 > buf->space) {
+                       buf->data[buf->size] = '\0';
+                       list_add_tail(&buf->list, &praa_buffer.list);
+                       praa_buffer.total_size += buf->size;
+                       praa_buffer.buf = NULL;
+                       continue;
+               }
+               memcpy(&buf->data[buf->size], line_buf, len);
+               buf->space -= len;
+               buf->size += len;
+               break;
+       }
+       spin_unlock_irqrestore(&praa_buffer.lock, flags);
+       return len;
+}
+
+static int persistent_ram_annotation_append_stop(void)
+{
+       int ret;
+       unsigned long flags;
+       struct praa_buf *buf;
+       spin_lock_irqsave(&praa_buffer.lock, flags);
+       if (praa_buffer.stop) {
+               spin_unlock_irqrestore(&praa_buffer.lock, flags);
+               return 0;
+       }
+       praa_buffer.stop = 1;
+       if (praa_buffer.buf) {
+               buf = praa_buffer.buf;
+               praa_buffer.buf = NULL;
+               buf->data[buf->size] = '\0';
+               list_add_tail(&buf->list, &praa_buffer.list);
+               praa_buffer.total_size += buf->size;
+       }
+       ret = praa_buffer.total_size;
+       spin_unlock_irqrestore(&praa_buffer.lock, flags);
+       return ret;
+}
+
+static void persistent_ram_annotation_append_push(char *ptr)
+{
+       unsigned long flags;
+       struct praa_buf *buf, *n;
+
+       spin_lock_irqsave(&praa_buffer.lock, flags);
+       list_for_each_entry_safe(buf, n, &praa_buffer.list, list) {
+               if (ptr) {
+                       memcpy(ptr, buf->data, buf->size);
+                       ptr += buf->size;
+               }
+               list_del(&buf->list);
+               praa_buffer.total_size -= buf->size;
+               free_page((unsigned long)buf);
+       }
+       spin_unlock_irqrestore(&praa_buffer.lock, flags);
+}
+
+void persistent_ram_annotation_merge(struct persistent_ram_zone *prz)
+{
+       size_t ext_size;
+       char *old_log2;
+
+       ext_size = persistent_ram_annotation_append_stop();
+       if (ext_size) {
+               if (!prz) {
+                       persistent_ram_annotation_append_push(NULL);
+                       pr_info("%s: discarded %zu\n", __func__, ext_size);
+                       return;
+               }
+               old_log2 = krealloc(prz->old_log,
+                               prz->old_log_size + ext_size, GFP_KERNEL);
+               if (old_log2) {
+                       persistent_ram_annotation_append_push(old_log2 +
+                               prz->old_log_size);
+                       prz->old_log = old_log2;
+                       prz->old_log_size += ext_size;
+                       pr_info("%s: merged %zu\n", __func__, ext_size);
+               } else {
+                       pr_err("%s: cannot merge %zu\n", __func__, ext_size);
+                       persistent_ram_annotation_append_push(NULL);
+               }
+       }
+}
+#endif
+
 void persistent_ram_save_old(struct persistent_ram_zone *prz)
 {
        struct persistent_ram_buffer *buffer = prz->buffer;
@@ -483,6 +624,8 @@ static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size,
 
        prz->buffer = prz->vaddr;
        prz->buffer_size = size - sizeof(struct persistent_ram_buffer);
+       pr_info("persistent_ram: paddr: %p, vaddr: %p, buf size = 0x%zx\n",
+               (void *)prz->paddr, (void *)prz->vaddr, prz->buffer_size);
 
        return 0;
 }
index 170bb981d2fd4a7fcaf47e01c2ffd0a4e52e5fa9..ab420ef094c17a38bcfeb4d1be5f216563830d5b 100644 (file)
@@ -44,6 +44,7 @@ enum pstore_type_id {
        PSTORE_TYPE_PPC_COMMON  = 6,
        PSTORE_TYPE_PMSG        = 7,
        PSTORE_TYPE_PPC_OPAL    = 8,
+       PSTORE_TYPE_ANNOTATE    = 9,
        PSTORE_TYPE_UNKNOWN     = 255
 };
 
@@ -196,10 +197,12 @@ struct pstore_info {
 #define PSTORE_FLAGS_CONSOLE   (1 << 1)
 #define PSTORE_FLAGS_FTRACE    (1 << 2)
 #define PSTORE_FLAGS_PMSG      (1 << 3)
+#define PSTORE_FLAGS_ANNOTATE  (1 << 4)
 
 extern int pstore_register(struct pstore_info *);
 extern void pstore_unregister(struct pstore_info *);
 extern bool pstore_cannot_block_path(enum kmsg_dump_reason reason);
+extern int pstore_annotate(const char *buf);
 
 struct pstore_ftrace_record {
        unsigned long ip;
index 10fe2e28701faad19811d1d26ef05644571aafc3..a7f0fb331976e9c3d2c39ea6ec06fed1321d6ac1 100644 (file)
@@ -79,6 +79,15 @@ void *persistent_ram_old(struct persistent_ram_zone *prz);
 void persistent_ram_free_old(struct persistent_ram_zone *prz);
 ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
        char *str, size_t len);
+#ifdef CONFIG_PSTORE_RAM_ANNOTATION_APPEND
+__printf(1, 2)  int persistent_ram_annotation_append(const char *fmt, ...);
+void persistent_ram_annotation_merge(struct persistent_ram_zone *prz);
+#else
+static inline __printf(1, 2) void persistent_ram_annotation_append(
+                       const char *fmt, ...) { };
+static inline void persistent_ram_annotation_merge(
+                       struct persistent_ram_zone *prz) { };
+#endif
 
 void ramoops_console_write_buf(const char *buf, size_t size);
 
@@ -98,6 +107,7 @@ struct ramoops_platform_data {
        unsigned long   console_size;
        unsigned long   ftrace_size;
        unsigned long   pmsg_size;
+       unsigned long   annotate_size;
        int             dump_oops;
        u32             flags;
        struct persistent_ram_ecc_info ecc_info;
index ce8c361b1c3b147e27190b8170e53580a12f5969..460f740484e2d5dffdee8875ea74cd54815fda53 100644 (file)
@@ -220,6 +220,7 @@ static struct ramoops_platform_data dss_ramoops_data = {
        .console_size   = SZ_512K,
        .ftrace_size    = SZ_512K,
        .pmsg_size      = SZ_512K,
+        .annotate_size  = SZ_2K,
 #endif
        .dump_oops      = 1,
 };