]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
pstore: add lzo/lz4 compression support
authorGeliang Tang <geliangtang@163.com>
Thu, 18 Feb 2016 14:04:22 +0000 (22:04 +0800)
committerKees Cook <keescook@chromium.org>
Thu, 2 Jun 2016 17:59:31 +0000 (10:59 -0700)
Like zlib compression in pstore, this patch added lzo and lz4
compression support so that users can have more options and better
compression ratio.

The original code treats the compressed data together with the
uncompressed ECC correction notice by using zlib decompress. The
ECC correction notice is missing in the decompression process. The
treatment also makes lzo and lz4 not working. So I treat them
separately by using pstore_decompress() to treat the compressed
data, and memcpy() to treat the uncompressed ECC correction notice.

Signed-off-by: Geliang Tang <geliangtang@163.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
arch/powerpc/kernel/nvram_64.c
drivers/acpi/apei/erst.c
drivers/firmware/efi/efi-pstore.c
fs/pstore/Kconfig
fs/pstore/platform.c
fs/pstore/ram.c
include/linux/pstore.h

index 856f9a7944cd91052582cd1fa5531c4bc6c9f9ea..64174bf95611456e1a05d8c09f875e84b3c7331b 100644 (file)
@@ -444,7 +444,8 @@ static int nvram_pstore_write(enum pstore_type_id type,
  */
 static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
                                int *count, struct timespec *time, char **buf,
-                               bool *compressed, struct pstore_info *psi)
+                               bool *compressed, ssize_t *ecc_notice_size,
+                               struct pstore_info *psi)
 {
        struct oops_log_info *oops_hdr;
        unsigned int err_type, id_no, size = 0;
@@ -545,6 +546,7 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
                        return -ENOMEM;
                kfree(buff);
 
+               *ecc_notice_size = 0;
                if (err_type == ERR_TYPE_KERNEL_PANIC_GZ)
                        *compressed = true;
                else
index 006c3894c6ea1a9b3fd344b28b49a88b851c5fc4..f096ab3cb54d6f8b67fa1c7dee4cf0d4cc5179e2 100644 (file)
@@ -927,7 +927,8 @@ static int erst_open_pstore(struct pstore_info *psi);
 static int erst_close_pstore(struct pstore_info *psi);
 static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count,
                           struct timespec *time, char **buf,
-                          bool *compressed, struct pstore_info *psi);
+                          bool *compressed, ssize_t *ecc_notice_size,
+                          struct pstore_info *psi);
 static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason,
                       u64 *id, unsigned int part, int count, bool compressed,
                       size_t size, struct pstore_info *psi);
@@ -987,7 +988,8 @@ static int erst_close_pstore(struct pstore_info *psi)
 
 static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count,
                           struct timespec *time, char **buf,
