diff options
author | Tony Lindgren <tony@atomide.com> | 2014-09-16 16:50:01 -0400 |
---|---|---|
committer | Tony Luck <tony.luck@intel.com> | 2014-12-11 16:38:31 -0500 |
commit | 027bc8b08242c59e19356b4b2c189f2d849ab660 (patch) | |
tree | 4bd5ad81fef12692af2c9d278f14903d15ddfb60 | |
parent | 7ae9cb81933515dc7db1aa3c47ef7653717e3090 (diff) |
pstore-ram: Allow optional mapping with pgprot_noncached
On some ARMs the memory can be mapped pgprot_noncached() and still
be working for atomic operations. As pointed out by Colin Cross
<ccross@android.com>, in some cases you do want to use
pgprot_noncached() if the SoC supports it to see a debug printk
just before a write hanging the system.
On ARMs, the atomic operations on strongly ordered memory are
implementation defined. So let's provide an optional kernel parameter
for configuring pgprot_noncached(), and use pgprot_writecombine() by
default.
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Rob Herring <robherring2@gmail.com>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Anton Vorontsov <anton@enomsg.org>
Cc: Colin Cross <ccross@android.com>
Cc: Olof Johansson <olof@lixom.net>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: stable@vger.kernel.org
Acked-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
-rw-r--r-- | Documentation/ramoops.txt | 13 | ||||
-rw-r--r-- | fs/pstore/ram.c | 13 | ||||
-rw-r--r-- | fs/pstore/ram_core.c | 31 | ||||
-rw-r--r-- | include/linux/pstore_ram.h | 4 |
4 files changed, 47 insertions, 14 deletions
diff --git a/Documentation/ramoops.txt b/Documentation/ramoops.txt index 69b3cac4749d..5d8675615e59 100644 --- a/Documentation/ramoops.txt +++ b/Documentation/ramoops.txt | |||
@@ -14,11 +14,19 @@ survive after a restart. | |||
14 | 14 | ||
15 | 1. Ramoops concepts | 15 | 1. Ramoops concepts |
16 | 16 | ||
17 | Ramoops uses a predefined memory area to store the dump. The start and size of | 17 | Ramoops uses a predefined memory area to store the dump. The start and size |
18 | the memory area are set using two variables: | 18 | and type of the memory area are set using three variables: |
19 | * "mem_address" for the start | 19 | * "mem_address" for the start |
20 | * "mem_size" for the size. The memory size will be rounded down to a | 20 | * "mem_size" for the size. The memory size will be rounded down to a |
21 | power of two. | 21 | power of two. |
22 | * "mem_type" to specifiy if the memory type (default is pgprot_writecombine). | ||
23 | |||
24 | Typically the default value of mem_type=0 should be used as that sets the pstore | ||
25 | mapping to pgprot_writecombine. Setting mem_type=1 attempts to use | ||
26 | pgprot_noncached, which only works on some platforms. This is because pstore | ||
27 | depends on atomic operations. At least on ARM, pgprot_noncached causes the | ||
28 | memory to be mapped strongly ordered, and atomic operations on strongly ordered | ||
29 | memory are implementation defined, and won't work on many ARMs such as omaps. | ||
22 | 30 | ||
23 | The memory area is divided into "record_size" chunks (also rounded down to | 31 | The memory area is divided into "record_size" chunks (also rounded down to |
24 | power of two) and each oops/panic writes a "record_size" chunk of | 32 | power of two) and each oops/panic writes a "record_size" chunk of |
@@ -55,6 +63,7 @@ Setting the ramoops parameters can be done in 2 different manners: | |||
55 | static struct ramoops_platform_data ramoops_data = { | 63 | static struct ramoops_platform_data ramoops_data = { |
56 | .mem_size = <...>, | 64 | .mem_size = <...>, |
57 | .mem_address = <...>, | 65 | .mem_address = <...>, |
66 | .mem_type = <...>, | ||
58 | .record_size = <...>, | 67 | .record_size = <...>, |
59 | .dump_oops = <...>, | 68 | .dump_oops = <...>, |
60 | .ecc = <...>, | 69 | .ecc = <...>, |
diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index ec881b312700..2f389ce5023c 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c | |||
@@ -61,6 +61,11 @@ module_param(mem_size, ulong, 0400); | |||
61 | MODULE_PARM_DESC(mem_size, | 61 | MODULE_PARM_DESC(mem_size, |
62 | "size of reserved RAM used to store oops/panic logs"); | 62 | "size of reserved RAM used to store oops/panic logs"); |
63 | 63 | ||
64 | static unsigned int mem_type; | ||
65 | module_param(mem_type, uint, 0600); | ||
66 | MODULE_PARM_DESC(mem_type, | ||
67 | "set to 1 to try to use unbuffered memory (default 0)"); | ||
68 | |||
64 | static int dump_oops = 1; | 69 | static int dump_oops = 1; |
65 | module_param(dump_oops, int, 0600); | 70 | module_param(dump_oops, int, 0600); |
66 | MODULE_PARM_DESC(dump_oops, | 71 | MODULE_PARM_DESC(dump_oops, |
@@ -79,6 +84,7 @@ struct ramoops_context { | |||
79 | struct persistent_ram_zone *fprz; | 84 | struct persistent_ram_zone *fprz; |
80 | phys_addr_t phys_addr; | 85 | phys_addr_t phys_addr; |
81 | unsigned long size; | 86 | unsigned long size; |
87 | unsigned int memtype; | ||
82 | size_t record_size; | 88 | size_t record_size; |
83 | size_t console_size; | 89 | size_t console_size; |
84 | size_t ftrace_size; | 90 | size_t ftrace_size; |
@@ -366,7 +372,8 @@ static int ramoops_init_przs(struct device *dev, struct ramoops_context *cxt, | |||
366 | size_t sz = cxt->record_size; | 372 | size_t sz = cxt->record_size; |
367 | 373 | ||
368 | cxt->przs[i] = persistent_ram_new(*paddr, sz, 0, | 374 | cxt->przs[i] = persistent_ram_new(*paddr, sz, 0, |
369 | &cxt->ecc_info); | 375 | &cxt->ecc_info, |
376 | cxt->memtype); | ||
370 | if (IS_ERR(cxt->przs[i])) { | 377 | if (IS_ERR(cxt->przs[i])) { |
371 | err = PTR_ERR(cxt->przs[i]); | 378 | err = PTR_ERR(cxt->przs[i]); |
372 | dev_err(dev, "failed to request mem region (0x%zx@0x%llx): %d\n", | 379 | dev_err(dev, "failed to request mem region (0x%zx@0x%llx): %d\n", |
@@ -396,7 +403,7 @@ static int ramoops_init_prz(struct device *dev, struct ramoops_context *cxt, | |||
396 | return -ENOMEM; | 403 | return -ENOMEM; |
397 | } | 404 | } |
398 | 405 | ||
399 | *prz = persistent_ram_new(*paddr, sz, sig, &cxt->ecc_info); | 406 | *prz = persistent_ram_new(*paddr, sz, sig, &cxt->ecc_info, cxt->memtype); |
400 | if (IS_ERR(*prz)) { | 407 | if (IS_ERR(*prz)) { |
401 | int err = PTR_ERR(*prz); | 408 | int err = PTR_ERR(*prz); |
402 | 409 | ||
@@ -443,6 +450,7 @@ static int ramoops_probe(struct platform_device *pdev) | |||
443 | 450 | ||
444 | cxt->size = pdata->mem_size; | 451 | cxt->size = pdata->mem_size; |
445 | cxt->phys_addr = pdata->mem_address; | 452 | cxt->phys_addr = pdata->mem_address; |
453 | cxt->memtype = pdata->mem_type; | ||
446 | cxt->record_size = pdata->record_size; | 454 | cxt->record_size = pdata->record_size; |
447 | cxt->console_size = pdata->console_size; | 455 | cxt->console_size = pdata->console_size; |
448 | cxt->ftrace_size = pdata->ftrace_size; | 456 | cxt->ftrace_size = pdata->ftrace_size; |
@@ -572,6 +580,7 @@ static void ramoops_register_dummy(void) | |||
572 | 580 | ||
573 | dummy_data->mem_size = mem_size; | 581 | dummy_data->mem_size = mem_size; |
574 | dummy_data->mem_address = mem_address; | 582 | dummy_data->mem_address = mem_address; |
583 | dummy_data->mem_type = 0; | ||
575 | dummy_data->record_size = record_size; | 584 | dummy_data->record_size = record_size; |
576 | dummy_data->console_size = ramoops_console_size; | 585 | dummy_data->console_size = ramoops_console_size; |
577 | dummy_data->ftrace_size = ramoops_ftrace_size; | 586 | dummy_data->ftrace_size = ramoops_ftrace_size; |
diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c index 24f94b0f2270..76c3f80efdfa 100644 --- a/fs/pstore/ram_core.c +++ b/fs/pstore/ram_core.c | |||
@@ -380,7 +380,8 @@ void persistent_ram_zap(struct persistent_ram_zone *prz) | |||
380 | persistent_ram_update_header_ecc(prz); | 380 | persistent_ram_update_header_ecc(prz); |
381 | } | 381 | } |
382 | 382 | ||
383 | static void *persistent_ram_vmap(phys_addr_t start, size_t size) | 383 | static void *persistent_ram_vmap(phys_addr_t start, size_t size, |
384 | unsigned int memtype) | ||
384 | { | 385 | { |
385 | struct page **pages; | 386 | struct page **pages; |
386 | phys_addr_t page_start; | 387 | phys_addr_t page_start; |
@@ -392,7 +393,10 @@ static void *persistent_ram_vmap(phys_addr_t start, size_t size) | |||
392 | page_start = start - offset_in_page(start); | 393 | page_start = start - offset_in_page(start); |
393 | page_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE); | 394 | page_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE); |
394 | 395 | ||
395 | prot = pgprot_writecombine(PAGE_KERNEL); | 396 | if (memtype) |
397 | prot = pgprot_noncached(PAGE_KERNEL); | ||
398 | else | ||
399 | prot = pgprot_writecombine(PAGE_KERNEL); | ||
396 | 400 | ||
397 | pages = kmalloc_array(page_count, sizeof(struct page *), GFP_KERNEL); | 401 | pages = kmalloc_array(page_count, sizeof(struct page *), GFP_KERNEL); |
398 | if (!pages) { | 402 | if (!pages) { |
@@ -411,8 +415,11 @@ static void *persistent_ram_vmap(phys_addr_t start, size_t size) | |||
411 | return vaddr; | 415 | return vaddr; |
412 | } | 416 | } |
413 | 417 | ||
414 | static void *persistent_ram_iomap(phys_addr_t start, size_t size) | 418 | static void *persistent_ram_iomap(phys_addr_t start, size_t size, |
419 | unsigned int memtype) | ||
415 | { | 420 | { |
421 | void *va; | ||
422 | |||
416 | if (!request_mem_region(start, size, "persistent_ram")) { | 423 | if (!request_mem_region(start, size, "persistent_ram")) { |
417 | pr_err("request mem region (0x%llx@0x%llx) failed\n", | 424 | pr_err("request mem region (0x%llx@0x%llx) failed\n", |
418 | (unsigned long long)size, (unsigned long long)start); | 425 | (unsigned long long)size, (unsigned long long)start); |
@@ -422,19 +429,24 @@ static void *persistent_ram_iomap(phys_addr_t start, size_t size) | |||
422 | buffer_start_add = buffer_start_add_locked; | 429 | buffer_start_add = buffer_start_add_locked; |
423 | buffer_size_add = buffer_size_add_locked; | 430 | buffer_size_add = buffer_size_add_locked; |
424 | 431 | ||
425 | return ioremap_wc(start, size); | 432 | if (memtype) |
433 | va = ioremap(start, size); | ||
434 | else | ||
435 | va = ioremap_wc(start, size); | ||
436 | |||
437 | return va; | ||
426 | } | 438 | } |
427 | 439 | ||
428 | static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size, | 440 | static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size, |
429 | struct persistent_ram_zone *prz) | 441 | struct persistent_ram_zone *prz, int memtype) |
430 | { | 442 | { |
431 | prz->paddr = start; | 443 | prz->paddr = start; |
432 | prz->size = size; | 444 | prz->size = size; |
433 | 445 | ||
434 | if (pfn_valid(start >> PAGE_SHIFT)) | 446 | if (pfn_valid(start >> PAGE_SHIFT)) |
435 | prz->vaddr = persistent_ram_vmap(start, size); | 447 | prz->vaddr = persistent_ram_vmap(start, size, memtype); |
436 | else | 448 | else |
437 | prz->vaddr = persistent_ram_iomap(start, size); | 449 | prz->vaddr = persistent_ram_iomap(start, size, memtype); |
438 | 450 | ||
439 | if (!prz->vaddr) { | 451 | if (!prz->vaddr) { |
440 | pr_err("%s: Failed to map 0x%llx pages at 0x%llx\n", __func__, | 452 | pr_err("%s: Failed to map 0x%llx pages at 0x%llx\n", __func__, |
@@ -500,7 +512,8 @@ void persistent_ram_free(struct persistent_ram_zone *prz) | |||
500 | } | 512 | } |
501 | 513 | ||
502 | struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size, | 514 | struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size, |
503 | u32 sig, struct persistent_ram_ecc_info *ecc_info) | 515 | u32 sig, struct persistent_ram_ecc_info *ecc_info, |
516 | unsigned int memtype) | ||
504 | { | 517 | { |
505 | struct persistent_ram_zone *prz; | 518 | struct persistent_ram_zone *prz; |
506 | int ret = -ENOMEM; | 519 | int ret = -ENOMEM; |
@@ -511,7 +524,7 @@ struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size, | |||
511 | goto err; | 524 | goto err; |
512 | } | 525 | } |
513 | 526 | ||
514 | ret = persistent_ram_buffer_map(start, size, prz); | 527 | ret = persistent_ram_buffer_map(start, size, prz, memtype); |
515 | if (ret) | 528 | if (ret) |
516 | goto err; | 529 | goto err; |
517 | 530 | ||
diff --git a/include/linux/pstore_ram.h b/include/linux/pstore_ram.h index 9974975d40db..4af3fdc85b01 100644 --- a/include/linux/pstore_ram.h +++ b/include/linux/pstore_ram.h | |||
@@ -53,7 +53,8 @@ struct persistent_ram_zone { | |||
53 | }; | 53 | }; |
54 | 54 | ||
55 | struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size, | 55 | struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size, |
56 | u32 sig, struct persistent_ram_ecc_info *ecc_info); | 56 | u32 sig, struct persistent_ram_ecc_info *ecc_info, |
57 | unsigned int memtype); | ||
57 | void persistent_ram_free(struct persistent_ram_zone *prz); | 58 | void persistent_ram_free(struct persistent_ram_zone *prz); |
58 | void persistent_ram_zap(struct persistent_ram_zone *prz); | 59 | void persistent_ram_zap(struct persistent_ram_zone *prz); |
59 | 60 | ||
@@ -76,6 +77,7 @@ ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz, | |||
76 | struct ramoops_platform_data { | 77 | struct ramoops_platform_data { |
77 | unsigned long mem_size; | 78 | unsigned long mem_size; |
78 | unsigned long mem_address; | 79 | unsigned long mem_address; |
80 | unsigned int mem_type; | ||
79 | unsigned long record_size; | 81 | unsigned long record_size; |
80 | unsigned long console_size; | 82 | unsigned long console_size; |
81 | unsigned long ftrace_size; | 83 | unsigned long ftrace_size; |