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>
This commit is contained in:
parent
7ae9cb8193
commit
027bc8b082
4 changed files with 47 additions and 14 deletions
|
@ -14,11 +14,19 @@ survive after a restart.
|
|||
|
||||
1. Ramoops concepts
|
||||
|
||||
Ramoops uses a predefined memory area to store the dump. The start and size of
|
||||
the memory area are set using two variables:
|
||||
Ramoops uses a predefined memory area to store the dump. The start and size
|
||||
and type of the memory area are set using three variables:
|
||||
* "mem_address" for the start
|
||||
* "mem_size" for the size. The memory size will be rounded down to a
|
||||
power of two.
|
||||
* "mem_type" to specifiy if the memory type (default is pgprot_writecombine).
|
||||
|
||||
Typically the default value of mem_type=0 should be used as that sets the pstore
|
||||
mapping to pgprot_writecombine. Setting mem_type=1 attempts to use
|
||||
pgprot_noncached, which only works on some platforms. This is because pstore
|
||||
depends on atomic operations. At least on ARM, pgprot_noncached causes the
|
||||
memory to be mapped strongly ordered, and atomic operations on strongly ordered
|
||||
memory are implementation defined, and won't work on many ARMs such as omaps.
|
||||
|
||||
The memory area is divided into "record_size" chunks (also rounded down to
|
||||
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:
|
|||
static struct ramoops_platform_data ramoops_data = {
|
||||
.mem_size = <...>,
|
||||
.mem_address = <...>,
|
||||
.mem_type = <...>,
|
||||
.record_size = <...>,
|
||||
.dump_oops = <...>,
|
||||
.ecc = <...>,
|
||||
|
|
|
@ -61,6 +61,11 @@ module_param(mem_size, ulong, 0400);
|
|||
MODULE_PARM_DESC(mem_size,
|
||||
"size of reserved RAM used to store oops/panic logs");
|
||||
|
||||
static unsigned int mem_type;
|
||||
module_param(mem_type, uint, 0600);
|
||||
MODULE_PARM_DESC(mem_type,
|
||||
"set to 1 to try to use unbuffered memory (default 0)");
|
||||
|
||||
static int dump_oops = 1;
|
||||
module_param(dump_oops, int, 0600);
|
||||
MODULE_PARM_DESC(dump_oops,
|
||||
|
@ -79,6 +84,7 @@ struct ramoops_context {
|
|||
struct persistent_ram_zone *fprz;
|
||||
phys_addr_t phys_addr;
|
||||
unsigned long size;
|
||||
unsigned int memtype;
|
||||
size_t record_size;
|
||||
size_t console_size;
|
||||
size_t ftrace_size;
|
||||
|
@ -366,7 +372,8 @@ static int ramoops_init_przs(struct device *dev, struct ramoops_context *cxt,
|
|||
size_t sz = cxt->record_size;
|
||||
|
||||
cxt->przs[i] = persistent_ram_new(*paddr, sz, 0,
|
||||
&cxt->ecc_info);
|
||||
&cxt->ecc_info,
|
||||
cxt->memtype);
|
||||
if (IS_ERR(cxt->przs[i])) {
|
||||
err = PTR_ERR(cxt->przs[i]);
|
||||
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,
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*prz = persistent_ram_new(*paddr, sz, sig, &cxt->ecc_info);
|
||||
*prz = persistent_ram_new(*paddr, sz, sig, &cxt->ecc_info, cxt->memtype);
|
||||
if (IS_ERR(*prz)) {
|
||||
int err = PTR_ERR(*prz);
|
||||
|
||||
|
@ -443,6 +450,7 @@ static int ramoops_probe(struct platform_device *pdev)
|
|||
|
||||
cxt->size = pdata->mem_size;
|
||||
cxt->phys_addr = pdata->mem_address;
|
||||
cxt->memtype = pdata->mem_type;
|
||||
cxt->record_size = pdata->record_size;
|
||||
cxt->console_size = pdata->console_size;
|
||||
cxt->ftrace_size = pdata->ftrace_size;
|
||||
|
@ -572,6 +580,7 @@ static void ramoops_register_dummy(void)
|
|||
|
||||
dummy_data->mem_size = mem_size;
|
||||
dummy_data->mem_address = mem_address;
|
||||
dummy_data->mem_type = 0;
|
||||
dummy_data->record_size = record_size;
|
||||
dummy_data->console_size = ramoops_console_size;
|
||||
dummy_data->ftrace_size = ramoops_ftrace_size;
|
||||
|
|
|
@ -380,7 +380,8 @@ void persistent_ram_zap(struct persistent_ram_zone *prz)
|
|||
persistent_ram_update_header_ecc(prz);
|
||||
}
|
||||
|
||||
static void *persistent_ram_vmap(phys_addr_t start, size_t size)
|
||||
static void *persistent_ram_vmap(phys_addr_t start, size_t size,
|
||||
unsigned int memtype)
|
||||
{
|
||||
struct page **pages;
|
||||
phys_addr_t page_start;
|
||||
|
@ -392,7 +393,10 @@ static void *persistent_ram_vmap(phys_addr_t start, size_t size)
|
|||
page_start = start - offset_in_page(start);
|
||||
page_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE);
|
||||
|
||||
prot = pgprot_writecombine(PAGE_KERNEL);
|
||||
if (memtype)
|
||||
prot = pgprot_noncached(PAGE_KERNEL);
|
||||
else
|
||||
prot = pgprot_writecombine(PAGE_KERNEL);
|
||||
|
||||
pages = kmalloc_array(page_count, sizeof(struct page *), GFP_KERNEL);
|
||||
if (!pages) {
|
||||
|
@ -411,8 +415,11 @@ static void *persistent_ram_vmap(phys_addr_t start, size_t size)
|
|||
return vaddr;
|
||||
}
|
||||
|
||||
static void *persistent_ram_iomap(phys_addr_t start, size_t size)
|
||||
static void *persistent_ram_iomap(phys_addr_t start, size_t size,
|
||||
unsigned int memtype)
|
||||
{
|
||||
void *va;
|
||||
|
||||
if (!request_mem_region(start, size, "persistent_ram")) {
|
||||
pr_err("request mem region (0x%llx@0x%llx) failed\n",
|
||||
(unsigned long long)size, (unsigned long long)start);
|
||||
|
@ -422,19 +429,24 @@ static void *persistent_ram_iomap(phys_addr_t start, size_t size)
|
|||
buffer_start_add = buffer_start_add_locked;
|
||||
buffer_size_add = buffer_size_add_locked;
|
||||
|
||||
return ioremap_wc(start, size);
|
||||
if (memtype)
|
||||
va = ioremap(start, size);
|
||||
else
|
||||
va = ioremap_wc(start, size);
|
||||
|
||||
return va;
|
||||
}
|
||||
|
||||
static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size,
|
||||
struct persistent_ram_zone *prz)
|
||||
struct persistent_ram_zone *prz, int memtype)
|
||||
{
|
||||
prz->paddr = start;
|
||||
prz->size = size;
|
||||
|
||||
if (pfn_valid(start >> PAGE_SHIFT))
|
||||
prz->vaddr = persistent_ram_vmap(start, size);
|
||||
prz->vaddr = persistent_ram_vmap(start, size, memtype);
|
||||
else
|
||||
prz->vaddr = persistent_ram_iomap(start, size);
|
||||
prz->vaddr = persistent_ram_iomap(start, size, memtype);
|
||||
|
||||
if (!prz->vaddr) {
|
||||
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)
|
|||
}
|
||||
|
||||
struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
|
||||
u32 sig, struct persistent_ram_ecc_info *ecc_info)
|
||||
u32 sig, struct persistent_ram_ecc_info *ecc_info,
|
||||
unsigned int memtype)
|
||||
{
|
||||
struct persistent_ram_zone *prz;
|
||||
int ret = -ENOMEM;
|
||||
|
@ -511,7 +524,7 @@ struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
|
|||
goto err;
|
||||
}
|
||||
|
||||
ret = persistent_ram_buffer_map(start, size, prz);
|
||||
ret = persistent_ram_buffer_map(start, size, prz, memtype);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
|
|
@ -53,7 +53,8 @@ struct persistent_ram_zone {
|
|||
};
|
||||
|
||||
struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
|
||||
u32 sig, struct persistent_ram_ecc_info *ecc_info);
|
||||
u32 sig, struct persistent_ram_ecc_info *ecc_info,
|
||||
unsigned int memtype);
|
||||
void persistent_ram_free(struct persistent_ram_zone *prz);
|
||||
void persistent_ram_zap(struct persistent_ram_zone *prz);
|
||||
|
||||
|
@ -76,6 +77,7 @@ ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
|
|||
struct ramoops_platform_data {
|
||||
unsigned long mem_size;
|
||||
unsigned long mem_address;
|
||||
unsigned int mem_type;
|
||||
unsigned long record_size;
|
||||
unsigned long console_size;
|
||||
unsigned long ftrace_size;
|
||||
|
|
Loading…
Reference in a new issue