From 08b326d071534fe5f122d8dec373cacb40bc7a7b Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 2 Jul 2013 09:02:32 +0800 Subject: 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 diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index 88d0b0f..6885809 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); -- cgit v0.10.2 From c39524e6744284452ef45480d3153bec28960c32 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 14 Aug 2013 10:55:49 -0700 Subject: 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 diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index 71bf5f4..6a4123d 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); -- cgit v0.10.2 From a3a5e94131d96ecdee7b1873a9d6215dc0ea7fff Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:52:33 -0700 Subject: 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 diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index 6a5f2b1..b966458 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); -- cgit v0.10.2 From b3b515bbd689ba3937cac2dd3fc55057f8c50329 Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:52:47 -0700 Subject: 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 diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index b966458..dbe5dad 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 6885809..bcdf8ce 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 73de5a9..fab6892 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 422962a..20fa686 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 a6119f9..fe7188f 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 4aa80ba..abfca4f 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, -- cgit v0.10.2 From 90ce4ca66864ee2de189b666e4e4230f452238a8 Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:52:57 -0700 Subject: 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 diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig index ca71db6..983d951 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 -- cgit v0.10.2 From b0aad7a99c1df90c23ff4bac76eea9cf25049e9e Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:53:10 -0700 Subject: 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 diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 20fa686..56218cb 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); -- cgit v0.10.2 From 9a4e1398208d147a9240731a1f8cfe7d8cc4c553 Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:53:19 -0700 Subject: 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 diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index dbe5dad..6c4dc52a 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 bcdf8ce..b412e91 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 fab6892..9a5425f 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 56218cb..6418eb7 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 fe7188f..2927223 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 abfca4f..abd437d 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, -- cgit v0.10.2 From adb42f5e105502aff2fc4518b16ba79c203fae4f Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:53:28 -0700 Subject: 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 diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 6418eb7..76bc5c1 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++; } -- cgit v0.10.2 From 9ad2cbe0a9b88ee6ee895d03b2c63fa1252c8e52 Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:53:39 -0700 Subject: 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 diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index 6a4123d..1282384 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 937d820..3b3d305 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 76bc5c1..4ffb7ab 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); -- cgit v0.10.2 From 40594264bd622e0865a981f14b98d9e09bc50b14 Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:53:49 -0700 Subject: 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 diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index 6c4dc52a..d276cd3 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; } -- cgit v0.10.2 From 901037ba31c09132b820f2a21b66444fca735749 Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:57:26 -0700 Subject: 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 diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index b412e91..822b1ed 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; -- cgit v0.10.2 From f8c62f34fe868f5bcca88a32e4a5c52b67de661d Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:57:51 -0700 Subject: 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 diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index 9a5425f..5002d50 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]; -- cgit v0.10.2 From 3f8f80f0cfebab185b6fe599c53b63e7e8ae02c9 Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:58:00 -0700 Subject: 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 diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index 2927223..4027c20 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); -- cgit v0.10.2 From 3bd11cf56e4d9c9a79c0c1a4ebe381c674ec9709 Mon Sep 17 00:00:00 2001 From: Maxime Bizon Date: Fri, 30 Aug 2013 18:06:41 +0200 Subject: 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 diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index 4027c20..fa8cef2 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; -- cgit v0.10.2