From 08b326d071534fe5f122d8dec373cacb40bc7a7b Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 2 Jul 2013 09:02:32 +0800 Subject: [PATCH 01/14] acpi/apei/erst: Add missing iounmap() on error in erst_exec_move_data() Add the missing iounmap() before return from erst_exec_move_data() in the error handling case. Signed-off-by: Wei Yongjun Acked-by: Kees Cook Signed-off-by: Tony Luck --- drivers/acpi/apei/erst.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index 88d0b0f9f92b..6885809abcd8 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -284,8 +284,10 @@ static int erst_exec_move_data(struct apei_exec_context *ctx, if (!src) return -ENOMEM; dst = ioremap(ctx->dst_base + offset, ctx->var2); - if (!dst) + if (!dst) { + iounmap(src); return -ENOMEM; + } memmove(dst, src, ctx->var2); From c39524e6744284452ef45480d3153bec28960c32 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 14 Aug 2013 10:55:49 -0700 Subject: [PATCH 02/14] pstore: d_alloc_name() doesn't return an ERR_PTR d_alloc_name() returns NULL on error. Also I changed the error code from -ENOSPC to -ENOMEM to reflect that we were short on RAM not disk space. Signed-off-by: Dan Carpenter Acked-by: Kees Cook Signed-off-by: Tony Luck --- fs/pstore/inode.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index 71bf5f4ae84c..6a4123d1849c 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c @@ -345,9 +345,8 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, mutex_lock(&root->d_inode->i_mutex); - rc = -ENOSPC; dentry = d_alloc_name(root, name); - if (IS_ERR(dentry)) + if (!dentry) goto fail_lockedalloc; memcpy(private->data, data, size); From a3a5e94131d96ecdee7b1873a9d6215dc0ea7fff Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:52:33 -0700 Subject: [PATCH 03/14] powerpc/pseries: Remove (de)compression in nvram with pstore enabled (De)compression support is provided in pstore in subsequent patches which needs an additional argument 'compressed' to determine if the data is compressed or not. This patch will take care of removing (de)compression in nvram with pstore which was making use of 'hsize' argument in pstore write as 'hsize' will be removed in the subsequent patch. Signed-off-by: Aruna Balakrishnaiah Reviewed-by: Kees Cook Signed-off-by: Tony Luck --- arch/powerpc/platforms/pseries/nvram.c | 102 +++---------------------- 1 file changed, 12 insertions(+), 90 deletions(-) diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index 6a5f2b1f32ca..b966458e9b21 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c @@ -539,36 +539,6 @@ static int zip_oops(size_t text_len) } #ifdef CONFIG_PSTORE -/* Derived from logfs_uncompress */ -int nvram_decompress(void *in, void *out, size_t inlen, size_t outlen) -{ - int err, ret; - - ret = -EIO; - err = zlib_inflateInit(&stream); - if (err != Z_OK) - goto error; - - stream.next_in = in; - stream.avail_in = inlen; - stream.total_in = 0; - stream.next_out = out; - stream.avail_out = outlen; - stream.total_out = 0; - - err = zlib_inflate(&stream, Z_FINISH); - if (err != Z_STREAM_END) - goto error; - - err = zlib_inflateEnd(&stream); - if (err != Z_OK) - goto error; - - ret = stream.total_out; -error: - return ret; -} - static int nvram_pstore_open(struct pstore_info *psi) { /* Reset the iterator to start reading partitions again */ @@ -611,30 +581,8 @@ static int nvram_pstore_write(enum pstore_type_id type, oops_hdr->report_length = (u16) size; oops_hdr->timestamp = get_seconds(); - if (big_oops_buf) { - rc = zip_oops(size); - /* - * If compression fails copy recent log messages from - * big_oops_buf to oops_data. - */ - if (rc != 0) { - size_t diff = size - oops_data_sz + hsize; - - if (size > oops_data_sz) { - memcpy(oops_data, big_oops_buf, hsize); - memcpy(oops_data + hsize, big_oops_buf + diff, - oops_data_sz - hsize); - - oops_hdr->report_length = (u16) oops_data_sz; - } else - memcpy(oops_data, big_oops_buf, size); - } else - err_type = ERR_TYPE_KERNEL_PANIC_GZ; - } - rc = nvram_write_os_partition(&oops_log_partition, oops_buf, - (int) (sizeof(*oops_hdr) + oops_hdr->report_length), err_type, - count); + (int) (sizeof(*oops_hdr) + size), err_type, count); if (rc != 0) return rc; @@ -655,7 +603,7 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, struct oops_log_info *oops_hdr; unsigned int err_type, id_no, size = 0; struct nvram_os_partition *part = NULL; - char *buff = NULL, *big_buff = NULL; + char *buff = NULL; int sig = 0; loff_t p; @@ -719,8 +667,7 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, *id = id_no; if (nvram_type_ids[read_type] == PSTORE_TYPE_DMESG) { - int length, unzipped_len; - size_t hdr_size; + size_t length, hdr_size; oops_hdr = (struct oops_log_info *)buff; if (oops_hdr->version < OOPS_HDR_VERSION) { @@ -740,24 +687,6 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, return -ENOMEM; memcpy(*buf, buff + hdr_size, length); kfree(buff); - - if (err_type == ERR_TYPE_KERNEL_PANIC_GZ) { - big_buff = kmalloc(big_oops_buf_sz, GFP_KERNEL); - if (!big_buff) - return -ENOMEM; - - unzipped_len = nvram_decompress(*buf, big_buff, - length, big_oops_buf_sz); - - if (unzipped_len < 0) { - pr_err("nvram: decompression failed, returned " - "rc %d\n", unzipped_len); - kfree(big_buff); - } else { - *buf = big_buff; - length = unzipped_len; - } - } return length; } @@ -777,13 +706,8 @@ static int nvram_pstore_init(void) { int rc = 0; - if (big_oops_buf) { - nvram_pstore_info.buf = big_oops_buf; - nvram_pstore_info.bufsize = big_oops_buf_sz; - } else { - nvram_pstore_info.buf = oops_data; - nvram_pstore_info.bufsize = oops_data_sz; - } + nvram_pstore_info.buf = oops_data; + nvram_pstore_info.bufsize = oops_data_sz; rc = pstore_register(&nvram_pstore_info); if (rc != 0) @@ -802,7 +726,6 @@ static int nvram_pstore_init(void) static void __init nvram_init_oops_partition(int rtas_partition_exists) { int rc; - size_t size; rc = pseries_nvram_init_os_partition(&oops_log_partition); if (rc != 0) { @@ -823,6 +746,11 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists) oops_data = oops_buf + sizeof(struct oops_log_info); oops_data_sz = oops_log_partition.size - sizeof(struct oops_log_info); + rc = nvram_pstore_init(); + + if (!rc) + return; + /* * Figure compression (preceded by elimination of each line's * severity prefix) will reduce the oops/panic report to at most @@ -831,9 +759,8 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists) big_oops_buf_sz = (oops_data_sz * 100) / 45; big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL); if (big_oops_buf) { - size = max(zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL), - zlib_inflate_workspacesize()); - stream.workspace = kmalloc(size, GFP_KERNEL); + stream.workspace = kmalloc(zlib_deflate_workspacesize( + WINDOW_BITS, MEM_LEVEL), GFP_KERNEL); if (!stream.workspace) { pr_err("nvram: No memory for compression workspace; " "skipping compression of %s partition data\n", @@ -847,11 +774,6 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists) stream.workspace = NULL; } - rc = nvram_pstore_init(); - - if (!rc) - return; - rc = kmsg_dump_register(&nvram_kmsg_dumper); if (rc != 0) { pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc); From b3b515bbd689ba3937cac2dd3fc55057f8c50329 Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:52:47 -0700 Subject: [PATCH 04/14] pstore: Add new argument 'compressed' in pstore write callback Addition of new argument 'compressed' in the write call back will help the backend to know if the data passed from pstore is compressed or not (In case where compression fails.). If compressed, the backend can add a tag indicating the data is compressed while writing to persistent store. Signed-off-by: Aruna Balakrishnaiah Reviewed-by: Kees Cook Signed-off-by: Tony Luck --- arch/powerpc/platforms/pseries/nvram.c | 4 ++-- drivers/acpi/apei/erst.c | 4 ++-- drivers/firmware/efi/efi-pstore.c | 2 +- fs/pstore/platform.c | 7 ++++--- fs/pstore/ram.c | 2 +- include/linux/pstore.h | 4 ++-- 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index b966458e9b21..dbe5dadb5fcc 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c @@ -554,7 +554,7 @@ static int nvram_pstore_open(struct pstore_info *psi) * @part: pstore writes data to registered buffer in parts, * part number will indicate the same. * @count: Indicates oops count - * @hsize: Size of header added by pstore + * @compressed: Flag to indicate the log is compressed * @size: number of bytes written to the registered buffer * @psi: registered pstore_info structure * @@ -565,7 +565,7 @@ static int nvram_pstore_open(struct pstore_info *psi) static int nvram_pstore_write(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, unsigned int part, int count, - size_t hsize, size_t size, + bool compressed, size_t size, struct pstore_info *psi) { int rc; diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index 6885809abcd8..bcdf8cefcb9e 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -937,7 +937,7 @@ static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, struct timespec *time, char **buf, 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, size_t hsize, + u64 *id, unsigned int part, int count, bool compressed, size_t size, struct pstore_info *psi); static int erst_clearer(enum pstore_type_id type, u64 id, int count, struct timespec time, struct pstore_info *psi); @@ -1057,7 +1057,7 @@ out: } static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, - u64 *id, unsigned int part, int count, size_t hsize, + u64 *id, unsigned int part, int count, bool compressed, size_t size, struct pstore_info *psi) { struct cper_pstore_record *rcd = (struct cper_pstore_record *) diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index 73de5a9c2247..fab6892f7053 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -103,7 +103,7 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, static int efi_pstore_write(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, - unsigned int part, int count, size_t hsize, size_t size, + unsigned int part, int count, bool compressed, size_t size, struct pstore_info *psi) { char name[DUMP_NAME_LEN]; diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 422962ae9fc2..20fa686f80fa 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -149,6 +149,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, unsigned long size; int hsize; size_t len; + bool compressed = false; dst = psinfo->buf; hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part); @@ -159,7 +160,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, break; ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part, - oopscount, hsize, hsize + len, psinfo); + oopscount, compressed, hsize + len, psinfo); if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted()) pstore_new_entry = 1; @@ -221,10 +222,10 @@ static void pstore_register_console(void) {} static int pstore_write_compat(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, unsigned int part, int count, - size_t hsize, size_t size, + bool compressed, size_t size, struct pstore_info *psi) { - return psi->write_buf(type, reason, id, part, psinfo->buf, hsize, + return psi->write_buf(type, reason, id, part, psinfo->buf, compressed, size, psi); } diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index a6119f9469e2..fe7188f742b7 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -196,7 +196,7 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, unsigned int part, const char *buf, - size_t hsize, size_t size, + bool compressed, size_t size, struct pstore_info *psi) { struct ramoops_context *cxt = psi->data; diff --git a/include/linux/pstore.h b/include/linux/pstore.h index 4aa80ba830a2..abfca4f59422 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h @@ -58,11 +58,11 @@ struct pstore_info { struct pstore_info *psi); int (*write)(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, - unsigned int part, int count, size_t hsize, + unsigned int part, int count, bool compressed, size_t size, struct pstore_info *psi); int (*write_buf)(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, - unsigned int part, const char *buf, size_t hsize, + unsigned int part, const char *buf, bool compressed, size_t size, struct pstore_info *psi); int (*erase)(enum pstore_type_id type, u64 id, int count, struct timespec time, From 90ce4ca66864ee2de189b666e4e4230f452238a8 Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:52:57 -0700 Subject: [PATCH 05/14] pstore/Kconfig: Select ZLIB_DEFLATE and ZLIB_INFLATE when PSTORE is selected Pstore will make use of deflate and inflate algorithm to compress and decompress the data. So when Pstore is enabled select zlib_deflate and zlib_inflate. Signed-off-by: Aruna Balakrishnaiah Reviewed-by: Kees Cook Signed-off-by: Tony Luck --- fs/pstore/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig index ca71db69da07..983d9510becc 100644 --- a/fs/pstore/Kconfig +++ b/fs/pstore/Kconfig @@ -1,6 +1,8 @@ config PSTORE bool "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 From b0aad7a99c1df90c23ff4bac76eea9cf25049e9e Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:53:10 -0700 Subject: [PATCH 06/14] pstore: Add compression support to pstore Add compression support to pstore which will help in capturing more data. Initially, pstore will make a call to kmsg_dump with a bigger buffer and will pass the size of bigger buffer to kmsg_dump and then compress the data to registered buffer of registered size. In case compression fails, pstore will capture the uncompressed data by making a call again to kmsg_dump with registered_buffer of registered size. Pstore will indicate the data is compressed or not with a flag in the write callback. Signed-off-by: Aruna Balakrishnaiah Reviewed-by: Kees Cook Signed-off-by: Tony Luck --- fs/pstore/platform.c | 148 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 139 insertions(+), 9 deletions(-) diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 20fa686f80fa..56218cb35267 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,15 @@ struct pstore_info *psinfo; static char *backend; +/* Compression parameters */ +#define COMPR_LEVEL 6 +#define WINDOW_BITS 12 +#define MEM_LEVEL 4 +static struct z_stream_s stream; + +static char *big_oops_buf; +static size_t big_oops_buf_sz; + /* How much of the console log to snapshot */ static unsigned long kmsg_bytes = 10240; @@ -117,6 +127,91 @@ bool pstore_cannot_block_path(enum kmsg_dump_reason reason) } EXPORT_SYMBOL_GPL(pstore_cannot_block_path); +/* Derived from logfs_compress() */ +static int pstore_compress(const void *in, void *out, size_t inlen, + size_t outlen) +{ + int err, ret; + + ret = -EIO; + err = zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS, + MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (err != Z_OK) + goto error; + + stream.next_in = in; + stream.avail_in = inlen; + stream.total_in = 0; + stream.next_out = out; + stream.avail_out = outlen; + stream.total_out = 0; + + err = zlib_deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) + goto error; + + err = zlib_deflateEnd(&stream); + if (err != Z_OK) + goto error; + + if (stream.total_out >= stream.total_in) + goto error; + + ret = stream.total_out; +error: + return ret; +} + +static void allocate_buf_for_compression(void) +{ + size_t size; + + big_oops_buf_sz = (psinfo->bufsize * 100) / 45; + big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL); + if (big_oops_buf) { + size = max(zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL), + zlib_inflate_workspacesize()); + stream.workspace = kmalloc(size, GFP_KERNEL); + if (!stream.workspace) { + pr_err("pstore: 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"); + stream.workspace = NULL; + } + +} + +/* + * Called when compression fails, since the printk buffer + * would be fetched for compression calling it again when + * compression fails would have moved the iterator of + * printk buffer which results in fetching old contents. + * Copy the recent messages from big_oops_buf to psinfo->buf + */ +static size_t copy_kmsg_to_buffer(int hsize, size_t len) +{ + size_t total_len; + size_t diff; + + total_len = hsize + len; + + if (total_len > psinfo->bufsize) { + diff = total_len - psinfo->bufsize + hsize; + memcpy(psinfo->buf, big_oops_buf, hsize); + memcpy(psinfo->buf + hsize, big_oops_buf + diff, + psinfo->bufsize - hsize); + total_len = psinfo->bufsize; + } else + memcpy(psinfo->buf, big_oops_buf, total_len); + + return total_len; +} + /* * callback from kmsg_dump. (s2,l2) has the most recently * written bytes, older bytes are in (s1,l1). Save as much @@ -148,23 +243,56 @@ static void pstore_dump(struct kmsg_dumper *dumper, char *dst; unsigned long size; int hsize; + int zipped_len = -1; size_t len; - bool compressed = false; + bool compressed; + size_t total_len; - dst = psinfo->buf; - hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part); - size = psinfo->bufsize - hsize; - dst += hsize; + if (big_oops_buf) { + dst = big_oops_buf; + hsize = sprintf(dst, "%s#%d Part%d\n", why, + oopscount, part); + size = big_oops_buf_sz - hsize; - if (!kmsg_dump_get_buffer(dumper, true, dst, size, &len)) - break; + if (!kmsg_dump_get_buffer(dumper, true, dst + hsize, + size, &len)) + break; + + zipped_len = pstore_compress(dst, psinfo->buf, + hsize + len, psinfo->bufsize); + + if (zipped_len > 0) { + compressed = true; + total_len = zipped_len; + } else { + pr_err("pstore: compression failed for Part %d" + " returned %d\n", part, zipped_len); + pr_err("pstore: Capture uncompressed" + " oops/panic report of Part %d\n", part); + compressed = false; + total_len = copy_kmsg_to_buffer(hsize, len); + } + } else { + dst = psinfo->buf; + hsize = sprintf(dst, "%s#%d Part%d\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; + } ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part, - oopscount, compressed, hsize + len, psinfo); + oopscount, compressed, total_len, psinfo); if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted()) pstore_new_entry = 1; - total += hsize + len; + total += total_len; part++; } if (pstore_cannot_block_path(reason)) { @@ -262,6 +390,8 @@ int pstore_register(struct pstore_info *psi) return -EINVAL; } + allocate_buf_for_compression(); + if (pstore_is_mounted()) pstore_get_records(0); From 9a4e1398208d147a9240731a1f8cfe7d8cc4c553 Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:53:19 -0700 Subject: [PATCH 07/14] pstore: Introduce new argument 'compressed' in the read callback Backends will set the flag 'compressed' after reading the log from persistent store to indicate the data being returned to pstore is compressed or not. Signed-off-by: Aruna Balakrishnaiah Reviewed-by: Kees Cook Signed-off-by: Tony Luck --- arch/powerpc/platforms/pseries/nvram.c | 2 +- drivers/acpi/apei/erst.c | 4 ++-- drivers/firmware/efi/efi-pstore.c | 3 ++- fs/pstore/platform.c | 4 +++- fs/pstore/ram.c | 3 ++- include/linux/pstore.h | 2 +- 6 files changed, 11 insertions(+), 7 deletions(-) diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index dbe5dadb5fcc..6c4dc52ac0b5 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c @@ -598,7 +598,7 @@ 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, - struct pstore_info *psi) + bool *compressed, struct pstore_info *psi) { struct oops_log_info *oops_hdr; unsigned int err_type, id_no, size = 0; diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index bcdf8cefcb9e..b412e9137e36 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -935,7 +935,7 @@ 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, - struct pstore_info *psi); + bool *compressed, 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); @@ -991,7 +991,7 @@ 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, - struct pstore_info *psi) + bool *compressed, struct pstore_info *psi) { int rc; ssize_t len = 0; diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index fab6892f7053..9a5425f3f885 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -87,7 +87,8 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, int *count, struct timespec *timespec, - char **buf, struct pstore_info *psi) + char **buf, bool *compressed, + struct pstore_info *psi) { struct pstore_read_data data; diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 56218cb35267..6418eb77d64b 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -428,6 +428,7 @@ void pstore_get_records(int quiet) enum pstore_type_id type; struct timespec time; int failed = 0, rc; + bool compressed; if (!psi) return; @@ -436,7 +437,8 @@ void pstore_get_records(int quiet) if (psi->open && psi->open(psi)) goto out; - while ((size = psi->read(&id, &type, &count, &time, &buf, psi)) > 0) { + while ((size = psi->read(&id, &type, &count, &time, &buf, &compressed, + psi)) > 0) { rc = pstore_mkfile(type, psi->name, id, count, buf, (size_t)size, time, psi); kfree(buf); diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index fe7188f742b7..292722327811 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -133,7 +133,8 @@ ramoops_get_next_prz(struct persistent_ram_zone *przs[], uint *c, uint max, static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, int *count, struct timespec *time, - char **buf, struct pstore_info *psi) + char **buf, bool *compressed, + struct pstore_info *psi) { ssize_t size; ssize_t ecc_notice_size; diff --git a/include/linux/pstore.h b/include/linux/pstore.h index abfca4f59422..abd437d0a8a7 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h @@ -55,7 +55,7 @@ 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, - struct pstore_info *psi); + bool *compressed, 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, From adb42f5e105502aff2fc4518b16ba79c203fae4f Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:53:28 -0700 Subject: [PATCH 08/14] pstore: Add decompression support to pstore Based on the flag 'compressed' set or not, pstore will decompress the data returning a plain text file. If decompression fails for a particular record it will have the compressed data in the file which can be decompressed with 'openssl' command line tool. Signed-off-by: Aruna Balakrishnaiah Reviewed-by: Kees Cook Signed-off-by: Tony Luck --- fs/pstore/platform.c | 53 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 6418eb77d64b..76bc5c12c0cf 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -162,6 +162,36 @@ error: return ret; } +/* Derived from logfs_uncompress */ +static int pstore_decompress(void *in, void *out, size_t inlen, size_t outlen) +{ + int err, ret; + + ret = -EIO; + err = zlib_inflateInit(&stream); + if (err != Z_OK) + goto error; + + stream.next_in = in; + stream.avail_in = inlen; + stream.total_in = 0; + stream.next_out = out; + stream.avail_out = outlen; + stream.total_out = 0; + + err = zlib_inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) + goto error; + + err = zlib_inflateEnd(&stream); + if (err != Z_OK) + goto error; + + ret = stream.total_out; +error: + return ret; +} + static void allocate_buf_for_compression(void) { size_t size; @@ -429,6 +459,7 @@ void pstore_get_records(int quiet) struct timespec time; int failed = 0, rc; bool compressed; + int unzipped_len = -1; if (!psi) return; @@ -439,10 +470,28 @@ void pstore_get_records(int quiet) while ((size = psi->read(&id, &type, &count, &time, &buf, &compressed, psi)) > 0) { + if (compressed && (type == PSTORE_TYPE_DMESG)) { + if (big_oops_buf) + unzipped_len = pstore_decompress(buf, + big_oops_buf, size, + big_oops_buf_sz); + + if (unzipped_len > 0) { + buf = big_oops_buf; + size = unzipped_len; + } else { + pr_err("pstore: decompression failed;" + "returned %d\n", unzipped_len); + } + } rc = pstore_mkfile(type, psi->name, id, count, buf, (size_t)size, time, psi); - kfree(buf); - buf = NULL; + if (unzipped_len < 0) { + /* Free buffer other than big oops */ + kfree(buf); + buf = NULL; + } else + unzipped_len = -1; if (rc && (rc != -EEXIST || !quiet)) failed++; } From 9ad2cbe0a9b88ee6ee895d03b2c63fa1252c8e52 Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:53:39 -0700 Subject: [PATCH 09/14] pstore: Add file extension to pstore file if compressed In case decompression fails, add a ".enc.z" to indicate the file has compressed data. This will help user space utilities to figure out the file contents. Signed-off-by: Aruna Balakrishnaiah Reviewed-by: Kees Cook Signed-off-by: Tony Luck --- fs/pstore/inode.c | 7 ++++--- fs/pstore/internal.h | 5 +++-- fs/pstore/platform.c | 4 +++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index 6a4123d1849c..12823845d324 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c @@ -275,8 +275,8 @@ int pstore_is_mounted(void) * Set the mtime & ctime to the date that this record was originally stored. */ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, - char *data, size_t size, struct timespec time, - struct pstore_info *psi) + char *data, bool compressed, size_t size, + struct timespec time, struct pstore_info *psi) { struct dentry *root = pstore_sb->s_root; struct dentry *dentry; @@ -315,7 +315,8 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count, switch (type) { case PSTORE_TYPE_DMESG: - sprintf(name, "dmesg-%s-%lld", psname, id); + sprintf(name, "dmesg-%s-%lld%s", psname, id, + compressed ? ".enc.z" : ""); break; case PSTORE_TYPE_CONSOLE: sprintf(name, "console-%s", psname); diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h index 937d820f273c..3b3d305277c4 100644 --- a/fs/pstore/internal.h +++ b/fs/pstore/internal.h @@ -50,8 +50,9 @@ extern struct pstore_info *psinfo; extern void pstore_set_kmsg_bytes(int); extern void pstore_get_records(int); extern int pstore_mkfile(enum pstore_type_id, char *psname, u64 id, - int count, char *data, size_t size, - struct timespec time, struct pstore_info *psi); + int count, char *data, bool compressed, + size_t size, struct timespec time, + struct pstore_info *psi); extern int pstore_is_mounted(void); #endif diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 76bc5c12c0cf..4ffb7ab5e397 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -479,13 +479,15 @@ void pstore_get_records(int quiet) if (unzipped_len > 0) { buf = big_oops_buf; size = unzipped_len; + compressed = false; } else { pr_err("pstore: decompression failed;" "returned %d\n", unzipped_len); + compressed = true; } } rc = pstore_mkfile(type, psi->name, id, count, buf, - (size_t)size, time, psi); + compressed, (size_t)size, time, psi); if (unzipped_len < 0) { /* Free buffer other than big oops */ kfree(buf); From 40594264bd622e0865a981f14b98d9e09bc50b14 Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:53:49 -0700 Subject: [PATCH 10/14] powerpc/pseries: Read and write to the 'compressed' flag of pstore If data returned from pstore is compressed, nvram's write callback will add a flag ERR_TYPE_KERNEL_PANIC_GZ indicating the data is compressed while writing to nvram. If the data read from nvram is compressed, nvram's read callback will set the flag 'compressed'. The patch adds backward compatibilty with old format oops header when reading from pstore. Signed-off-by: Aruna Balakrishnaiah Reviewed-by: Kees Cook Signed-off-by: Tony Luck --- arch/powerpc/platforms/pseries/nvram.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index 6c4dc52ac0b5..d276cd3edd8f 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c @@ -581,6 +581,9 @@ static int nvram_pstore_write(enum pstore_type_id type, oops_hdr->report_length = (u16) size; oops_hdr->timestamp = get_seconds(); + if (compressed) + err_type = ERR_TYPE_KERNEL_PANIC_GZ; + rc = nvram_write_os_partition(&oops_log_partition, oops_buf, (int) (sizeof(*oops_hdr) + size), err_type, count); @@ -687,6 +690,11 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, return -ENOMEM; memcpy(*buf, buff + hdr_size, length); kfree(buff); + + if (err_type == ERR_TYPE_KERNEL_PANIC_GZ) + *compressed = true; + else + *compressed = false; return length; } From 901037ba31c09132b820f2a21b66444fca735749 Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:57:26 -0700 Subject: [PATCH 11/14] erst: Read and write to the 'compressed' flag of pstore In pstore write, set the section type to CPER_SECTION_TYPE_DMESG_COMPR if the data is compressed. In pstore read, read the section type and update the 'compressed' flag accordingly. Signed-off-by: Aruna Balakrishnaiah Reviewed-by: Kees Cook Signed-off-by: Tony Luck --- drivers/acpi/apei/erst.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index b412e9137e36..822b1ed3b00f 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -958,6 +958,9 @@ static struct pstore_info erst_info = { #define CPER_SECTION_TYPE_DMESG \ UUID_LE(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54, \ 0x94, 0x19, 0xeb, 0x12) +#define CPER_SECTION_TYPE_DMESG_Z \ + UUID_LE(0x4f118707, 0x04dd, 0x4055, 0xb5, 0xdd, 0x95, 0x6d, \ + 0x34, 0xdd, 0xfa, 0xc6) #define CPER_SECTION_TYPE_MCE \ UUID_LE(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96, \ 0x04, 0x4a, 0x38, 0xfc) @@ -1036,7 +1039,12 @@ skip: } memcpy(*buf, rcd->data, len - sizeof(*rcd)); *id = record_id; + *compressed = false; if (uuid_le_cmp(rcd->sec_hdr.section_type, + CPER_SECTION_TYPE_DMESG_Z) == 0) { + *type = PSTORE_TYPE_DMESG; + *compressed = true; + } else if (uuid_le_cmp(rcd->sec_hdr.section_type, CPER_SECTION_TYPE_DMESG) == 0) *type = PSTORE_TYPE_DMESG; else if (uuid_le_cmp(rcd->sec_hdr.section_type, @@ -1087,7 +1095,10 @@ static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, rcd->sec_hdr.flags = CPER_SEC_PRIMARY; switch (type) { case PSTORE_TYPE_DMESG: - rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG; + if (compressed) + rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG_Z; + else + rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG; break; case PSTORE_TYPE_MCE: rcd->sec_hdr.section_type = CPER_SECTION_TYPE_MCE; From f8c62f34fe868f5bcca88a32e4a5c52b67de661d Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:57:51 -0700 Subject: [PATCH 12/14] efi-pstore: Read and write to the 'compressed' flag of pstore In pstore write, Efi will add a character 'C'(compressed) or D'(decompressed) in its header while writing to persistent store. In pstore read, read the header and update the 'compressed' flag accordingly. Signed-off-by: Aruna Balakrishnaiah Reviewed-by: Kees Cook Signed-off-by: Tony Luck --- drivers/firmware/efi/efi-pstore.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index 9a5425f3f885..5002d50e3781 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -35,6 +35,7 @@ struct pstore_read_data { enum pstore_type_id *type; int *count; struct timespec *timespec; + bool *compressed; char **buf; }; @@ -42,7 +43,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) { efi_guid_t vendor = LINUX_EFI_CRASH_GUID; struct pstore_read_data *cb_data = data; - char name[DUMP_NAME_LEN]; + char name[DUMP_NAME_LEN], data_type; int i; int cnt; unsigned int part; @@ -54,12 +55,23 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) for (i = 0; i < DUMP_NAME_LEN; i++) name[i] = entry->var.VariableName[i]; - if (sscanf(name, "dump-type%u-%u-%d-%lu", + if (sscanf(name, "dump-type%u-%u-%d-%lu-%c", + cb_data->type, &part, &cnt, &time, &data_type) == 5) { + *cb_data->id = part; + *cb_data->count = cnt; + cb_data->timespec->tv_sec = time; + cb_data->timespec->tv_nsec = 0; + if (data_type == 'C') + *cb_data->compressed = true; + else + *cb_data->compressed = false; + } else if (sscanf(name, "dump-type%u-%u-%d-%lu", cb_data->type, &part, &cnt, &time) == 4) { *cb_data->id = part; *cb_data->count = cnt; cb_data->timespec->tv_sec = time; cb_data->timespec->tv_nsec = 0; + *cb_data->compressed = false; } else if (sscanf(name, "dump-type%u-%u-%lu", cb_data->type, &part, &time) == 3) { /* @@ -71,6 +83,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) *cb_data->count = 0; cb_data->timespec->tv_sec = time; cb_data->timespec->tv_nsec = 0; + *cb_data->compressed = false; } else return 0; @@ -96,6 +109,7 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, data.type = type; data.count = count; data.timespec = timespec; + data.compressed = compressed; data.buf = buf; return __efivar_entry_iter(efi_pstore_read_func, &efivar_sysfs_list, &data, @@ -112,8 +126,8 @@ static int efi_pstore_write(enum pstore_type_id type, efi_guid_t vendor = LINUX_EFI_CRASH_GUID; int i, ret = 0; - sprintf(name, "dump-type%u-%u-%d-%lu", type, part, count, - get_seconds()); + sprintf(name, "dump-type%u-%u-%d-%lu-%c", type, part, count, + get_seconds(), compressed ? 'C' : 'D'); for (i = 0; i < DUMP_NAME_LEN; i++) efi_name[i] = name[i]; From 3f8f80f0cfebab185b6fe599c53b63e7e8ae02c9 Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:58:00 -0700 Subject: [PATCH 13/14] pstore/ram: Read and write to the 'compressed' flag of pstore In pstore write, add character 'C'(compressed) or 'D'(decompressed) in the header while writing to Ram persistent buffer. In pstore read, read the header and update the 'compressed' flag accordingly. Signed-off-by: Aruna Balakrishnaiah Reviewed-by: Kees Cook Signed-off-by: Tony Luck --- fs/pstore/ram.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index 292722327811..4027c2065842 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -131,6 +131,27 @@ ramoops_get_next_prz(struct persistent_ram_zone *przs[], uint *c, uint max, return prz; } +static void ramoops_read_kmsg_hdr(char *buffer, struct timespec *time, + bool *compressed) +{ + char data_type; + + if (sscanf(buffer, RAMOOPS_KERNMSG_HDR "%lu.%lu-%c\n", + &time->tv_sec, &time->tv_nsec, &data_type) == 3) { + if (data_type == 'C') + *compressed = true; + else + *compressed = false; + } else if (sscanf(buffer, RAMOOPS_KERNMSG_HDR "%lu.%lu\n", + &time->tv_sec, &time->tv_nsec) == 2) { + *compressed = false; + } else { + time->tv_sec = 0; + time->tv_nsec = 0; + *compressed = false; + } +} + static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, int *count, struct timespec *time, char **buf, bool *compressed, @@ -153,10 +174,6 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, if (!prz) return 0; - /* TODO(kees): Bogus time for the moment. */ - time->tv_sec = 0; - time->tv_nsec = 0; - size = persistent_ram_old_size(prz); /* ECC correction notice */ @@ -167,12 +184,14 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, return -ENOMEM; memcpy(*buf, persistent_ram_old(prz), size); + ramoops_read_kmsg_hdr(*buf, time, compressed); persistent_ram_ecc_string(prz, *buf + size, ecc_notice_size + 1); return size + ecc_notice_size; } -static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz) +static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz, + bool compressed) { char *hdr; struct timespec timestamp; @@ -183,8 +202,9 @@ static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz) timestamp.tv_sec = 0; timestamp.tv_nsec = 0; } - hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lu.%lu\n", - (long)timestamp.tv_sec, (long)(timestamp.tv_nsec / 1000)); + hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lu.%lu-%c\n", + (long)timestamp.tv_sec, (long)(timestamp.tv_nsec / 1000), + compressed ? 'C' : 'D'); WARN_ON_ONCE(!hdr); len = hdr ? strlen(hdr) : 0; persistent_ram_write(prz, hdr, len); @@ -243,7 +263,7 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type, prz = cxt->przs[cxt->dump_write_cnt]; - hlen = ramoops_write_kmsg_hdr(prz); + hlen = ramoops_write_kmsg_hdr(prz, compressed); if (size + hlen > prz->buffer_size) size = prz->buffer_size - hlen; persistent_ram_write(prz, buf, size); From 3bd11cf56e4d9c9a79c0c1a4ebe381c674ec9709 Mon Sep 17 00:00:00 2001 From: Maxime Bizon Date: Fri, 30 Aug 2013 18:06:41 +0200 Subject: [PATCH 14/14] pstore/ram: (really) fix undefined usage of rounddown_pow_of_two Previous attempt to fix was b042e47491ba5f487601b5141a3f1d8582304170 Suggested use of is_power_of_2() was bogus because is_power_of_2(0) is false (documented behaviour). Signed-off-by: Maxime Bizon Acked-by: Kees Cook Signed-off-by: Tony Luck --- fs/pstore/ram.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index 4027c2065842..fa8cef2cca3a 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -421,11 +421,11 @@ static int ramoops_probe(struct platform_device *pdev) goto fail_out; } - if (!is_power_of_2(pdata->record_size)) + if (pdata->record_size && !is_power_of_2(pdata->record_size)) pdata->record_size = rounddown_pow_of_two(pdata->record_size); - if (!is_power_of_2(pdata->console_size)) + if (pdata->console_size && !is_power_of_2(pdata->console_size)) pdata->console_size = rounddown_pow_of_two(pdata->console_size); - if (!is_power_of_2(pdata->ftrace_size)) + if (pdata->ftrace_size && !is_power_of_2(pdata->ftrace_size)) pdata->ftrace_size = rounddown_pow_of_two(pdata->ftrace_size); cxt->dump_read_cnt = 0;