Merge tag 'please-pull-pstore' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 4 Sep 2013 04:14:06 +0000 (21:14 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 4 Sep 2013 04:14:06 +0000 (21:14 -0700)
Pull pstore changes from Tony Luck:
 "A big part of this is the addition of compression to the generic
  pstore layer so that all backends can use the pitiful amounts of
  storage they control more effectively.  Three other small
  fixes/cleanups too.

* tag 'please-pull-pstore' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux:
  pstore/ram: (really) fix undefined usage of rounddown_pow_of_two
  pstore/ram: Read and write to the 'compressed' flag of pstore
  efi-pstore: Read and write to the 'compressed' flag of pstore
  erst: Read and write to the 'compressed' flag of pstore
  powerpc/pseries: Read and write to the 'compressed' flag of pstore
  pstore: Add file extension to pstore file if compressed
  pstore: Add decompression support to pstore
  pstore: Introduce new argument 'compressed' in the read callback
  pstore: Add compression support to pstore
  pstore/Kconfig: Select ZLIB_DEFLATE and ZLIB_INFLATE when PSTORE is selected
  pstore: Add new argument 'compressed' in pstore write callback
  powerpc/pseries: Remove (de)compression in nvram with pstore enabled
  pstore: d_alloc_name() doesn't return an ERR_PTR
  acpi/apei/erst: Add missing iounmap() on error in erst_exec_move_data()

arch/powerpc/platforms/pseries/nvram.c
drivers/acpi/apei/erst.c
drivers/firmware/efi/efi-pstore.c
fs/pstore/Kconfig
fs/pstore/inode.c
fs/pstore/internal.h
fs/pstore/platform.c
fs/pstore/ram.c
include/linux/pstore.h

index 6a5f2b1..d276cd3 100644 (file)
@@ -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 */
@@ -584,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
  *
@@ -595,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;
@@ -611,30 +581,11 @@ 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;
-       }
+       if (compressed)
+               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;
@@ -650,12 +601,12 @@ 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;
        struct nvram_os_partition *part = NULL;
-       char *buff = NULL, *big_buff = NULL;
+       char *buff = NULL;
        int sig = 0;
        loff_t p;
 
@@ -719,8 +670,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) {
@@ -741,23 +691,10 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
                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;
-                       }
-               }
+               if (err_type == ERR_TYPE_KERNEL_PANIC_GZ)
+                       *compressed = true;
+               else
+                       *compressed = false;
                return length;
        }
 
@@ -777,13 +714,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 +734,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 +754,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 <n>
         * severity prefix) will reduce the oops/panic report to at most
@@ -831,9 +767,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 +782,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);
index 88d0b0f..822b1ed 100644 (file)
@@ -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);
 
@@ -933,9 +935,9 @@ 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, 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);
@@ -956,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)
@@ -989,7 +994,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;
@@ -1034,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,
@@ -1055,7 +1065,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 *)
@@ -1085,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;
index 73de5a9..5002d50 100644 (file)
@@ -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;
 
@@ -87,7 +100,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;
 
@@ -95,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,
@@ -103,7 +118,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];
@@ -111,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];
index ca71db6..983d951 100644 (file)
@@ -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
index 71bf5f4..1282384 100644 (file)
@@ -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);
@@ -345,9 +346,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);
index 937d820..3b3d305 100644 (file)
@@ -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
index 422962a..4ffb7ab 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/console.h>
 #include <linux/module.h>
 #include <linux/pstore.h>
+#include <linux/zlib.h>
 #include <linux/string.h>
 #include <linux/timer.h>
 #include <linux/slab.h>
@@ -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,121 @@ 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;
+}
+
+/* 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;
+
+       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,22 +273,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;
+               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, hsize, 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)) {
@@ -221,10 +380,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);
 }
 
@@ -261,6 +420,8 @@ int pstore_register(struct pstore_info *psi)
                return -EINVAL;
        }
 
+       allocate_buf_for_compression();
+
        if (pstore_is_mounted())
                pstore_get_records(0);
 
@@ -297,6 +458,8 @@ void pstore_get_records(int quiet)
        enum pstore_type_id     type;
        struct timespec         time;
        int                     failed = 0, rc;
+       bool                    compressed;
+       int                     unzipped_len = -1;
 
        if (!psi)
                return;
@@ -305,11 +468,32 @@ 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) {
+               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;
+                               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);
-               kfree(buf);
-               buf = NULL;
+                                 compressed, (size_t)size, time, psi);
+               if (unzipped_len < 0) {
+                       /* Free buffer other than big oops */
+                       kfree(buf);
+                       buf = NULL;
+               } else
+                       unzipped_len = -1;
                if (rc && (rc != -EEXIST || !quiet))
                        failed++;
        }
index a6119f9..fa8cef2 100644 (file)
@@ -131,9 +131,31 @@ 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, struct pstore_info *psi)
+                                  char **buf, bool *compressed,
+                                  struct pstore_info *psi)
 {
        ssize_t size;
        ssize_t ecc_notice_size;
@@ -152,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 */
@@ -166,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;
@@ -182,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);
@@ -196,7 +217,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;
@@ -242,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);
@@ -400,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;
index 4aa80ba..abd437d 100644 (file)
@@ -55,14 +55,14 @@ 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, 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,