-                          bool *compressed, struct pstore_info *psi)
+                          bool *compressed, ssize_t *ecc_notice_size,
+                          struct pstore_info *psi)
 {
        int rc;
        ssize_t len = 0;
@@ -1033,6 +1035,7 @@ static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count,
        memcpy(*buf, rcd->data, len - sizeof(*rcd));
        *id = record_id;
        *compressed = false;
+       *ecc_notice_size = 0;
        if (uuid_le_cmp(rcd->sec_hdr.section_type,
                        CPER_SECTION_TYPE_DMESG_Z) == 0) {
                *type = PSTORE_TYPE_DMESG;
index eac76a79a88015a4b5e8f2d20d0c56c9cc7c7de0..d5903ea77238987421003d1f25d5344d74926b52 100644 (file)
@@ -34,6 +34,7 @@ struct pstore_read_data {
        int *count;
        struct timespec *timespec;
        bool *compressed;
+       ssize_t *ecc_notice_size;
        char **buf;
 };
 
@@ -69,6 +70,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
                        *cb_data->compressed = true;
                else
                        *cb_data->compressed = false;
+               *cb_data->ecc_notice_size = 0;
        } else if (sscanf(name, "dump-type%u-%u-%d-%lu",
                   cb_data->type, &part, &cnt, &time) == 4) {
                *cb_data->id = generic_id(time, part, cnt);
@@ -76,6 +78,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
                cb_data->timespec->tv_sec = time;
                cb_data->timespec->tv_nsec = 0;
                *cb_data->compressed = false;
+               *cb_data->ecc_notice_size = 0;
        } else if (sscanf(name, "dump-type%u-%u-%lu",
                          cb_data->type, &part, &time) == 3) {
                /*
@@ -88,6 +91,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
                cb_data->timespec->tv_sec = time;
                cb_data->timespec->tv_nsec = 0;
                *cb_data->compressed = false;
+               *cb_data->ecc_notice_size = 0;
        } else
                return 0;
 
@@ -210,6 +214,7 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
 static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
                               int *count, struct timespec *timespec,
                               char **buf, bool *compressed,
+                              ssize_t *ecc_notice_size,
                               struct pstore_info *psi)
 {
        struct pstore_read_data data;
@@ -220,6 +225,7 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
        data.count = count;
        data.timespec = timespec;
        data.compressed = compressed;
+       data.ecc_notice_size = ecc_notice_size;
        data.buf = buf;
 
        *data.buf = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL);
index 360ae43f590cccf24630f4b5676ebf5f00ebca0b..be40813eff52c6a1dda5844a45e4b5670672ec6f 100644 (file)
@@ -1,8 +1,6 @@
 config PSTORE
        tristate "Persistent store support"
        default n
-       select ZLIB_DEFLATE
-       select ZLIB_INFLATE
        help
           This option enables generic access to platform level
           persistent storage via "pstore" filesystem that can
@@ -14,6 +12,35 @@ config PSTORE
           If you don't have a platform persistent store driver,
           say N.
 
+choice
+        prompt "Choose compression algorithm"
+        depends on PSTORE
+        default PSTORE_ZLIB_COMPRESS
+        help
+          This option chooses compression algorithm.
+
+config PSTORE_ZLIB_COMPRESS
+        bool "ZLIB"
+        select ZLIB_DEFLATE
+        select ZLIB_INFLATE
+        help
+          This option enables ZLIB compression algorithm support.
+
+config PSTORE_LZO_COMPRESS
+        bool "LZO"
+        select LZO_COMPRESS
+        select LZO_DECOMPRESS
+        help
+          This option enables LZO compression algorithm support.
+
+config PSTORE_LZ4_COMPRESS
+        bool "LZ4"
+        select LZ4_COMPRESS
+        select LZ4_DECOMPRESS
+        help
+          This option enables LZ4 compression algorithm support.
+endchoice
+
 config PSTORE_CONSOLE
        bool "Log kernel console messages"
        depends on PSTORE
index fe41d8ec663a150c83617cf75d3719fe0102d90b..16ecca5b72d815d92163def3f03bd9f26c4cc8b1 100644 (file)
 #include <linux/console.h>
 #include <linux/module.h>
 #include <linux/pstore.h>
+#ifdef CONFIG_PSTORE_ZLIB_COMPRESS
 #include <linux/zlib.h>
+#endif
+#ifdef CONFIG_PSTORE_LZO_COMPRESS
+#include <linux/lzo.h>
+#endif
+#ifdef CONFIG_PSTORE_LZ4_COMPRESS
+#include <linux/lz4.h>
+#endif
 #include <linux/string.h>
 #include <linux/timer.h>
 #include <linux/slab.h>
@@ -69,10 +77,23 @@ struct pstore_info *psinfo;
 static char *backend;
 
 /* Compression parameters */
+#ifdef CONFIG_PSTORE_ZLIB_COMPRESS
 #define COMPR_LEVEL 6
 #define WINDOW_BITS 12
 #define MEM_LEVEL 4
 static struct z_stream_s stream;
+#else
+static unsigned char *workspace;
+#endif
+
+struct pstore_zbackend {
+       int (*compress)(const void *in, void *out, size_t inlen, size_t outlen);
+       int (*decompress)(void *in, void *out, size_t inlen, size_t outlen);
+       void (*allocate)(void);
+       void (*free)(void);
+
+       const char *name;
+};
 
 static char *big_oops_buf;
 static size_t big_oops_buf_sz;
@@ -129,9 +150,9 @@ bool pstore_cannot_block_path(enum kmsg_dump_reason reason)
 }
 EXPORT_SYMBOL_GPL(pstore_cannot_block_path);
 
+#ifdef CONFIG_PSTORE_ZLIB_COMPRESS
 /* Derived from logfs_compress() */
-static int pstore_compress(const void *in, void *out, size_t inlen,
-                                                       size_t outlen)
+static int compress_zlib(const void *in, void *out, size_t inlen, size_t outlen)
 {
        int err, ret;
 
@@ -165,7 +186,7 @@ static int pstore_compress(const void *in, void *out, size_t inlen,
 }
 
 /* Derived from logfs_uncompress */
-static int pstore_decompress(void *in, void *out, size_t inlen, size_t outlen)
+static int decompress_zlib(void *in, void *out, size_t inlen, size_t outlen)
 {
        int err, ret;
 
@@ -194,7 +215,7 @@ static int pstore_decompress(void *in, void *out, size_t inlen, size_t outlen)
        return ret;
 }
 
-static void allocate_buf_for_compression(void)
+static void allocate_zlib(void)
 {
        size_t size;
        size_t cmpr;
@@ -237,12 +258,190 @@ static void allocate_buf_for_compression(void)
 
 }
 
-static void free_buf_for_compression(void)
+static void free_zlib(void)
 {
        kfree(stream.workspace);
        stream.workspace = NULL;
        kfree(big_oops_buf);
        big_oops_buf = NULL;
+       big_oops_buf_sz = 0;
+}
+
+static struct pstore_zbackend backend_zlib = {
+       .compress       = compress_zlib,
+       .decompress     = decompress_zlib,
+       .allocate       = allocate_zlib,
+       .free           = free_zlib,
+       .name           = "zlib",
+};
+#endif
+
+#ifdef CONFIG_PSTORE_LZO_COMPRESS
+static int compress_lzo(const void *in, void *out, size_t inlen, size_t outlen)
+{
+       int ret;
+
+       ret = lzo1x_1_compress(in, inlen, out, &outlen, workspace);
+       if (ret != LZO_E_OK) {
+               pr_err("lzo_compress error, ret = %d!\n", ret);
+               return -EIO;
+       }
+
+       return outlen;
+}
+
+static int decompress_lzo(void *in, void *out, size_t inlen, size_t outlen)
+{
+       int ret;
+
+       ret = lzo1x_decompress_safe(in, inlen, out, &outlen);
+       if (ret != LZO_E_OK) {
+               pr_err("lzo_decompress error, ret = %d!\n", ret);
+               return -EIO;
+       }
+
+       return outlen;
+}
+
+static void allocate_lzo(void)
+{
+       big_oops_buf_sz = lzo1x_worst_compress(psinfo->bufsize);
+       big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL);
+       if (big_oops_buf) {
+               workspace = kmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
+               if (!workspace) {
+                       pr_err("No memory for compression workspace; skipping compression\n");
+                       kfree(big_oops_buf);
+                       big_oops_buf = NULL;
+               }
+       } else {
+               pr_err("No memory for uncompressed data; skipping compression\n");
+               workspace = NULL;
+       }
+}
+
+static void free_lzo(void)
+{
+       kfree(workspace);
+       kfree(big_oops_buf);
+       big_oops_buf = NULL;
+       big_oops_buf_sz = 0;
+}
+
+static struct pstore_zbackend backend_lzo = {
+       .compress       = compress_lzo,
+       .decompress     = decompress_lzo,
+       .allocate       = allocate_lzo,
+       .free           = free_lzo,
+       .name           = "lzo",
+};
+#endif
+
+#ifdef CONFIG_PSTORE_LZ4_COMPRESS
+static int compress_lz4(const void *in, void *out, size_t inlen, size_t outlen)
+{
+       int ret;
+
+       ret = lz4_compress(in, inlen, out, &outlen, workspace);
+       if (ret) {
+               pr_err("lz4_compress error, ret = %d!\n", ret);
+               return -EIO;
+       }
+
+       return outlen;
+}
+
+static int decompress_lz4(void *in, void *out, size_t inlen, size_t outlen)
+{
+       int ret;
+
+       ret = lz4_decompress_unknownoutputsize(in, inlen, out, &outlen);
+       if (ret) {
+               pr_err("lz4_decompress error, ret = %d!\n", ret);
+               return -EIO;
+       }
+
+       return outlen;
+}
+
+static void allocate_lz4(void)
+{
+       big_oops_buf_sz = lz4_compressbound(psinfo->bufsize);
+       big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL);
+       if (big_oops_buf) {
+               workspace = kmalloc(LZ4_MEM_COMPRESS, GFP_KERNEL);
+               if (!workspace) {
+                       pr_err("No memory for compression workspace; skipping compression\n");
+                       kfree(big_oops_buf);
+                       big_oops_buf = NULL;
+               }
+       } else {
+               pr_err("No memory for uncompressed data; skipping compression\n");
+               workspace = NULL;
+       }
+}
+
+static void free_lz4(void)
+{
+       kfree(workspace);
+       kfree(big_oops_buf);
+       big_oops_buf = NULL;
+       big_oops_buf_sz = 0;
+}
+
+static struct pstore_zbackend backend_lz4 = {
+       .compress       = compress_lz4,
+       .decompress     = decompress_lz4,
+       .allocate       = allocate_lz4,
+       .free           = free_lz4,
+       .name           = "lz4",
+};
+#endif
+
+static struct pstore_zbackend *zbackend =
+#if defined(CONFIG_PSTORE_ZLIB_COMPRESS)
+       &backend_zlib;
+#elif defined(CONFIG_PSTORE_LZO_COMPRESS)
+       &backend_lzo;
+#elif defined(CONFIG_PSTORE_LZ4_COMPRESS)
+       &backend_lz4;
+#else
+       NULL;
+#endif
+
+static int pstore_compress(const void *in, void *out,
+                          size_t inlen, size_t outlen)
+{
+       if (zbackend)
+               return zbackend->compress(in, out, inlen, outlen);
+       else
+               return -EIO;
+}
+
+static int pstore_decompress(void *in, void *out, size_t inlen, size_t outlen)
+{
+       if (zbackend)
+               return zbackend->decompress(in, out, inlen, outlen);
+       else
+               return -EIO;
+}
+
+static void allocate_buf_for_compression(void)
+{
+       if (zbackend) {
+               pr_info("using %s compression\n", zbackend->name);
+               zbackend->allocate();
+       } else {
+               pr_err("allocate compression buffer error!\n");
+       }
+}
+
+static void free_buf_for_compression(void)
+{
+       if (zbackend)
+               zbackend->free();
+       else
+               pr_err("free compression buffer error!\n");
 }
 
 /*
@@ -522,6 +721,7 @@ void pstore_get_records(int quiet)
        int                     failed = 0, rc;
        bool                    compressed;
        int                     unzipped_len = -1;
+       ssize_t                 ecc_notice_size = 0;
 
        if (!psi)
                return;
@@ -531,7 +731,7 @@ void pstore_get_records(int quiet)
                goto out;
 
        while ((size = psi->read(&id, &type, &count, &time, &buf, &compressed,
-                               psi)) > 0) {
+                                &ecc_notice_size, psi)) > 0) {
                if (compressed && (type == PSTORE_TYPE_DMESG)) {
                        if (big_oops_buf)
                                unzipped_len = pstore_decompress(buf,
@@ -539,6 +739,9 @@ void pstore_get_records(int quiet)
                                                        big_oops_buf_sz);
 
                        if (unzipped_len > 0) {
+                               if (ecc_notice_size)
+                                       memcpy(big_oops_buf + unzipped_len,
+                                              buf + size, ecc_notice_size);
                                kfree(buf);
                                buf = big_oops_buf;
                                size = unzipped_len;
@@ -550,7 +753,8 @@ void pstore_get_records(int quiet)
                        }
                }
                rc = pstore_mkfile(type, psi->name, id, count, buf,
-                                 compressed, (size_t)size, time, psi);
+                                  compressed, size + ecc_notice_size,
+                                  time, psi);
                if (unzipped_len < 0) {
                        /* Free buffer other than big oops */
                        kfree(buf);
