Merge tag 'pstore-v6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux
Pull pstore updates from Kees Cook: - Greatly simplify compression support (Ard Biesheuvel) - Avoid crashes for corrupted offsets when prz size is 0 (Enlin Mu) - Expand range of usable record sizes (Yuxiao Zhang) - Fix kernel-doc warning (Matthew Wilcox) * tag 'pstore-v6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: pstore: Fix kernel-doc warning pstore: Support record sizes larger than kmalloc() limit pstore/ram: Check start of empty przs during init pstore: Replace crypto API compression with zlib_deflate library calls pstore: Remove worst-case compression size logic
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0-only
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
config PSTORE
|
config PSTORE
|
||||||
tristate "Persistent store support"
|
tristate "Persistent store support"
|
||||||
select CRYPTO if PSTORE_COMPRESS
|
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
This option enables generic access to platform level
|
This option enables generic access to platform level
|
||||||
@@ -22,99 +21,18 @@ config PSTORE_DEFAULT_KMSG_BYTES
|
|||||||
Defines default size of pstore kernel log storage.
|
Defines default size of pstore kernel log storage.
|
||||||
Can be enlarged if needed, not recommended to shrink it.
|
Can be enlarged if needed, not recommended to shrink it.
|
||||||
|
|
||||||
config PSTORE_DEFLATE_COMPRESS
|
|
||||||
tristate "DEFLATE (ZLIB) compression"
|
|
||||||
default y
|
|
||||||
depends on PSTORE
|
|
||||||
select CRYPTO_DEFLATE
|
|
||||||
help
|
|
||||||
This option enables DEFLATE (also known as ZLIB) compression
|
|
||||||
algorithm support.
|
|
||||||
|
|
||||||
config PSTORE_LZO_COMPRESS
|
|
||||||
tristate "LZO compression"
|
|
||||||
depends on PSTORE
|
|
||||||
select CRYPTO_LZO
|
|
||||||
help
|
|
||||||
This option enables LZO compression algorithm support.
|
|
||||||
|
|
||||||
config PSTORE_LZ4_COMPRESS
|
|
||||||
tristate "LZ4 compression"
|
|
||||||
depends on PSTORE
|
|
||||||
select CRYPTO_LZ4
|
|
||||||
help
|
|
||||||
This option enables LZ4 compression algorithm support.
|
|
||||||
|
|
||||||
config PSTORE_LZ4HC_COMPRESS
|
|
||||||
tristate "LZ4HC compression"
|
|
||||||
depends on PSTORE
|
|
||||||
select CRYPTO_LZ4HC
|
|
||||||
help
|
|
||||||
This option enables LZ4HC (high compression) mode algorithm.
|
|
||||||
|
|
||||||
config PSTORE_842_COMPRESS
|
|
||||||
bool "842 compression"
|
|
||||||
depends on PSTORE
|
|
||||||
select CRYPTO_842
|
|
||||||
help
|
|
||||||
This option enables 842 compression algorithm support.
|
|
||||||
|
|
||||||
config PSTORE_ZSTD_COMPRESS
|
|
||||||
bool "zstd compression"
|
|
||||||
depends on PSTORE
|
|
||||||
select CRYPTO_ZSTD
|
|
||||||
help
|
|
||||||
This option enables zstd compression algorithm support.
|
|
||||||
|
|
||||||
config PSTORE_COMPRESS
|
config PSTORE_COMPRESS
|
||||||
def_bool y
|
bool "Pstore compression (deflate)"
|
||||||
depends on PSTORE
|
depends on PSTORE
|
||||||
depends on PSTORE_DEFLATE_COMPRESS || PSTORE_LZO_COMPRESS || \
|
select ZLIB_INFLATE
|
||||||
PSTORE_LZ4_COMPRESS || PSTORE_LZ4HC_COMPRESS || \
|
select ZLIB_DEFLATE
|
||||||
PSTORE_842_COMPRESS || PSTORE_ZSTD_COMPRESS
|
default y
|
||||||
|
|
||||||
choice
|
|
||||||
prompt "Default pstore compression algorithm"
|
|
||||||
depends on PSTORE_COMPRESS
|
|
||||||
help
|
help
|
||||||
This option chooses the default active compression algorithm.
|
Whether pstore records should be compressed before being written to
|
||||||
This change be changed at boot with "pstore.compress=..." on
|
the backing store. This is implemented using the zlib 'deflate'
|
||||||
the kernel command line.
|
algorithm, using the library implementation instead of using the full
|
||||||
|
blown crypto API. This reduces the risk of secondary oopses or other
|
||||||
Currently, pstore has support for 6 compression algorithms:
|
problems while pstore is recording panic metadata.
|
||||||
deflate, lzo, lz4, lz4hc, 842 and zstd.
|
|
||||||
|
|
||||||
The default compression algorithm is deflate.
|
|
||||||
|
|
||||||
config PSTORE_DEFLATE_COMPRESS_DEFAULT
|
|
||||||
bool "deflate" if PSTORE_DEFLATE_COMPRESS
|
|
||||||
|
|
||||||
config PSTORE_LZO_COMPRESS_DEFAULT
|
|
||||||
bool "lzo" if PSTORE_LZO_COMPRESS
|
|
||||||
|
|
||||||
config PSTORE_LZ4_COMPRESS_DEFAULT
|
|
||||||
bool "lz4" if PSTORE_LZ4_COMPRESS
|
|
||||||
|
|
||||||
config PSTORE_LZ4HC_COMPRESS_DEFAULT
|
|
||||||
bool "lz4hc" if PSTORE_LZ4HC_COMPRESS
|
|
||||||
|
|
||||||
config PSTORE_842_COMPRESS_DEFAULT
|
|
||||||
bool "842" if PSTORE_842_COMPRESS
|
|
||||||
|
|
||||||
config PSTORE_ZSTD_COMPRESS_DEFAULT
|
|
||||||
bool "zstd" if PSTORE_ZSTD_COMPRESS
|
|
||||||
|
|
||||||
endchoice
|
|
||||||
|
|
||||||
config PSTORE_COMPRESS_DEFAULT
|
|
||||||
string
|
|
||||||
depends on PSTORE_COMPRESS
|
|
||||||
default "deflate" if PSTORE_DEFLATE_COMPRESS_DEFAULT
|
|
||||||
default "lzo" if PSTORE_LZO_COMPRESS_DEFAULT
|
|
||||||
default "lz4" if PSTORE_LZ4_COMPRESS_DEFAULT
|
|
||||||
default "lz4hc" if PSTORE_LZ4HC_COMPRESS_DEFAULT
|
|
||||||
default "842" if PSTORE_842_COMPRESS_DEFAULT
|
|
||||||
default "zstd" if PSTORE_ZSTD_COMPRESS_DEFAULT
|
|
||||||
|
|
||||||
config PSTORE_CONSOLE
|
config PSTORE_CONSOLE
|
||||||
bool "Log kernel console messages"
|
bool "Log kernel console messages"
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ static void free_pstore_private(struct pstore_private *private)
|
|||||||
if (!private)
|
if (!private)
|
||||||
return;
|
return;
|
||||||
if (private->record) {
|
if (private->record) {
|
||||||
kfree(private->record->buf);
|
kvfree(private->record->buf);
|
||||||
kfree(private->record->priv);
|
kfree(private->record->priv);
|
||||||
kfree(private->record);
|
kfree(private->record);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,24 +14,17 @@
|
|||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/kmsg_dump.h>
|
#include <linux/kmsg_dump.h>
|
||||||
#include <linux/console.h>
|
#include <linux/console.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pstore.h>
|
#include <linux/pstore.h>
|
||||||
#if IS_ENABLED(CONFIG_PSTORE_LZO_COMPRESS)
|
|
||||||
#include <linux/lzo.h>
|
|
||||||
#endif
|
|
||||||
#if IS_ENABLED(CONFIG_PSTORE_LZ4_COMPRESS) || IS_ENABLED(CONFIG_PSTORE_LZ4HC_COMPRESS)
|
|
||||||
#include <linux/lz4.h>
|
|
||||||
#endif
|
|
||||||
#if IS_ENABLED(CONFIG_PSTORE_ZSTD_COMPRESS)
|
|
||||||
#include <linux/zstd.h>
|
|
||||||
#endif
|
|
||||||
#include <linux/crypto.h>
|
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/jiffies.h>
|
#include <linux/jiffies.h>
|
||||||
|
#include <linux/vmalloc.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
#include <linux/zlib.h>
|
||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
@@ -80,12 +73,21 @@ static char *backend;
|
|||||||
module_param(backend, charp, 0444);
|
module_param(backend, charp, 0444);
|
||||||
MODULE_PARM_DESC(backend, "specific backend to use");
|
MODULE_PARM_DESC(backend, "specific backend to use");
|
||||||
|
|
||||||
static char *compress =
|
/*
|
||||||
#ifdef CONFIG_PSTORE_COMPRESS_DEFAULT
|
* pstore no longer implements compression via the crypto API, and only
|
||||||
CONFIG_PSTORE_COMPRESS_DEFAULT;
|
* supports zlib deflate compression implemented using the zlib library
|
||||||
#else
|
* interface. This removes additional complexity which is hard to justify for a
|
||||||
NULL;
|
* diagnostic facility that has to operate in conditions where the system may
|
||||||
#endif
|
* have become unstable. Zlib deflate is comparatively small in terms of code
|
||||||
|
* size, and compresses ASCII text comparatively well. In terms of compression
|
||||||
|
* speed, deflate is not the best performer but for recording the log output on
|
||||||
|
* a kernel panic, this is not considered critical.
|
||||||
|
*
|
||||||
|
* The only remaining arguments supported by the compress= module parameter are
|
||||||
|
* 'deflate' and 'none'. To retain compatibility with existing installations,
|
||||||
|
* all other values are logged and replaced with 'deflate'.
|
||||||
|
*/
|
||||||
|
static char *compress = "deflate";
|
||||||
module_param(compress, charp, 0444);
|
module_param(compress, charp, 0444);
|
||||||
MODULE_PARM_DESC(compress, "compression to use");
|
MODULE_PARM_DESC(compress, "compression to use");
|
||||||
|
|
||||||
@@ -94,16 +96,9 @@ unsigned long kmsg_bytes = CONFIG_PSTORE_DEFAULT_KMSG_BYTES;
|
|||||||
module_param(kmsg_bytes, ulong, 0444);
|
module_param(kmsg_bytes, ulong, 0444);
|
||||||
MODULE_PARM_DESC(kmsg_bytes, "amount of kernel log to snapshot (in bytes)");
|
MODULE_PARM_DESC(kmsg_bytes, "amount of kernel log to snapshot (in bytes)");
|
||||||
|
|
||||||
/* Compression parameters */
|
static void *compress_workspace;
|
||||||
static struct crypto_comp *tfm;
|
|
||||||
|
|
||||||
struct pstore_zbackend {
|
|
||||||
int (*zbufsize)(size_t size);
|
|
||||||
const char *name;
|
|
||||||
};
|
|
||||||
|
|
||||||
static char *big_oops_buf;
|
static char *big_oops_buf;
|
||||||
static size_t big_oops_buf_sz;
|
|
||||||
|
|
||||||
void pstore_set_kmsg_bytes(int bytes)
|
void pstore_set_kmsg_bytes(int bytes)
|
||||||
{
|
{
|
||||||
@@ -168,206 +163,89 @@ static bool pstore_cannot_block_path(enum kmsg_dump_reason reason)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_PSTORE_DEFLATE_COMPRESS)
|
|
||||||
static int zbufsize_deflate(size_t size)
|
|
||||||
{
|
|
||||||
size_t cmpr;
|
|
||||||
|
|
||||||
switch (size) {
|
|
||||||
/* buffer range for efivars */
|
|
||||||
case 1000 ... 2000:
|
|
||||||
cmpr = 56;
|
|
||||||
break;
|
|
||||||
case 2001 ... 3000:
|
|
||||||
cmpr = 54;
|
|
||||||
break;
|
|
||||||
case 3001 ... 3999:
|
|
||||||
cmpr = 52;
|
|
||||||
break;
|
|
||||||
/* buffer range for nvram, erst */
|
|
||||||
case 4000 ... 10000:
|
|
||||||
cmpr = 45;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
cmpr = 60;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (size * 100) / cmpr;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_PSTORE_LZO_COMPRESS)
|
|
||||||
static int zbufsize_lzo(size_t size)
|
|
||||||
{
|
|
||||||
return lzo1x_worst_compress(size);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_PSTORE_LZ4_COMPRESS) || IS_ENABLED(CONFIG_PSTORE_LZ4HC_COMPRESS)
|
|
||||||
static int zbufsize_lz4(size_t size)
|
|
||||||
{
|
|
||||||
return LZ4_compressBound(size);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_PSTORE_842_COMPRESS)
|
|
||||||
static int zbufsize_842(size_t size)
|
|
||||||
{
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_PSTORE_ZSTD_COMPRESS)
|
|
||||||
static int zbufsize_zstd(size_t size)
|
|
||||||
{
|
|
||||||
return zstd_compress_bound(size);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const struct pstore_zbackend *zbackend __ro_after_init;
|
|
||||||
|
|
||||||
static const struct pstore_zbackend zbackends[] = {
|
|
||||||
#if IS_ENABLED(CONFIG_PSTORE_DEFLATE_COMPRESS)
|
|
||||||
{
|
|
||||||
.zbufsize = zbufsize_deflate,
|
|
||||||
.name = "deflate",
|
|
||||||
},
|
|
||||||
#endif
|
|
||||||
#if IS_ENABLED(CONFIG_PSTORE_LZO_COMPRESS)
|
|
||||||
{
|
|
||||||
.zbufsize = zbufsize_lzo,
|
|
||||||
.name = "lzo",
|
|
||||||
},
|
|
||||||
#endif
|
|
||||||
#if IS_ENABLED(CONFIG_PSTORE_LZ4_COMPRESS)
|
|
||||||
{
|
|
||||||
.zbufsize = zbufsize_lz4,
|
|
||||||
.name = "lz4",
|
|
||||||
},
|
|
||||||
#endif
|
|
||||||
#if IS_ENABLED(CONFIG_PSTORE_LZ4HC_COMPRESS)
|
|
||||||
{
|
|
||||||
.zbufsize = zbufsize_lz4,
|
|
||||||
.name = "lz4hc",
|
|
||||||
},
|
|
||||||
#endif
|
|
||||||
#if IS_ENABLED(CONFIG_PSTORE_842_COMPRESS)
|
|
||||||
{
|
|
||||||
.zbufsize = zbufsize_842,
|
|
||||||
.name = "842",
|
|
||||||
},
|
|
||||||
#endif
|
|
||||||
#if IS_ENABLED(CONFIG_PSTORE_ZSTD_COMPRESS)
|
|
||||||
{
|
|
||||||
.zbufsize = zbufsize_zstd,
|
|
||||||
.name = "zstd",
|
|
||||||
},
|
|
||||||
#endif
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
|
|
||||||
static int pstore_compress(const void *in, void *out,
|
static int pstore_compress(const void *in, void *out,
|
||||||
unsigned int inlen, unsigned int outlen)
|
unsigned int inlen, unsigned int outlen)
|
||||||
{
|
{
|
||||||
|
struct z_stream_s zstream = {
|
||||||
|
.next_in = in,
|
||||||
|
.avail_in = inlen,
|
||||||
|
.next_out = out,
|
||||||
|
.avail_out = outlen,
|
||||||
|
.workspace = compress_workspace,
|
||||||
|
};
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS))
|
if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = crypto_comp_compress(tfm, in, inlen, out, &outlen);
|
ret = zlib_deflateInit2(&zstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
|
||||||
if (ret) {
|
-MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
|
||||||
pr_err("crypto_comp_compress failed, ret = %d!\n", ret);
|
if (ret != Z_OK)
|
||||||
return ret;
|
return -EINVAL;
|
||||||
}
|
|
||||||
|
|
||||||
return outlen;
|
ret = zlib_deflate(&zstream, Z_FINISH);
|
||||||
|
if (ret != Z_STREAM_END)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = zlib_deflateEnd(&zstream);
|
||||||
|
if (ret != Z_OK)
|
||||||
|
pr_warn_once("zlib_deflateEnd() failed: %d\n", ret);
|
||||||
|
|
||||||
|
return zstream.total_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void allocate_buf_for_compression(void)
|
static void allocate_buf_for_compression(void)
|
||||||
{
|
{
|
||||||
struct crypto_comp *ctx;
|
|
||||||
int size;
|
|
||||||
char *buf;
|
char *buf;
|
||||||
|
|
||||||
/* Skip if not built-in or compression backend not selected yet. */
|
/* Skip if not built-in or compression disabled. */
|
||||||
if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS) || !zbackend)
|
if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS) || !compress ||
|
||||||
return;
|
!strcmp(compress, "none")) {
|
||||||
|
compress = NULL;
|
||||||
/* Skip if no pstore backend yet or compression init already done. */
|
|
||||||
if (!psinfo || tfm)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!crypto_has_comp(zbackend->name, 0, 0)) {
|
|
||||||
pr_err("Unknown compression: %s\n", zbackend->name);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size = zbackend->zbufsize(psinfo->bufsize);
|
if (strcmp(compress, "deflate")) {
|
||||||
if (size <= 0) {
|
pr_err("Unsupported compression '%s', falling back to deflate\n",
|
||||||
pr_err("Invalid compression size for %s: %d\n",
|
compress);
|
||||||
zbackend->name, size);
|
compress = "deflate";
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buf = kmalloc(size, GFP_KERNEL);
|
/*
|
||||||
|
* The compression buffer only needs to be as large as the maximum
|
||||||
|
* uncompressed record size, since any record that would be expanded by
|
||||||
|
* compression is just stored uncompressed.
|
||||||
|
*/
|
||||||
|
buf = kvzalloc(psinfo->bufsize, GFP_KERNEL);
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
pr_err("Failed %d byte compression buffer allocation for: %s\n",
|
pr_err("Failed %zu byte compression buffer allocation for: %s\n",
|
||||||
size, zbackend->name);
|
psinfo->bufsize, compress);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = crypto_alloc_comp(zbackend->name, 0, 0);
|
compress_workspace =
|
||||||
if (IS_ERR_OR_NULL(ctx)) {
|
vmalloc(zlib_deflate_workspacesize(MAX_WBITS, DEF_MEM_LEVEL));
|
||||||
kfree(buf);
|
if (!compress_workspace) {
|
||||||
pr_err("crypto_alloc_comp('%s') failed: %ld\n", zbackend->name,
|
pr_err("Failed to allocate zlib deflate workspace\n");
|
||||||
PTR_ERR(ctx));
|
kvfree(buf);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* A non-NULL big_oops_buf indicates compression is available. */
|
/* A non-NULL big_oops_buf indicates compression is available. */
|
||||||
tfm = ctx;
|
|
||||||
big_oops_buf_sz = size;
|
|
||||||
big_oops_buf = buf;
|
big_oops_buf = buf;
|
||||||
|
|
||||||
pr_info("Using crash dump compression: %s\n", zbackend->name);
|
pr_info("Using crash dump compression: %s\n", compress);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_buf_for_compression(void)
|
static void free_buf_for_compression(void)
|
||||||
{
|
{
|
||||||
if (IS_ENABLED(CONFIG_PSTORE_COMPRESS) && tfm) {
|
if (IS_ENABLED(CONFIG_PSTORE_COMPRESS) && compress_workspace) {
|
||||||
crypto_free_comp(tfm);
|
vfree(compress_workspace);
|
||||||
tfm = NULL;
|
compress_workspace = NULL;
|
||||||
}
|
}
|
||||||
kfree(big_oops_buf);
|
|
||||||
|
kvfree(big_oops_buf);
|
||||||
big_oops_buf = NULL;
|
big_oops_buf = NULL;
|
||||||
big_oops_buf_sz = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pstore_record_init(struct pstore_record *record,
|
void pstore_record_init(struct pstore_record *record,
|
||||||
@@ -426,13 +304,8 @@ static void pstore_dump(struct kmsg_dumper *dumper,
|
|||||||
record.part = part;
|
record.part = part;
|
||||||
record.buf = psinfo->buf;
|
record.buf = psinfo->buf;
|
||||||
|
|
||||||
if (big_oops_buf) {
|
dst = big_oops_buf ?: psinfo->buf;
|
||||||
dst = big_oops_buf;
|
dst_size = psinfo->bufsize;
|
||||||
dst_size = big_oops_buf_sz;
|
|
||||||
} else {
|
|
||||||
dst = psinfo->buf;
|
|
||||||
dst_size = psinfo->bufsize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write dump header. */
|
/* Write dump header. */
|
||||||
header_size = snprintf(dst, dst_size, "%s#%d Part%u\n", why,
|
header_size = snprintf(dst, dst_size, "%s#%d Part%u\n", why,
|
||||||
@@ -453,8 +326,8 @@ static void pstore_dump(struct kmsg_dumper *dumper,
|
|||||||
record.compressed = true;
|
record.compressed = true;
|
||||||
record.size = zipped_len;
|
record.size = zipped_len;
|
||||||
} else {
|
} else {
|
||||||
record.size = copy_kmsg_to_buffer(header_size,
|
record.size = header_size + dump_size;
|
||||||
dump_size);
|
memcpy(psinfo->buf, dst, record.size);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
record.size = header_size + dump_size;
|
record.size = header_size + dump_size;
|
||||||
@@ -549,7 +422,7 @@ static int pstore_write_user_compat(struct pstore_record *record,
|
|||||||
if (record->buf)
|
if (record->buf)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
record->buf = memdup_user(buf, record->size);
|
record->buf = vmemdup_user(buf, record->size);
|
||||||
if (IS_ERR(record->buf)) {
|
if (IS_ERR(record->buf)) {
|
||||||
ret = PTR_ERR(record->buf);
|
ret = PTR_ERR(record->buf);
|
||||||
goto out;
|
goto out;
|
||||||
@@ -557,7 +430,7 @@ static int pstore_write_user_compat(struct pstore_record *record,
|
|||||||
|
|
||||||
ret = record->psi->write(record);
|
ret = record->psi->write(record);
|
||||||
|
|
||||||
kfree(record->buf);
|
kvfree(record->buf);
|
||||||
out:
|
out:
|
||||||
record->buf = NULL;
|
record->buf = NULL;
|
||||||
|
|
||||||
@@ -681,7 +554,8 @@ void pstore_unregister(struct pstore_info *psi)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(pstore_unregister);
|
EXPORT_SYMBOL_GPL(pstore_unregister);
|
||||||
|
|
||||||
static void decompress_record(struct pstore_record *record)
|
static void decompress_record(struct pstore_record *record,
|
||||||
|
struct z_stream_s *zstream)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
int unzipped_len;
|
int unzipped_len;
|
||||||
@@ -697,40 +571,50 @@ static void decompress_record(struct pstore_record *record)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Missing compression buffer means compression was not initialized. */
|
/* Missing compression buffer means compression was not initialized. */
|
||||||
if (!big_oops_buf) {
|
if (!zstream->workspace) {
|
||||||
pr_warn("no decompression method initialized!\n");
|
pr_warn("no decompression method initialized!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = zlib_inflateReset(zstream);
|
||||||
|
if (ret != Z_OK) {
|
||||||
|
pr_err("zlib_inflateReset() failed, ret = %d!\n", ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Allocate enough space to hold max decompression and ECC. */
|
/* Allocate enough space to hold max decompression and ECC. */
|
||||||
unzipped_len = big_oops_buf_sz;
|
workspace = kvzalloc(psinfo->bufsize + record->ecc_notice_size,
|
||||||
workspace = kmalloc(unzipped_len + record->ecc_notice_size,
|
GFP_KERNEL);
|
||||||
GFP_KERNEL);
|
|
||||||
if (!workspace)
|
if (!workspace)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* After decompression "unzipped_len" is almost certainly smaller. */
|
zstream->next_in = record->buf;
|
||||||
ret = crypto_comp_decompress(tfm, record->buf, record->size,
|
zstream->avail_in = record->size;
|
||||||
workspace, &unzipped_len);
|
zstream->next_out = workspace;
|
||||||
if (ret) {
|
zstream->avail_out = psinfo->bufsize;
|
||||||
pr_err("crypto_comp_decompress failed, ret = %d!\n", ret);
|
|
||||||
kfree(workspace);
|
ret = zlib_inflate(zstream, Z_FINISH);
|
||||||
|
if (ret != Z_STREAM_END) {
|
||||||
|
pr_err("zlib_inflate() failed, ret = %d!\n", ret);
|
||||||
|
kvfree(workspace);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unzipped_len = zstream->total_out;
|
||||||
|
|
||||||
/* Append ECC notice to decompressed buffer. */
|
/* Append ECC notice to decompressed buffer. */
|
||||||
memcpy(workspace + unzipped_len, record->buf + record->size,
|
memcpy(workspace + unzipped_len, record->buf + record->size,
|
||||||
record->ecc_notice_size);
|
record->ecc_notice_size);
|
||||||
|
|
||||||
/* Copy decompressed contents into an minimum-sized allocation. */
|
/* Copy decompressed contents into an minimum-sized allocation. */
|
||||||
unzipped = kmemdup(workspace, unzipped_len + record->ecc_notice_size,
|
unzipped = kvmemdup(workspace, unzipped_len + record->ecc_notice_size,
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
kfree(workspace);
|
kvfree(workspace);
|
||||||
if (!unzipped)
|
if (!unzipped)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Swap out compressed contents with decompressed contents. */
|
/* Swap out compressed contents with decompressed contents. */
|
||||||
kfree(record->buf);
|
kvfree(record->buf);
|
||||||
record->buf = unzipped;
|
record->buf = unzipped;
|
||||||
record->size = unzipped_len;
|
record->size = unzipped_len;
|
||||||
record->compressed = false;
|
record->compressed = false;
|
||||||
@@ -747,10 +631,17 @@ void pstore_get_backend_records(struct pstore_info *psi,
|
|||||||
{
|
{
|
||||||
int failed = 0;
|
int failed = 0;
|
||||||
unsigned int stop_loop = 65536;
|
unsigned int stop_loop = 65536;
|
||||||
|
struct z_stream_s zstream = {};
|
||||||
|
|
||||||
if (!psi || !root)
|
if (!psi || !root)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (IS_ENABLED(CONFIG_PSTORE_COMPRESS) && compress) {
|
||||||
|
zstream.workspace = kvmalloc(zlib_inflate_workspacesize(),
|
||||||
|
GFP_KERNEL);
|
||||||
|
zlib_inflateInit2(&zstream, -DEF_WBITS);
|
||||||
|
}
|
||||||
|
|
||||||
mutex_lock(&psi->read_mutex);
|
mutex_lock(&psi->read_mutex);
|
||||||
if (psi->open && psi->open(psi))
|
if (psi->open && psi->open(psi))
|
||||||
goto out;
|
goto out;
|
||||||
@@ -779,11 +670,11 @@ void pstore_get_backend_records(struct pstore_info *psi,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
decompress_record(record);
|
decompress_record(record, &zstream);
|
||||||
rc = pstore_mkfile(root, record);
|
rc = pstore_mkfile(root, record);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
/* pstore_mkfile() did not take record, so free it. */
|
/* pstore_mkfile() did not take record, so free it. */
|
||||||
kfree(record->buf);
|
kvfree(record->buf);
|
||||||
kfree(record->priv);
|
kfree(record->priv);
|
||||||
kfree(record);
|
kfree(record);
|
||||||
if (rc != -EEXIST || !quiet)
|
if (rc != -EEXIST || !quiet)
|
||||||
@@ -795,6 +686,12 @@ void pstore_get_backend_records(struct pstore_info *psi,
|
|||||||
out:
|
out:
|
||||||
mutex_unlock(&psi->read_mutex);
|
mutex_unlock(&psi->read_mutex);
|
||||||
|
|
||||||
|
if (IS_ENABLED(CONFIG_PSTORE_COMPRESS) && compress) {
|
||||||
|
if (zlib_inflateEnd(&zstream) != Z_OK)
|
||||||
|
pr_warn("zlib_inflateEnd() failed\n");
|
||||||
|
kvfree(zstream.workspace);
|
||||||
|
}
|
||||||
|
|
||||||
if (failed)
|
if (failed)
|
||||||
pr_warn("failed to create %d record(s) from '%s'\n",
|
pr_warn("failed to create %d record(s) from '%s'\n",
|
||||||
failed, psi->name);
|
failed, psi->name);
|
||||||
@@ -818,34 +715,10 @@ static void pstore_timefunc(struct timer_list *unused)
|
|||||||
pstore_timer_kick();
|
pstore_timer_kick();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __init pstore_choose_compression(void)
|
|
||||||
{
|
|
||||||
const struct pstore_zbackend *step;
|
|
||||||
|
|
||||||
if (!compress)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (step = zbackends; step->name; step++) {
|
|
||||||
if (!strcmp(compress, step->name)) {
|
|
||||||
zbackend = step;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __init pstore_init(void)
|
static int __init pstore_init(void)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
pstore_choose_compression();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check if any pstore backends registered earlier but did not
|
|
||||||
* initialize compression because crypto was not ready. If so,
|
|
||||||
* initialize compression now.
|
|
||||||
*/
|
|
||||||
allocate_buf_for_compression();
|
|
||||||
|
|
||||||
ret = pstore_init_fs();
|
ret = pstore_init_fs();
|
||||||
if (ret)
|
if (ret)
|
||||||
free_buf_for_compression();
|
free_buf_for_compression();
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <linux/compiler.h>
|
#include <linux/compiler.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "ram_internal.h"
|
#include "ram_internal.h"
|
||||||
@@ -268,7 +269,7 @@ static ssize_t ramoops_pstore_read(struct pstore_record *record)
|
|||||||
/* ECC correction notice */
|
/* ECC correction notice */
|
||||||
record->ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0);
|
record->ecc_notice_size = persistent_ram_ecc_string(prz, NULL, 0);
|
||||||
|
|
||||||
record->buf = kmalloc(size + record->ecc_notice_size + 1, GFP_KERNEL);
|
record->buf = kvzalloc(size + record->ecc_notice_size + 1, GFP_KERNEL);
|
||||||
if (record->buf == NULL) {
|
if (record->buf == NULL) {
|
||||||
size = -ENOMEM;
|
size = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
@@ -282,7 +283,7 @@ static ssize_t ramoops_pstore_read(struct pstore_record *record)
|
|||||||
|
|
||||||
out:
|
out:
|
||||||
if (free_prz) {
|
if (free_prz) {
|
||||||
kfree(prz->old_log);
|
kvfree(prz->old_log);
|
||||||
kfree(prz);
|
kfree(prz);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -833,7 +834,7 @@ static int ramoops_probe(struct platform_device *pdev)
|
|||||||
*/
|
*/
|
||||||
if (cxt->pstore.flags & PSTORE_FLAGS_DMESG) {
|
if (cxt->pstore.flags & PSTORE_FLAGS_DMESG) {
|
||||||
cxt->pstore.bufsize = cxt->dprzs[0]->buffer_size;
|
cxt->pstore.bufsize = cxt->dprzs[0]->buffer_size;
|
||||||
cxt->pstore.buf = kzalloc(cxt->pstore.bufsize, GFP_KERNEL);
|
cxt->pstore.buf = kvzalloc(cxt->pstore.bufsize, GFP_KERNEL);
|
||||||
if (!cxt->pstore.buf) {
|
if (!cxt->pstore.buf) {
|
||||||
pr_err("cannot allocate pstore crash dump buffer\n");
|
pr_err("cannot allocate pstore crash dump buffer\n");
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
@@ -866,7 +867,7 @@ static int ramoops_probe(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail_buf:
|
fail_buf:
|
||||||
kfree(cxt->pstore.buf);
|
kvfree(cxt->pstore.buf);
|
||||||
fail_clear:
|
fail_clear:
|
||||||
cxt->pstore.bufsize = 0;
|
cxt->pstore.bufsize = 0;
|
||||||
fail_init:
|
fail_init:
|
||||||
@@ -881,7 +882,7 @@ static void ramoops_remove(struct platform_device *pdev)
|
|||||||
|
|
||||||
pstore_unregister(&cxt->pstore);
|
pstore_unregister(&cxt->pstore);
|
||||||
|
|
||||||
kfree(cxt->pstore.buf);
|
kvfree(cxt->pstore.buf);
|
||||||
cxt->pstore.bufsize = 0;
|
cxt->pstore.bufsize = 0;
|
||||||
|
|
||||||
ramoops_free_przs(cxt);
|
ramoops_free_przs(cxt);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
#include <asm/page.h>
|
#include <asm/page.h>
|
||||||
|
|
||||||
#include "ram_internal.h"
|
#include "ram_internal.h"
|
||||||
@@ -24,12 +25,10 @@
|
|||||||
/**
|
/**
|
||||||
* struct persistent_ram_buffer - persistent circular RAM buffer
|
* struct persistent_ram_buffer - persistent circular RAM buffer
|
||||||
*
|
*
|
||||||
* @sig:
|
* @sig: Signature to indicate header (PERSISTENT_RAM_SIG xor PRZ-type value)
|
||||||
* signature to indicate header (PERSISTENT_RAM_SIG xor PRZ-type value)
|
* @start: First valid byte in the buffer.
|
||||||
* @start:
|
* @size: Number of valid bytes in the buffer.
|
||||||
* offset into @data where the beginning of the stored bytes begin
|
* @data: The contents of the buffer.
|
||||||
* @size:
|
|
||||||
* number of valid bytes stored in @data
|
|
||||||
*/
|
*/
|
||||||
struct persistent_ram_buffer {
|
struct persistent_ram_buffer {
|
||||||
uint32_t sig;
|
uint32_t sig;
|
||||||
@@ -301,7 +300,7 @@ void persistent_ram_save_old(struct persistent_ram_zone *prz)
|
|||||||
|
|
||||||
if (!prz->old_log) {
|
if (!prz->old_log) {
|
||||||
persistent_ram_ecc_old(prz);
|
persistent_ram_ecc_old(prz);
|
||||||
prz->old_log = kmalloc(size, GFP_KERNEL);
|
prz->old_log = kvzalloc(size, GFP_KERNEL);
|
||||||
}
|
}
|
||||||
if (!prz->old_log) {
|
if (!prz->old_log) {
|
||||||
pr_err("failed to allocate buffer\n");
|
pr_err("failed to allocate buffer\n");
|
||||||
@@ -385,7 +384,7 @@ void *persistent_ram_old(struct persistent_ram_zone *prz)
|
|||||||
|
|
||||||
void persistent_ram_free_old(struct persistent_ram_zone *prz)
|
void persistent_ram_free_old(struct persistent_ram_zone *prz)
|
||||||
{
|
{
|
||||||
kfree(prz->old_log);
|
kvfree(prz->old_log);
|
||||||
prz->old_log = NULL;
|
prz->old_log = NULL;
|
||||||
prz->old_log_size = 0;
|
prz->old_log_size = 0;
|
||||||
}
|
}
|
||||||
@@ -519,7 +518,7 @@ static int persistent_ram_post_init(struct persistent_ram_zone *prz, u32 sig,
|
|||||||
sig ^= PERSISTENT_RAM_SIG;
|
sig ^= PERSISTENT_RAM_SIG;
|
||||||
|
|
||||||
if (prz->buffer->sig == sig) {
|
if (prz->buffer->sig == sig) {
|
||||||
if (buffer_size(prz) == 0) {
|
if (buffer_size(prz) == 0 && buffer_start(prz) == 0) {
|
||||||
pr_debug("found existing empty buffer\n");
|
pr_debug("found existing empty buffer\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user