Merge tag 'trace-v6.6-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux...
[linux-2.6-microblaze.git] / fs / pstore / platform.c
index 62356d5..e5bca9a 100644 (file)
@@ -98,7 +98,14 @@ MODULE_PARM_DESC(kmsg_bytes, "amount of kernel log to snapshot (in bytes)");
 
 static void *compress_workspace;
 
+/*
+ * Compression is only used for dmesg output, which consists of low-entropy
+ * ASCII text, and so we can assume worst-case 60%.
+ */
+#define DMESG_COMP_PERCENT     60
+
 static char *big_oops_buf;
+static size_t max_compressed_size;
 
 void pstore_set_kmsg_bytes(int bytes)
 {
@@ -196,6 +203,7 @@ static int pstore_compress(const void *in, void *out,
 
 static void allocate_buf_for_compression(void)
 {
+       size_t compressed_size;
        char *buf;
 
        /* Skip if not built-in or compression disabled. */
@@ -216,7 +224,8 @@ static void allocate_buf_for_compression(void)
         * uncompressed record size, since any record that would be expanded by
         * compression is just stored uncompressed.
         */
-       buf = kvzalloc(psinfo->bufsize, GFP_KERNEL);
+       compressed_size = (psinfo->bufsize * 100) / DMESG_COMP_PERCENT;
+       buf = kvzalloc(compressed_size, GFP_KERNEL);
        if (!buf) {
                pr_err("Failed %zu byte compression buffer allocation for: %s\n",
                       psinfo->bufsize, compress);
@@ -233,6 +242,7 @@ static void allocate_buf_for_compression(void)
 
        /* A non-NULL big_oops_buf indicates compression is available. */
        big_oops_buf = buf;
+       max_compressed_size = compressed_size;
 
        pr_info("Using crash dump compression: %s\n", compress);
 }
@@ -246,6 +256,7 @@ static void free_buf_for_compression(void)
 
        kvfree(big_oops_buf);
        big_oops_buf = NULL;
+       max_compressed_size = 0;
 }
 
 void pstore_record_init(struct pstore_record *record,
@@ -305,7 +316,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
                record.buf = psinfo->buf;
 
                dst = big_oops_buf ?: psinfo->buf;
-               dst_size = psinfo->bufsize;
+               dst_size = max_compressed_size ?: psinfo->bufsize;
 
                /* Write dump header. */
                header_size = snprintf(dst, dst_size, "%s#%d Part%u\n", why,
@@ -326,8 +337,15 @@ static void pstore_dump(struct kmsg_dumper *dumper,
                                record.compressed = true;
                                record.size = zipped_len;
                        } else {
-                               record.size = header_size + dump_size;
-                               memcpy(psinfo->buf, dst, record.size);
+                               /*
+                                * Compression failed, so the buffer is most
+                                * likely filled with binary data that does not
+                                * compress as well as ASCII text. Copy as much
+                                * of the uncompressed data as possible into
+                                * the pstore record, and discard the rest.
+                                */
+                               record.size = psinfo->bufsize;
+                               memcpy(psinfo->buf, dst, psinfo->bufsize);
                        }
                } else {
                        record.size = header_size + dump_size;
@@ -560,6 +578,7 @@ static void decompress_record(struct pstore_record *record,
        int ret;
        int unzipped_len;
        char *unzipped, *workspace;
+       size_t max_uncompressed_size;
 
        if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS) || !record->compressed)
                return;
@@ -583,7 +602,8 @@ static void decompress_record(struct pstore_record *record,
        }
 
        /* Allocate enough space to hold max decompression and ECC. */
-       workspace = kvzalloc(psinfo->bufsize + record->ecc_notice_size,
+       max_uncompressed_size = 3 * psinfo->bufsize;
+       workspace = kvzalloc(max_uncompressed_size + record->ecc_notice_size,
                             GFP_KERNEL);
        if (!workspace)
                return;
@@ -591,11 +611,11 @@ static void decompress_record(struct pstore_record *record,
        zstream->next_in        = record->buf;
        zstream->avail_in       = record->size;
        zstream->next_out       = workspace;
-       zstream->avail_out      = psinfo->bufsize;
+       zstream->avail_out      = max_uncompressed_size;
 
        ret = zlib_inflate(zstream, Z_FINISH);
        if (ret != Z_STREAM_END) {
-               pr_err("zlib_inflate() failed, ret = %d!\n", ret);
+               pr_err_ratelimited("zlib_inflate() failed, ret = %d!\n", ret);
                kvfree(workspace);
                return;
        }