index bd9812e83461238745a57e30a6d39a732282680f..d9668c2b43cbde7ba4235b5a2e99f820899ee161 100644 (file)
@@ -181,10 +181,10 @@ static bool prz_ok(struct persistent_ram_zone *prz)
 static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
                                   int *count, struct timespec *time,
                                   char **buf, bool *compressed,
+                                  ssize_t *ecc_notice_size,
                                   struct pstore_info *psi)
 {
        ssize_t size;
-       ssize_t ecc_notice_size;
        struct ramoops_context *cxt = psi->data;
        struct persistent_ram_zone *prz = NULL;
        int header_length = 0;
@@ -229,16 +229,16 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
        size = persistent_ram_old_size(prz) - header_length;
 
        /* ECC correction notice */
-       ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0);
+       *ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0);
 
-       *buf = kmalloc(size + ecc_notice_size + 1, GFP_KERNEL);
+       *buf = kmalloc(size + *ecc_notice_size + 1, GFP_KERNEL);
        if (*buf == NULL)
                return -ENOMEM;
 
        memcpy(*buf, (char *)persistent_ram_old(prz) + header_length, size);
-       persistent_ram_ecc_string(prz, *buf + size, ecc_notice_size + 1);
+       persistent_ram_ecc_string(prz, *buf + size, *ecc_notice_size + 1);
 
-       return size + ecc_notice_size;
+       return size;
 }
 
 static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz,
index 831479f8df8f1b70638d59edc3cde6fc6fdcc22f..899e95e844006d9b2eb7ff1ecc41b9e40cc076a6 100644 (file)
@@ -58,7 +58,8 @@ struct pstore_info {
        int             (*close)(struct pstore_info *psi);
        ssize_t         (*read)(u64 *id, enum pstore_type_id *type,
                        int *count, struct timespec *time, char **buf,
-                       bool *compressed, struct pstore_info *psi);
+                       bool *compressed, ssize_t *ecc_notice_size,
+                       struct pstore_info *psi);
        int             (*write)(enum pstore_type_id type,
                        enum kmsg_dump_reason reason, u64 *id,
                        unsigned int part, int count, bool compressed,