lkml.org 
[lkml]   [2015]   [Nov]   [4]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Subject[PATCH 4/5] ramoops: support multiple pmsg instances
From
Date
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@hitachi.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Anton Vorontsov <anton@enomsg.org>
Cc: Colin Cross <ccross@android.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Mark Salyzyn <salyzyn@android.com>
Cc: Seiji Aguchi <seiji.aguchi.tr@hitachi.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: linux-doc@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
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;
};


\
 
 \ /
  Last update: 2015-11-05 04:01    [W:0.047 / U:0.068 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site