diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-07-27 04:48:23 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-07-27 04:48:23 +0300 |
commit | 1b3fc0bef8859268d542230172f80e85553fdab4 (patch) | |
tree | dca6ce8a7f2722a8d04e2c3c604cc10871c2f489 | |
parent | d31dcd92473b26463cd804410174f16be8a02573 (diff) | |
parent | 74e630a7582e6b3cb39559d712a0049f08dea8a0 (diff) | |
download | linux-1b3fc0bef8859268d542230172f80e85553fdab4.tar.xz |
Merge tag 'pstore-v4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux
Pull pstore subsystem updates from Kees Cook:
"This expands the supported compressors, fixes some bugs, and finally
adds DT bindings"
* tag 'pstore-v4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
pstore/ram: add Device Tree bindings
efi-pstore: implement efivars_pstore_exit()
pstore: drop file opened reference count
pstore: add lzo/lz4 compression support
pstore: Cleanup pstore_dump()
pstore: Enable compression on normal path (again)
ramoops: Only unregister when registered
-rw-r--r-- | Documentation/devicetree/bindings/misc/ramoops.txt | 48 | ||||
-rw-r--r-- | Documentation/ramoops.txt | 6 | ||||
-rw-r--r-- | arch/powerpc/kernel/nvram_64.c | 4 | ||||
-rw-r--r-- | drivers/acpi/apei/erst.c | 7 | ||||
-rw-r--r-- | drivers/firmware/efi/efi-pstore.c | 13 | ||||
-rw-r--r-- | fs/pstore/Kconfig | 31 | ||||
-rw-r--r-- | fs/pstore/inode.c | 1 | ||||
-rw-r--r-- | fs/pstore/platform.c | 269 | ||||
-rw-r--r-- | fs/pstore/ram.c | 105 | ||||
-rw-r--r-- | include/linux/pstore.h | 3 |
10 files changed, 436 insertions, 51 deletions
diff --git a/Documentation/devicetree/bindings/misc/ramoops.txt b/Documentation/devicetree/bindings/misc/ramoops.txt new file mode 100644 index 000000000000..cd02cec67d38 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/ramoops.txt @@ -0,0 +1,48 @@ +Ramoops oops/panic logger +========================= + +ramoops provides persistent RAM storage for oops and panics, so they can be +recovered after a reboot. It is a backend to pstore, so this node is named +"ramoops" after the backend, rather than "pstore" which is the subsystem. + +Parts of this storage may be set aside for other persistent log buffers, such +as kernel log messages, or for optional ECC error-correction data. The total +size of these optional buffers must fit in the reserved region. + +Any remaining space will be used for a circular buffer of oops and panic +records. These records have a configurable size, with a size of 0 indicating +that they should be disabled. + +At least one of "record-size", "console-size", "ftrace-size", or "pmsg-size" +must be set non-zero, but are otherwise optional as listed below. + + +Required properties: + +- compatible: must be "ramoops" + +- memory-region: phandle to a region of memory that is preserved between + reboots + + +Optional properties: + +- ecc-size: enables ECC support and specifies ECC buffer size in bytes + (defaults to 0: no ECC) + +- record-size: maximum size in bytes of each dump done on oops/panic + (defaults to 0: disabled) + +- console-size: size in bytes of log buffer reserved for kernel messages + (defaults to 0: disabled) + +- 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 + (defaults to 0: disabled) + +- unbuffered: if present, use unbuffered mappings to map the reserved region + (defaults to buffered mappings) + +- no-dump-oops: if present, only dump panics (defaults to panics and oops) diff --git a/Documentation/ramoops.txt b/Documentation/ramoops.txt index 5d8675615e59..9264bcab4099 100644 --- a/Documentation/ramoops.txt +++ b/Documentation/ramoops.txt @@ -45,7 +45,7 @@ corrupt, but usually it is restorable. 2. Setting the parameters -Setting the ramoops parameters can be done in 2 different manners: +Setting the ramoops parameters can be done in 3 different manners: 1. Use the module parameters (which have the names of the variables described as before). For quick debugging, you can also reserve parts of memory during boot @@ -54,7 +54,9 @@ Setting the ramoops parameters can be done in 2 different manners: kernel to use only the first 128 MB of memory, and place ECC-protected ramoops region at 128 MB boundary: "mem=128M ramoops.mem_address=0x8000000 ramoops.ecc=1" - 2. Use a platform device and set the platform data. The parameters can then + 2. Use Device Tree bindings, as described in + Documentation/device-tree/bindings/misc/ramoops.txt. + 3. Use a platform device and set the platform data. The parameters can then be set through that platform data. An example of doing that is: #include <linux/pstore_ram.h> diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c index 856f9a7944cd..64174bf95611 100644 --- a/arch/powerpc/kernel/nvram_64.c +++ b/arch/powerpc/kernel/nvram_64.c @@ -444,7 +444,8 @@ static int nvram_pstore_write(enum pstore_type_id type, */ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, int *count, struct timespec *time, char **buf, - bool *compressed, struct pstore_info *psi) + bool *compressed, ssize_t *ecc_notice_size, + struct pstore_info *psi) { struct oops_log_info *oops_hdr; unsigned int err_type, id_no, size = 0; @@ -545,6 +546,7 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, return -ENOMEM; kfree(buff); + *ecc_notice_size = 0; if (err_type == ERR_TYPE_KERNEL_PANIC_GZ) *compressed = true; else diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index 006c3894c6ea..f096ab3cb54d 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -927,7 +927,8 @@ static int erst_open_pstore(struct pstore_info *psi); static int erst_close_pstore(struct pstore_info *psi); static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, struct timespec *time, char **buf, - bool *compressed, struct pstore_info *psi); + bool *compressed, ssize_t *ecc_notice_size, + struct pstore_info *psi); static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, unsigned int part, int count, bool compressed, size_t size, struct pstore_info *psi); @@ -987,7 +988,8 @@ static int erst_close_pstore(struct pstore_info *psi) static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, struct timespec *time, char **buf, - bool *compressed, struct pstore_info *psi) + bool *compressed, ssize_t *ecc_notice_size, + struct pstore_info *psi) { int rc; ssize_t len = 0; @@ -1033,6 +1035,7 @@ skip: memcpy(*buf, rcd->data, len - sizeof(*rcd)); *id = record_id; *compressed = false; + *ecc_notice_size = 0; if (uuid_le_cmp(rcd->sec_hdr.section_type, CPER_SECTION_TYPE_DMESG_Z) == 0) { *type = PSTORE_TYPE_DMESG; diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index eac76a79a880..30a24d09ea6c 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -34,6 +34,7 @@ struct pstore_read_data { int *count; struct timespec *timespec; bool *compressed; + ssize_t *ecc_notice_size; char **buf; }; @@ -69,6 +70,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) *cb_data->compressed = true; else *cb_data->compressed = false; + *cb_data->ecc_notice_size = 0; } else if (sscanf(name, "dump-type%u-%u-%d-%lu", cb_data->type, &part, &cnt, &time) == 4) { *cb_data->id = generic_id(time, part, cnt); @@ -76,6 +78,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) cb_data->timespec->tv_sec = time; cb_data->timespec->tv_nsec = 0; *cb_data->compressed = false; + *cb_data->ecc_notice_size = 0; } else if (sscanf(name, "dump-type%u-%u-%lu", cb_data->type, &part, &time) == 3) { /* @@ -88,6 +91,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) cb_data->timespec->tv_sec = time; cb_data->timespec->tv_nsec = 0; *cb_data->compressed = false; + *cb_data->ecc_notice_size = 0; } else return 0; @@ -210,6 +214,7 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos) static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, int *count, struct timespec *timespec, char **buf, bool *compressed, + ssize_t *ecc_notice_size, struct pstore_info *psi) { struct pstore_read_data data; @@ -220,6 +225,7 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, data.count = count; data.timespec = timespec; data.compressed = compressed; + data.ecc_notice_size = ecc_notice_size; data.buf = buf; *data.buf = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL); @@ -393,6 +399,13 @@ static __init int efivars_pstore_init(void) static __exit void efivars_pstore_exit(void) { + if (!efi_pstore_info.bufsize) + return; + + pstore_unregister(&efi_pstore_info); + kfree(efi_pstore_info.buf); + efi_pstore_info.buf = NULL; + efi_pstore_info.bufsize = 0; } module_init(efivars_pstore_init); diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig index 360ae43f590c..be40813eff52 100644 --- a/fs/pstore/Kconfig +++ b/fs/pstore/Kconfig @@ -1,8 +1,6 @@ config PSTORE tristate "Persistent store support" default n - select ZLIB_DEFLATE - select ZLIB_INFLATE help This option enables generic access to platform level persistent storage via "pstore" filesystem that can @@ -14,6 +12,35 @@ config PSTORE If you don't have a platform persistent store driver, say N. +choice + prompt "Choose compression algorithm" + depends on PSTORE + default PSTORE_ZLIB_COMPRESS + help + This option chooses compression algorithm. + +config PSTORE_ZLIB_COMPRESS + bool "ZLIB" + select ZLIB_DEFLATE + select ZLIB_INFLATE + help + This option enables ZLIB compression algorithm support. + +config PSTORE_LZO_COMPRESS + bool "LZO" + select LZO_COMPRESS + select LZO_DECOMPRESS + help + This option enables LZO compression algorithm support. + +config PSTORE_LZ4_COMPRESS + bool "LZ4" + select LZ4_COMPRESS + select LZ4_DECOMPRESS + help + This option enables LZ4 compression algorithm support. +endchoice + config PSTORE_CONSOLE bool "Log kernel console messages" depends on PSTORE diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index 45d6110744cb..ec9ddef5ae75 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c @@ -178,7 +178,6 @@ static loff_t pstore_file_llseek(struct file *file, loff_t off, int whence) } static const struct file_operations pstore_file_operations = { - .owner = THIS_MODULE, .open = pstore_file_open, .read = pstore_file_read, .llseek = pstore_file_llseek, diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 588461bb2dd4..16ecca5b72d8 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -28,7 +28,15 @@ #include <linux/console.h> #include <linux/module.h> #include <linux/pstore.h> +#ifdef CONFIG_PSTORE_ZLIB_COMPRESS #include <linux/zlib.h> +#endif +#ifdef CONFIG_PSTORE_LZO_COMPRESS +#include <linux/lzo.h> +#endif +#ifdef CONFIG_PSTORE_LZ4_COMPRESS +#include <linux/lz4.h> +#endif #include <linux/string.h> #include <linux/timer.h> #include <linux/slab.h> @@ -69,10 +77,23 @@ struct pstore_info *psinfo; static char *backend; /* Compression parameters */ +#ifdef CONFIG_PSTORE_ZLIB_COMPRESS #define COMPR_LEVEL 6 #define WINDOW_BITS 12 #define MEM_LEVEL 4 static struct z_stream_s stream; +#else +static unsigned char *workspace; +#endif + +struct pstore_zbackend { + int (*compress)(const void *in, void *out, size_t inlen, size_t outlen); + int (*decompress)(void *in, void *out, size_t inlen, size_t outlen); + void (*allocate)(void); + void (*free)(void); + + const char *name; +}; static char *big_oops_buf; static size_t big_oops_buf_sz; @@ -129,9 +150,9 @@ bool pstore_cannot_block_path(enum kmsg_dump_reason reason) } EXPORT_SYMBOL_GPL(pstore_cannot_block_path); +#ifdef CONFIG_PSTORE_ZLIB_COMPRESS /* Derived from logfs_compress() */ -static int pstore_compress(const void *in, void *out, size_t inlen, - size_t outlen) +static int compress_zlib(const void *in, void *out, size_t inlen, size_t outlen) { int err, ret; @@ -165,7 +186,7 @@ error: } /* Derived from logfs_uncompress */ -static int pstore_decompress(void *in, void *out, size_t inlen, size_t outlen) +static int decompress_zlib(void *in, void *out, size_t inlen, size_t outlen) { int err, ret; @@ -194,7 +215,7 @@ error: return ret; } -static void allocate_buf_for_compression(void) +static void allocate_zlib(void) { size_t size; size_t cmpr; @@ -237,12 +258,190 @@ static void allocate_buf_for_compression(void) } -static void free_buf_for_compression(void) +static void free_zlib(void) { kfree(stream.workspace); stream.workspace = NULL; kfree(big_oops_buf); big_oops_buf = NULL; + big_oops_buf_sz = 0; +} + +static struct pstore_zbackend backend_zlib = { + .compress = compress_zlib, + .decompress = decompress_zlib, + .allocate = allocate_zlib, + .free = free_zlib, + .name = "zlib", +}; +#endif + +#ifdef CONFIG_PSTORE_LZO_COMPRESS +static int compress_lzo(const void *in, void *out, size_t inlen, size_t outlen) +{ + int ret; + + ret = lzo1x_1_compress(in, inlen, out, &outlen, workspace); + if (ret != LZO_E_OK) { + pr_err("lzo_compress error, ret = %d!\n", ret); + return -EIO; + } + + return outlen; +} + +static int decompress_lzo(void *in, void *out, size_t inlen, size_t outlen) +{ + int ret; + + ret = lzo1x_decompress_safe(in, inlen, out, &outlen); + if (ret != LZO_E_OK) { + pr_err("lzo_decompress error, ret = %d!\n", ret); + return -EIO; + } + + return outlen; +} + +static void allocate_lzo(void) +{ + big_oops_buf_sz = lzo1x_worst_compress(psinfo->bufsize); + big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL); + if (big_oops_buf) { + workspace = kmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL); + if (!workspace) { + pr_err("No memory for compression workspace; skipping compression\n"); + kfree(big_oops_buf); + big_oops_buf = NULL; + } + } else { + pr_err("No memory for uncompressed data; skipping compression\n"); + workspace = NULL; + } +} + +static void free_lzo(void) +{ + kfree(workspace); + kfree(big_oops_buf); + big_oops_buf = NULL; + big_oops_buf_sz = 0; +} + +static struct pstore_zbackend backend_lzo = { + .compress = compress_lzo, + .decompress = decompress_lzo, + .allocate = allocate_lzo, + .free = free_lzo, + .name = "lzo", +}; +#endif + +#ifdef CONFIG_PSTORE_LZ4_COMPRESS +static int compress_lz4(const void *in, void *out, size_t inlen, size_t outlen) +{ + int ret; + + ret = lz4_compress(in, inlen, out, &outlen, workspace); + if (ret) { + pr_err("lz4_compress error, ret = %d!\n", ret); + return -EIO; + } + + return outlen; +} + +static int decompress_lz4(void *in, void *out, size_t inlen, size_t outlen) +{ + int ret; + + ret = lz4_decompress_unknownoutputsize(in, inlen, out, &outlen); + if (ret) { + pr_err("lz4_decompress error, ret = %d!\n", ret); + return -EIO; + } + + return outlen; +} + +static void allocate_lz4(void) +{ + big_oops_buf_sz = lz4_compressbound(psinfo->bufsize); + big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL); + if (big_oops_buf) { + workspace = kmalloc(LZ4_MEM_COMPRESS, GFP_KERNEL); + if (!workspace) { + pr_err("No memory for compression workspace; skipping compression\n"); + kfree(big_oops_buf); + big_oops_buf = NULL; + } + } else { + pr_err("No memory for uncompressed data; skipping compression\n"); + workspace = NULL; + } +} + +static void free_lz4(void) +{ + kfree(workspace); + kfree(big_oops_buf); + big_oops_buf = NULL; + big_oops_buf_sz = 0; +} + +static struct pstore_zbackend backend_lz4 = { + .compress = compress_lz4, + .decompress = decompress_lz4, + .allocate = allocate_lz4, + .free = free_lz4, + .name = "lz4", +}; +#endif + +static struct pstore_zbackend *zbackend = +#if defined(CONFIG_PSTORE_ZLIB_COMPRESS) + &backend_zlib; +#elif defined(CONFIG_PSTORE_LZO_COMPRESS) + &backend_lzo; +#elif defined(CONFIG_PSTORE_LZ4_COMPRESS) + &backend_lz4; +#else + NULL; +#endif + +static int pstore_compress(const void *in, void *out, + size_t inlen, size_t outlen) +{ + if (zbackend) + return zbackend->compress(in, out, inlen, outlen); + else + return -EIO; +} + +static int pstore_decompress(void *in, void *out, size_t inlen, size_t outlen) +{ + if (zbackend) + return zbackend->decompress(in, out, inlen, outlen); + else + return -EIO; +} + +static void allocate_buf_for_compression(void) +{ + if (zbackend) { + pr_info("using %s compression\n", zbackend->name); + zbackend->allocate(); + } else { + pr_err("allocate compression buffer error!\n"); + } +} + +static void free_buf_for_compression(void) +{ + if (zbackend) + zbackend->free(); + else + pr_err("free compression buffer error!\n"); } /* @@ -284,7 +483,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, u64 id; unsigned int part = 1; unsigned long flags = 0; - int is_locked = 0; + int is_locked; int ret; why = get_reason_str(reason); @@ -295,8 +494,10 @@ static void pstore_dump(struct kmsg_dumper *dumper, pr_err("pstore dump routine blocked in %s path, may corrupt error record\n" , in_nmi() ? "NMI" : why); } - } else + } else { spin_lock_irqsave(&psinfo->buf_lock, flags); + is_locked = 1; + } oopscount++; while (total < kmsg_bytes) { char *dst; @@ -304,19 +505,25 @@ static void pstore_dump(struct kmsg_dumper *dumper, int hsize; int zipped_len = -1; size_t len; - bool compressed; + bool compressed = false; size_t total_len; if (big_oops_buf && is_locked) { dst = big_oops_buf; - hsize = sprintf(dst, "%s#%d Part%u\n", why, - oopscount, part); - size = big_oops_buf_sz - hsize; + size = big_oops_buf_sz; + } else { + dst = psinfo->buf; + size = psinfo->bufsize; + } - if (!kmsg_dump_get_buffer(dumper, true, dst + hsize, - size, &len)) - break; + hsize = sprintf(dst, "%s#%d Part%u\n", why, oopscount, part); + size -= hsize; + + if (!kmsg_dump_get_buffer(dumper, true, dst + hsize, + size, &len)) + break; + if (big_oops_buf && is_locked) { zipped_len = pstore_compress(dst, psinfo->buf, hsize + len, psinfo->bufsize); @@ -324,21 +531,9 @@ static void pstore_dump(struct kmsg_dumper *dumper, compressed = true; total_len = zipped_len; } else { - compressed = false; total_len = copy_kmsg_to_buffer(hsize, len); } } else { - dst = psinfo->buf; - hsize = sprintf(dst, "%s#%d Part%u\n", why, oopscount, - part); - size = psinfo->bufsize - hsize; - dst += hsize; - - if (!kmsg_dump_get_buffer(dumper, true, dst, - size, &len)) - break; - - compressed = false; total_len = hsize + len; } @@ -350,10 +545,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, total += total_len; part++; } - if (pstore_cannot_block_path(reason)) { - if (is_locked) - spin_unlock_irqrestore(&psinfo->buf_lock, flags); - } else + if (is_locked) spin_unlock_irqrestore(&psinfo->buf_lock, flags); } @@ -497,9 +689,11 @@ EXPORT_SYMBOL_GPL(pstore_register); void pstore_unregister(struct pstore_info *psi) { - pstore_unregister_pmsg(); - pstore_unregister_ftrace(); - pstore_unregister_console(); + if ((psi->flags & PSTORE_FLAGS_FRAGILE) == 0) { + pstore_unregister_pmsg(); + pstore_unregister_ftrace(); + pstore_unregister_console(); + } pstore_unregister_kmsg(); free_buf_for_compression(); @@ -527,6 +721,7 @@ void pstore_get_records(int quiet) int failed = 0, rc; bool compressed; int unzipped_len = -1; + ssize_t ecc_notice_size = 0; if (!psi) return; @@ -536,7 +731,7 @@ void pstore_get_records(int quiet) goto out; while ((size = psi->read(&id, &type, &count, &time, &buf, &compressed, - psi)) > 0) { + &ecc_notice_size, psi)) > 0) { if (compressed && (type == PSTORE_TYPE_DMESG)) { if (big_oops_buf) unzipped_len = pstore_decompress(buf, @@ -544,6 +739,9 @@ void pstore_get_records(int quiet) big_oops_buf_sz); if (unzipped_len > 0) { + if (ecc_notice_size) + memcpy(big_oops_buf + unzipped_len, + buf + size, ecc_notice_size); kfree(buf); buf = big_oops_buf; size = unzipped_len; @@ -555,7 +753,8 @@ void pstore_get_records(int quiet) } } rc = pstore_mkfile(type, psi->name, id, count, buf, - compressed, (size_t)size, time, psi); + compressed, size + ecc_notice_size, + time, psi); if (unzipped_len < 0) { /* Free buffer other than big oops */ kfree(buf); diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index bd9812e83461..47516a794011 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -34,6 +34,8 @@ #include <linux/slab.h> #include <linux/compiler.h> #include <linux/pstore_ram.h> +#include <linux/of.h> +#include <linux/of_address.h> #define RAMOOPS_KERNMSG_HDR "====" #define MIN_MEM_SIZE 4096UL @@ -181,10 +183,10 @@ static bool prz_ok(struct persistent_ram_zone *prz) static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, int *count, struct timespec *time, char **buf, bool *compressed, + ssize_t *ecc_notice_size, struct pstore_info *psi) { ssize_t size; - ssize_t ecc_notice_size; struct ramoops_context *cxt = psi->data; struct persistent_ram_zone *prz = NULL; int header_length = 0; @@ -229,16 +231,16 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, size = persistent_ram_old_size(prz) - header_length; /* ECC correction notice */ - ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0); + *ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0); - *buf = kmalloc(size + ecc_notice_size + 1, GFP_KERNEL); + *buf = kmalloc(size + *ecc_notice_size + 1, GFP_KERNEL); if (*buf == NULL) return -ENOMEM; memcpy(*buf, (char *)persistent_ram_old(prz) + header_length, size); - persistent_ram_ecc_string(prz, *buf + size, ecc_notice_size + 1); + persistent_ram_ecc_string(prz, *buf + size, *ecc_notice_size + 1); - return size + ecc_notice_size; + return size; } static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz, @@ -458,15 +460,98 @@ static int ramoops_init_prz(struct device *dev, struct ramoops_context *cxt, return 0; } +static int ramoops_parse_dt_size(struct platform_device *pdev, + const char *propname, u32 *value) +{ + u32 val32 = 0; + int ret; + + ret = of_property_read_u32(pdev->dev.of_node, propname, &val32); + if (ret < 0 && ret != -EINVAL) { + dev_err(&pdev->dev, "failed to parse property %s: %d\n", + propname, ret); + return ret; + } + + if (val32 > INT_MAX) { + dev_err(&pdev->dev, "%s %u > INT_MAX\n", propname, val32); + return -EOVERFLOW; + } + + *value = val32; + return 0; +} + +static int ramoops_parse_dt(struct platform_device *pdev, + struct ramoops_platform_data *pdata) +{ + struct device_node *of_node = pdev->dev.of_node; + struct device_node *mem_region; + struct resource res; + u32 value; + int ret; + + dev_dbg(&pdev->dev, "using Device Tree\n"); + + mem_region = of_parse_phandle(of_node, "memory-region", 0); + if (!mem_region) { + dev_err(&pdev->dev, "no memory-region phandle\n"); + return -ENODEV; + } + + ret = of_address_to_resource(mem_region, 0, &res); + of_node_put(mem_region); + if (ret) { + dev_err(&pdev->dev, + "failed to translate memory-region to resource: %d\n", + ret); + return ret; + } + + pdata->mem_size = resource_size(&res); + pdata->mem_address = res.start; + pdata->mem_type = of_property_read_bool(of_node, "unbuffered"); + pdata->dump_oops = !of_property_read_bool(of_node, "no-dump-oops"); + +#define parse_size(name, field) { \ + ret = ramoops_parse_dt_size(pdev, name, &value); \ + if (ret < 0) \ + return ret; \ + field = value; \ + } + + 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); + +#undef parse_size + + return 0; +} + static int ramoops_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct ramoops_platform_data *pdata = pdev->dev.platform_data; + struct ramoops_platform_data *pdata = dev->platform_data; struct ramoops_context *cxt = &oops_cxt; size_t dump_mem_sz; phys_addr_t paddr; int err = -EINVAL; + if (dev_of_node(dev) && !pdata) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + err = -ENOMEM; + goto fail_out; + } + + err = ramoops_parse_dt(pdev, pdata); + if (err < 0) + goto fail_out; + } + /* Only a single ramoops area allowed at a time, so fail extra * probes. */ @@ -596,11 +681,17 @@ static int ramoops_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id dt_match[] = { + { .compatible = "ramoops" }, + {} +}; + static struct platform_driver ramoops_driver = { .probe = ramoops_probe, .remove = ramoops_remove, .driver = { - .name = "ramoops", + .name = "ramoops", + .of_match_table = dt_match, }, }; diff --git a/include/linux/pstore.h b/include/linux/pstore.h index 831479f8df8f..899e95e84400 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h @@ -58,7 +58,8 @@ struct pstore_info { int (*close)(struct pstore_info *psi); ssize_t (*read)(u64 *id, enum pstore_type_id *type, int *count, struct timespec *time, char **buf, - bool *compressed, struct pstore_info *psi); + bool *compressed, ssize_t *ecc_notice_size, + struct pstore_info *psi); int (*write)(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, unsigned int part, int count, bool compressed, |