From: zhaoxp3 Date: Thu, 2 Aug 2018 06:51:07 +0000 (+0800) Subject: fs:pstore:ramoops: Add annotation support to pstore X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=248fca44b1d24d50074c8fa2bd362a6d69742be7;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git fs:pstore:ramoops: Add annotation support to pstore 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 Reviewed-on: https://gerrit.mot.com/1220328 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Submit-Approved: Jira Key --- diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig index 144e5027fe84..ed1cf942c286 100644 --- a/fs/pstore/Kconfig +++ b/fs/pstore/Kconfig @@ -88,3 +88,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. diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index d814723fb27d..0fff750e9bb6 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c @@ -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); diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 6a1176c1be06..95ca101bfaf2 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -431,6 +431,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) { @@ -440,6 +441,7 @@ static void allocate_buf_for_compression(void) pr_err("allocate compression buffer error!\n"); } } +#endif static void free_buf_for_compression(void) { @@ -595,6 +597,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) { @@ -705,8 +754,10 @@ int pstore_register(struct pstore_info *psi) return -EINVAL; } +#if 0 if (psi->flags & PSTORE_FLAGS_DMESG) allocate_buf_for_compression(); +#endif if (pstore_is_mounted()) pstore_get_records(0); diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index c476d88e435d..782f8dd9e8e4 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -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. */ @@ -492,6 +509,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; } @@ -712,6 +732,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 @@ -742,7 +763,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. @@ -760,7 +781,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"); err = -EINVAL; @@ -775,6 +797,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; @@ -783,6 +812,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; @@ -790,7 +820,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); @@ -818,6 +848,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; /* * Prepare frontend flags based on which areas are initialized. @@ -834,6 +871,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; /* * Since bufsize is only used for dmesg crash dumps, it @@ -878,7 +917,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); diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c index e05272b4b523..8e5b754874b4 100644 --- a/fs/pstore/ram_core.c +++ b/fs/pstore/ram_core.c @@ -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; @@ -487,6 +628,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; } diff --git a/include/linux/pstore.h b/include/linux/pstore.h index 70913ec87785..83266c25393e 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h @@ -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,9 +197,11 @@ 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 int pstore_annotate(const char *buf); struct pstore_ftrace_record { unsigned long ip; diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h index 10fe2e28701f..a7f0fb331976 100644 --- a/include/linux/pstore_ram.h +++ b/include/linux/pstore_ram.h @@ -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; diff --git a/lib/debug-snapshot-pstore.c b/lib/debug-snapshot-pstore.c index ce8c361b1c3b..460f740484e2 100644 --- a/lib/debug-snapshot-pstore.c +++ b/lib/debug-snapshot-pstore.c @@ -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, };