This patch enables ramoops to deal with multiple pmsg instances. We can configure the size of each buffers by its module parameter as follows. pmsg_size=0x1000,0x2000,... Then, the ramoops allocates multiple buffers and tells the number of buffers to pstore to create multiple /dev/pmsg[ID]. Signed-off-by: Hiraku Toyooka <hiraku.toyooka.gu@xxxxxxxxxxx> Cc: Jonathan Corbet <corbet@xxxxxxx> Cc: Anton Vorontsov <anton@xxxxxxxxxx> Cc: Colin Cross <ccross@xxxxxxxxxxx> Cc: Kees Cook <keescook@xxxxxxxxxxxx> Cc: Mark Salyzyn <salyzyn@xxxxxxxxxxx> Cc: Seiji Aguchi <seiji.aguchi.tr@xxxxxxxxxxx> Cc: Tony Luck <tony.luck@xxxxxxxxx> Cc: linux-doc@xxxxxxxxxxxxxxx Cc: linux-kernel@xxxxxxxxxxxxxxx --- Documentation/ramoops.txt | 22 +++++++ fs/pstore/ram.c | 146 ++++++++++++++++++++++++++++++++++++++------ include/linux/pstore_ram.h | 8 ++ 3 files changed, 154 insertions(+), 22 deletions(-) diff --git a/Documentation/ramoops.txt b/Documentation/ramoops.txt index 5d86756..cff6ac7 100644 --- a/Documentation/ramoops.txt +++ b/Documentation/ramoops.txt @@ -126,3 +126,25 @@ file. Here is an example of usage: 0 ffffffff811d9c54 ffffffff8101a7a0 __const_udelay <- native_machine_emergency_restart+0x110/0x1e0 0 ffffffff811d9c34 ffffffff811d9c80 __delay <- __const_udelay+0x30/0x40 0 ffffffff811d9d14 ffffffff811d9c3f delay_tsc <- __delay+0xf/0x20 + +6. Pmsg support + +Ramoops supports pmsg - logging userspace mesages in persistent store. By +default, one pmsg area becomes available in ramoops. You can write data into +/dev/pmsg0, e.g.: + + # echo foo > /dev/pmsg0 + +After reboot, the stored data can be read from pmsg-ramoops-0 on the pstore +filesystem. + +You can specify size of the pmsg area by additional kernel command line, e.g.: + + "ramoops.pmsg_size=0x1000" + +You can also use multiple pmsg areas, e.g.: + + "ramoops.pmsg_size=0x1000,0x2000,..." + +Then, pmsg0, pmsg1, ... will appear on /dev. This is useful to control +individual content aging or priority. diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index 89cc90e..1d1378c 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -51,8 +51,8 @@ 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); +static char *ramoops_pmsg_size_str; +module_param_named(pmsg_size, ramoops_pmsg_size_str, charp, 0400); MODULE_PARM_DESC(pmsg_size, "size of user space message log"); static ulong mem_address; @@ -86,14 +86,14 @@ struct ramoops_context { struct persistent_ram_zone **dprzs; struct persistent_ram_zone *cprz; struct persistent_ram_zone *fprz; - struct persistent_ram_zone *mprz; + struct persistent_ram_zone **mprzs; 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; + size_t *pmsg_size; int dump_oops; struct persistent_ram_ecc_info ecc_info; unsigned int max_dump_cnt; @@ -103,6 +103,7 @@ struct ramoops_context { unsigned int console_read_cnt; unsigned int ftrace_read_cnt; unsigned int pmsg_read_cnt; + unsigned int num_pmsg; struct pstore_info pstore; }; @@ -220,9 +221,10 @@ 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); + while (cxt->pmsg_read_cnt < cxt->num_pmsg && !prz) + prz = ramoops_get_next_prz(cxt->mprzs, &cxt->pmsg_read_cnt, + cxt->num_pmsg, id, type, + PSTORE_TYPE_PMSG, 0); if (!prz_ok(prz)) return 0; @@ -286,9 +288,11 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type, persistent_ram_write(cxt->fprz, buf, size); return 0; } else if (type == PSTORE_TYPE_PMSG) { - if (!cxt->mprz) + if (part >= cxt->num_pmsg) + return -EINVAL; + if (!cxt->mprzs || !cxt->mprzs[part]) return -ENOMEM; - persistent_ram_write(cxt->mprz, buf, size); + persistent_ram_write(cxt->mprzs[part], buf, size); return 0; } @@ -348,7 +352,9 @@ static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, int count, prz = cxt->fprz; break; case PSTORE_TYPE_PMSG: - prz = cxt->mprz; + if (id >= cxt->num_pmsg) + return -EINVAL; + prz = cxt->mprzs[id]; break; default: return -EINVAL; @@ -426,6 +432,39 @@ fail_prz: return err; } +static int ramoops_init_mprzs(struct device *dev, struct ramoops_context *cxt, + phys_addr_t *paddr, unsigned long total) +{ + int err = -ENOMEM; + int i; + + if (!total) + return 0; + + if (*paddr + total - cxt->phys_addr > cxt->size) { + dev_err(dev, "no room for pmsg\n"); + return -ENOMEM; + } + + cxt->mprzs = kcalloc(cxt->num_pmsg, sizeof(*cxt->mprzs), GFP_KERNEL); + if (!cxt->mprzs) { + dev_err(dev, "failed to initialize a mprz array for pmsg\n"); + goto fail_prz; + } + + for (i = 0; i < cxt->num_pmsg; i++) { + err = __ramoops_init_prz(dev, cxt, &cxt->mprzs[i], paddr, + cxt->pmsg_size[i], 0, true); + if (err) + goto fail_prz; + } + + return 0; +fail_prz: + ramoops_free_przs(cxt->mprzs); + return err; +} + static int ramoops_init_prz(struct device *dev, struct ramoops_context *cxt, struct persistent_ram_zone **prz, phys_addr_t *paddr, size_t sz, u32 sig) @@ -472,6 +511,8 @@ static int ramoops_probe(struct platform_device *pdev) size_t dump_mem_sz; phys_addr_t paddr; int err = -EINVAL; + unsigned long pmsg_size_total = 0; + unsigned int num_pmsg = 0; /* Only a single ramoops area allowed at a time, so fail extra * probes. @@ -492,8 +533,18 @@ 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); + if (pdata->pmsg_size) { + for (;; num_pmsg++) { + unsigned long size = pdata->pmsg_size[num_pmsg]; + + if (!size) + break; + if (!is_power_of_2(size)) + pdata->pmsg_size[num_pmsg] + = rounddown_pow_of_two(size); + pmsg_size_total += size; + } + } cxt->size = pdata->mem_size; cxt->phys_addr = pdata->mem_address; @@ -501,17 +552,26 @@ 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; + cxt->num_pmsg = num_pmsg; + if (num_pmsg) { + cxt->pmsg_size = kcalloc(num_pmsg, sizeof(size_t), GFP_KERNEL); + if (!cxt->pmsg_size) { + err = -ENOMEM; + goto fail_out; + } + memcpy(cxt->pmsg_size, pdata->pmsg_size, + sizeof(size_t) * num_pmsg); + } paddr = cxt->phys_addr; dump_mem_sz = cxt->size - cxt->console_size - cxt->ftrace_size - - cxt->pmsg_size; + - pmsg_size_total; err = ramoops_init_dprzs(dev, cxt, &paddr, dump_mem_sz); if (err) - goto fail_out; + goto fail_init_dprzs; err = ramoops_init_prz(dev, cxt, &cxt->cprz, &paddr, cxt->console_size, 0); @@ -523,11 +583,12 @@ 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); + err = ramoops_init_mprzs(dev, cxt, &paddr, pmsg_size_total); if (err) - goto fail_init_mprz; + goto fail_init_mprzs; cxt->pstore.data = cxt; + cxt->pstore.num_pmsg = num_pmsg; /* * Console can handle any buffer size, so prefer LOG_LINE_MAX. If we * have to handle dumps, we must have at least record_size buffer. And @@ -560,7 +621,6 @@ static int ramoops_probe(struct platform_device *pdev) record_size = pdata->record_size; dump_oops = pdata->dump_oops; ramoops_console_size = pdata->console_size; - ramoops_pmsg_size = pdata->pmsg_size; ramoops_ftrace_size = pdata->ftrace_size; pr_info("attached 0x%lx@0x%llx, ecc: %d/%d\n", @@ -573,14 +633,16 @@ fail_buf: kfree(cxt->pstore.buf); fail_clear: cxt->pstore.bufsize = 0; - persistent_ram_free(cxt->mprz); -fail_init_mprz: + ramoops_free_przs(cxt->mprzs); +fail_init_mprzs: persistent_ram_free(cxt->fprz); fail_init_fprz: persistent_ram_free(cxt->cprz); fail_init_cprz: cxt->max_dump_cnt = 0; ramoops_free_przs(cxt->dprzs); +fail_init_dprzs: + kfree(cxt->pmsg_size); fail_out: return err; } @@ -600,6 +662,7 @@ static int __exit ramoops_remove(struct platform_device *pdev) /* TODO(kees): When pstore supports unregistering, call it here. */ kfree(cxt->pstore.buf); cxt->pstore.bufsize = 0; + kfree(cxt->pmsg_size); return 0; #endif @@ -614,6 +677,38 @@ static struct platform_driver ramoops_driver = { }, }; +static unsigned long *parse_size_str(char *size_str) +{ + int i, ret; + unsigned long *size_array, count = 1; + + if (size_str) { + char *s = size_str; + /* Necessary array size is the number of commas + 1 */ + for (; (s = strchr(s, ',')); s++) + count++; + } + + size_array = kcalloc(count + 1, sizeof(unsigned long), GFP_KERNEL); + if (!size_array) + goto out; + + if (size_str) { + for (i = 0; i < count; i++) { + ret = get_option(&size_str, (int *)&size_array[i]); + if (ret == 1) + break; + else if (ret != 2) { + size_array[i] = 0; + break; + } + } + } else + size_array[0] = MIN_MEM_SIZE; +out: + return size_array; +} + static void ramoops_register_dummy(void) { if (!mem_size) @@ -633,7 +728,9 @@ 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->pmsg_size = parse_size_str(ramoops_pmsg_size_str); + if (!dummy_data->pmsg_size) + goto fail_pmsg_size; dummy_data->dump_oops = dump_oops; /* * For backwards compatibility ramoops.ecc=1 means 16 bytes ECC @@ -646,7 +743,13 @@ static void ramoops_register_dummy(void) if (IS_ERR(dummy)) { pr_info("could not create platform device: %ld\n", PTR_ERR(dummy)); + goto fail_pdev; } + return; +fail_pdev: + kfree(dummy_data->pmsg_size); +fail_pmsg_size: + kfree(dummy_data); } static int __init ramoops_init(void) @@ -660,6 +763,7 @@ static void __exit ramoops_exit(void) { platform_driver_unregister(&ramoops_driver); platform_device_unregister(dummy); + kfree(dummy_data->pmsg_size); kfree(dummy_data); } module_exit(ramoops_exit); diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h index 9c9d6c1..e808789 100644 --- a/include/linux/pstore_ram.h +++ b/include/linux/pstore_ram.h @@ -72,6 +72,12 @@ ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz, * Ramoops platform data * @mem_size memory size for ramoops * @mem_address physical memory address to contain ramoops + * @pmsg_size array containing size of each pmsg area. 0 value in the array + * indicates the end of the content. For example, if the following + * array is given, + * .pmsg_size = { 0x1000, 0x2000, 0x0, 0x3000, }; + * then, pmsg areas are allocated for the first two size values + * and '0x3000' is just ignored. */ struct ramoops_platform_data { @@ -81,7 +87,7 @@ struct ramoops_platform_data { unsigned long record_size; unsigned long console_size; unsigned long ftrace_size; - unsigned long pmsg_size; + unsigned long *pmsg_size; int dump_oops; struct persistent_ram_ecc_info ecc_info; }; -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html