From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932189AbdAaB7P (ORCPT ); Mon, 30 Jan 2017 20:59:15 -0500 Received: from mail9.hitachi.co.jp ([133.145.228.44]:54019 "EHLO mail9.hitachi.co.jp" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753822AbdAaB7N (ORCPT ); Mon, 30 Jan 2017 20:59:13 -0500 From: Nobuhiro Iwamatsu To: Anton Vorontsov , Colin Cross , Kees Cook , Tony Luck Cc: linux-kernel@vger.kernel.org, Nobuhiro Iwamatsu , Hiraku Toyooka , Jonathan Corbet , Mark Salyzyn , Seiji Aguchi Subject: [PATCH v4 2nd 3/4] ramoops: support multiple pmsg instances Date: Tue, 31 Jan 2017 10:58:34 +0900 Message-Id: <1485827915-9620-4-git-send-email-nobuhiro.iwamatsu.kw@hitachi.com> X-Mailer: git-send-email 2.8.1.windows.1 In-Reply-To: <1485827915-9620-1-git-send-email-nobuhiro.iwamatsu.kw@hitachi.com> References: <1485827915-9620-1-git-send-email-nobuhiro.iwamatsu.kw@hitachi.com> X-TM-AS-GCONF: 00 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This enables ramoops to deal with multiple pmsg instances. A User 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 Signed-off-by: Nobuhiro Iwamatsu Cc: Jonathan Corbet Cc: Anton Vorontsov Cc: Colin Cross Cc: Kees Cook Cc: Mark Salyzyn Cc: Seiji Aguchi Cc: Tony Luck V4: - Rebase. V3: - Rebase. - Update documentiation of DT binding. - Update parsing function of ramoops_pmsg_size, add NULL-terminated. - Update module parameters for pmsg_size list. --- Documentation/admin-guide/ramoops.rst | 22 ++ .../bindings/reserved-memory/ramoops.txt | 6 +- fs/pstore/ram.c | 223 ++++++++++++++++++--- include/linux/pstore_ram.h | 8 +- 4 files changed, 231 insertions(+), 28 deletions(-) diff --git a/Documentation/admin-guide/ramoops.rst b/Documentation/admin-guide/ramoops.rst index 4efd7ce77565..611ee1d7d9ba 100644 --- a/Documentation/admin-guide/ramoops.rst +++ b/Documentation/admin-guide/ramoops.rst @@ -154,3 +154,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/Documentation/devicetree/bindings/reserved-memory/ramoops.txt b/Documentation/devicetree/bindings/reserved-memory/ramoops.txt index 0eba562fe5c6..e270221b98df 100644 --- a/Documentation/devicetree/bindings/reserved-memory/ramoops.txt +++ b/Documentation/devicetree/bindings/reserved-memory/ramoops.txt @@ -39,7 +39,11 @@ Optional properties: - ftrace-size: size in bytes of log buffer reserved for function tracing and profiling (defaults to 0: disabled) -- pmsg-size: size in bytes of log buffer reserved for userspace messages +- pmsg-size: array of value that are used to size in bytes of log buffer + reserved for userspace messages. + + pmsg-size = <0x10000 0x10000>; + (defaults to 0: disabled) - unbuffered: if present, use unbuffered mappings to map the reserved region diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index 72072e8123e3..50bc78aff6b8 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -53,8 +53,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 unsigned long long mem_address; @@ -88,14 +88,14 @@ struct ramoops_context { struct persistent_ram_zone **dprzs; /* Oops dump zones */ 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 **mprzs; /* PMSG zone */ 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; u32 flags; struct persistent_ram_ecc_info ecc_info; @@ -275,9 +275,11 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, prz = ramoops_get_next_prz(&cxt->cprz, &cxt->console_read_cnt, 1, id, type, PSTORE_TYPE_CONSOLE, 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->pstore.num_pmsg && !prz_ok(prz)) + /* get pmsg prz */ + prz = ramoops_get_next_prz(cxt->mprzs, &cxt->pmsg_read_cnt, + cxt->pstore.num_pmsg, id, type, + PSTORE_TYPE_PMSG, 0); /* ftrace is last since it may want to dynamically allocate memory. */ if (!prz_ok(prz)) { @@ -455,9 +457,9 @@ static int notrace ramoops_pstore_write_buf_user(enum pstore_type_id type, if (type == PSTORE_TYPE_PMSG) { struct ramoops_context *cxt = psi->data; - if (!cxt->mprz) + if (!cxt->mprzs) return -ENOMEM; - return persistent_ram_write_user(cxt->mprz, buf, size); + return persistent_ram_write_user(cxt->mprzs[part], buf, size); } return -EINVAL; @@ -484,7 +486,9 @@ static int ramoops_pstore_erase(enum pstore_type_id type, u64 id, int count, prz = cxt->fprzs[id]; break; case PSTORE_TYPE_PMSG: - prz = cxt->mprz; + if (id >= cxt->pstore.num_pmsg) + return -EINVAL; + prz = cxt->mprzs[id]; break; default: return -EINVAL; @@ -634,6 +638,40 @@ static int ramoops_init_przs(const char *name, return err; } +/* int function for pmsg przs */ +static int ramoops_init_mprzs(const char *name, + 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->pstore.num_pmsg, sizeof(*cxt->mprzs), + GFP_KERNEL); + if (!cxt->mprzs) + return -ENOMEM; + + for (i = 0; i < cxt->pstore.num_pmsg; i++) { + err = __ramoops_init_prz(name, dev, cxt, &cxt->mprzs[i], paddr, + cxt->pmsg_size[i], 0, 0, true); + if (err) + goto fail_mprz; + } + + return 0; +fail_mprz: + ramoops_free_przs(cxt->mprzs); + return err; +} + static int ramoops_init_prz(const char *name, struct device *dev, struct ramoops_context *cxt, struct persistent_ram_zone **prz, @@ -670,7 +708,8 @@ static int ramoops_parse_dt(struct platform_device *pdev, struct device_node *of_node = pdev->dev.of_node; struct resource *res; u32 value; - int ret; + int ret, i; + unsigned int num_pmsg; dev_dbg(&pdev->dev, "using Device Tree\n"); @@ -696,12 +735,71 @@ static int ramoops_parse_dt(struct platform_device *pdev, parse_size("record-size", pdata->record_size); parse_size("console-size", pdata->console_size); parse_size("ftrace-size", pdata->ftrace_size); - parse_size("pmsg-size", pdata->pmsg_size); parse_size("ecc-size", pdata->ecc_info.ecc_size); parse_size("flags", pdata->flags); #undef parse_size + /* Parse pmesg size */ + if (!of_find_property(of_node, "pmsg-size", &num_pmsg)) + return 0; /* no data */ + + num_pmsg = num_pmsg / sizeof(u32); + + pdata->pmsg_size = + devm_kzalloc(&pdev->dev, sizeof(size_t) * (num_pmsg + 1), + GFP_KERNEL); + if (!pdata->pmsg_size) + return -ENOMEM; + + for (i = 0; i < num_pmsg; i++) { + ret = of_property_read_u32_index(of_node, "pmsg-size", + i, &value); + if (ret) { + dev_warn(&pdev->dev, + "%s: failed to read pmsg-size[%d]: %d\n", + __func__, i, ret); + return -EINVAL; + } + + /* CHECK INT_MAX */ + if (value > INT_MAX) { + dev_err(&pdev->dev, "value %x > INT_MAX\n", value); + return -EOVERFLOW; + } + pdata->pmsg_size[i] = value; + } + + return 0; +} + +#define ADDR_STRING_SIZE 10 /* "0x00000000" */ +static int update_pmsg_size_mod_param(size_t *pmsg_size, int num) +{ + int i, len; + + /* string size + commas count + NULL-terminated */ + len = ADDR_STRING_SIZE * num + (num - 1) + 1; + + /* using commandline or already allocation buffer.*/ + if (ramoops_pmsg_size_str) + goto out; + + ramoops_pmsg_size_str = kzalloc(len, GFP_KERNEL); + if (!ramoops_pmsg_size_str) + return -ENOMEM; + + for (i = 0 ; i < num; i++) { + char str[ADDR_STRING_SIZE + 1]; + + memset(str, 0, sizeof(str)); + if (i == num - 1) + snprintf(str, sizeof(str), "0x%x", pmsg_size[i]); + else + snprintf(str, sizeof(str), "0x%x,", pmsg_size[i]); + strcat(ramoops_pmsg_size_str, str); + } +out: return 0; } @@ -713,6 +811,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; if (dev_of_node(dev) && !pdata) { pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); @@ -755,8 +855,20 @@ 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; @@ -764,20 +876,29 @@ 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->flags = pdata->flags; cxt->ecc_info = pdata->ecc_info; + cxt->pstore.num_pmsg = num_pmsg; - paddr = cxt->phys_addr; + 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_przs("dump", dev, cxt, &cxt->dprzs, &paddr, dump_mem_sz, cxt->record_size, &cxt->max_dump_cnt, 0, 0); if (err) - goto fail_out; + goto fail_init_dprzs; err = ramoops_init_prz("console", dev, cxt, &cxt->cprz, &paddr, cxt->console_size, 0); @@ -795,10 +916,9 @@ static int ramoops_probe(struct platform_device *pdev) if (err) goto fail_init_fprz; - err = ramoops_init_prz("pmsg", dev, cxt, &cxt->mprz, &paddr, - cxt->pmsg_size, 0); + err = ramoops_init_mprzs("pmsg", dev, cxt, &paddr, pmsg_size_total); if (err) - goto fail_init_mprz; + goto fail_init_mprzs; cxt->pstore.data = cxt; /* @@ -841,8 +961,8 @@ 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; + update_pmsg_size_mod_param(cxt->pmsg_size, cxt->pstore.num_pmsg); pr_info("attached 0x%lx@0x%llx, ecc: %d/%d\n", cxt->size, (unsigned long long)cxt->phys_addr, @@ -854,8 +974,8 @@ static int ramoops_probe(struct platform_device *pdev) 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: cxt->max_ftrace_cnt = 0; ramoops_free_przs(cxt->fprzs); fail_init_fprz: @@ -863,6 +983,8 @@ static int ramoops_probe(struct platform_device *pdev) fail_init_cprz: cxt->max_dump_cnt = 0; ramoops_free_przs(cxt->dprzs); +fail_init_dprzs: + kfree(cxt->pmsg_size); fail_out: return err; } @@ -875,8 +997,10 @@ static int ramoops_remove(struct platform_device *pdev) kfree(cxt->pstore.buf); cxt->pstore.bufsize = 0; + kfree(cxt->pmsg_size); - persistent_ram_free(cxt->mprz); + ramoops_free_przs(cxt->mprzs); + cxt->pstore.num_pmsg = 0; persistent_ram_free(cxt->cprz); /* Free ftrace PRZs */ @@ -903,6 +1027,43 @@ 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++; + } + + /* Add NULL-terminated */ + count++; + + size_array = kcalloc(count, 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) @@ -922,7 +1083,10 @@ 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; dummy_data->flags = RAMOOPS_FLAG_FTRACE_PER_CPU; @@ -937,7 +1101,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) @@ -951,6 +1121,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 9395f06e8372..030fa45af2bf 100644 --- a/include/linux/pstore_ram.h +++ b/include/linux/pstore_ram.h @@ -84,6 +84,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. */ #define RAMOOPS_FLAG_FTRACE_PER_CPU BIT(0) @@ -95,7 +101,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; u32 flags; struct persistent_ram_ecc_info ecc_info; -- 2.11.0