Commit 69824bcc authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull pstore updates from Kees Cook:
 "This cycle was almost entirely improvements to the pstore compression
  options, noted below:

   - Add lz4hc and 842 to pstore compression options (Geliang Tang)

   - Refactor to use crypto compression API (Geliang Tang)

   - Fix up Kconfig dependencies for compression (Arnd Bergmann)

   - Allow for run-time compression selection

   - Remove stack VLA usage"

* tag 'pstore-v4.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
  pstore: fix crypto dependencies
  pstore: Use crypto compress API
  pstore/ram: Do not use stack VLA for parity workspace
  pstore: Select compression at runtime
  pstore: Avoid size casts for 842 compression
  pstore: Add lz4hc and 842 compression support
parents 3b54765c 58eb5b67
Loading
Loading
Loading
Loading
+78 −23
Original line number Diff line number Diff line
config PSTORE
	tristate "Persistent store support"
	select CRYPTO if PSTORE_COMPRESS
	default n
	help
	   This option enables generic access to platform level
@@ -12,35 +13,89 @@ config PSTORE
	   If you don't have a platform persistent store driver,
	   say N.

choice
        prompt "Choose compression algorithm"
config PSTORE_DEFLATE_COMPRESS
	tristate "DEFLATE (ZLIB) compression"
	default y
	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
	select CRYPTO_DEFLATE
	help
          This option enables ZLIB compression algorithm support.
	  This option enables DEFLATE (also known as ZLIB) compression
	  algorithm support.

config PSTORE_LZO_COMPRESS
        bool "LZO"
        select LZO_COMPRESS
        select LZO_DECOMPRESS
	tristate "LZO compression"
	depends on PSTORE
	select CRYPTO_LZO
	help
	  This option enables LZO compression algorithm support.

config PSTORE_LZ4_COMPRESS
        bool "LZ4"
        select LZ4_COMPRESS
        select LZ4_DECOMPRESS
	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_COMPRESS
	def_bool y
	depends on PSTORE
	depends on PSTORE_DEFLATE_COMPRESS || PSTORE_LZO_COMPRESS ||	\
		   PSTORE_LZ4_COMPRESS || PSTORE_LZ4HC_COMPRESS ||	\
		   PSTORE_842_COMPRESS

choice
	prompt "Default pstore compression algorithm"
	depends on PSTORE_COMPRESS
	help
	  This option chooses the default active compression algorithm.
	  This change be changed at boot with "pstore.compress=..." on
	  the kernel command line.

	  Currently, pstore has support for 5 compression algorithms:
	  deflate, lzo, lz4, lz4hc and 842.

	  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

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

config PSTORE_CONSOLE
	bool "Log kernel console messages"
	depends on PSTORE
+2 −0
Original line number Diff line number Diff line
@@ -486,6 +486,8 @@ static int __init init_pstore_fs(void)
{
	int err;

	pstore_choose_compression();

	/* Create a convenient mount point for people to access pstore */
	err = sysfs_create_mount_point(fs_kobj, "pstore");
	if (err)
+3 −0
Original line number Diff line number Diff line
@@ -37,4 +37,7 @@ extern bool pstore_is_mounted(void);
extern void	pstore_record_init(struct pstore_record *record,
				   struct pstore_info *psi);

/* Called during module_init() */
extern void __init pstore_choose_compression(void);

#endif
+119 −253
Original line number Diff line number Diff line
@@ -28,15 +28,13 @@
#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
#if IS_ENABLED(CONFIG_PSTORE_LZO_COMPRESS)
#include <linux/lzo.h>
#endif
#ifdef CONFIG_PSTORE_LZ4_COMPRESS
#if IS_ENABLED(CONFIG_PSTORE_LZ4_COMPRESS) || IS_ENABLED(CONFIG_PSTORE_LZ4HC_COMPRESS)
#include <linux/lz4.h>
#endif
#include <linux/crypto.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/slab.h>
@@ -74,23 +72,18 @@ static DEFINE_SPINLOCK(pstore_lock);
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;
static char *compress =
#ifdef CONFIG_PSTORE_COMPRESS_DEFAULT
		CONFIG_PSTORE_COMPRESS_DEFAULT;
#else
static unsigned char *workspace;
		NULL;
#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);
/* Compression parameters */
static struct crypto_comp *tfm;

struct pstore_zbackend {
	int (*zbufsize)(size_t size);
	const char *name;
};

@@ -149,77 +142,12 @@ 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 compress_zlib(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 decompress_zlib(void *in, void *out, size_t inlen, size_t outlen)
#if IS_ENABLED(CONFIG_PSTORE_DEFLATE_COMPRESS)
static int zbufsize_deflate(size_t size)
{
	int err, ret;

	ret = -EIO;
	err = zlib_inflateInit2(&stream, WINDOW_BITS);
	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_zlib(void)
{
	size_t size;
	size_t cmpr;

	switch (psinfo->bufsize) {
	switch (size) {
	/* buffer range for efivars */
	case 1000 ... 2000:
		cmpr = 56;
@@ -239,212 +167,131 @@ static void allocate_zlib(void)
		break;
	}

	big_oops_buf_sz = (psinfo->bufsize * 100) / cmpr;
	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("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;
	}

	return (size * 100) / cmpr;
}
#endif

static void free_zlib(void)
#if IS_ENABLED(CONFIG_PSTORE_LZO_COMPRESS)
static int zbufsize_lzo(size_t size)
{
	kfree(stream.workspace);
	stream.workspace = NULL;
	kfree(big_oops_buf);
	big_oops_buf = NULL;
	big_oops_buf_sz = 0;
	return lzo1x_worst_compress(size);
}

static const 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)
#if IS_ENABLED(CONFIG_PSTORE_LZ4_COMPRESS) || IS_ENABLED(CONFIG_PSTORE_LZ4HC_COMPRESS)
static int zbufsize_lz4(size_t size)
{
	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;
	return LZ4_compressBound(size);
}
#endif

static int decompress_lzo(void *in, void *out, size_t inlen, size_t outlen)
#if IS_ENABLED(CONFIG_PSTORE_842_COMPRESS)
static int zbufsize_842(size_t size)
{
	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 size;
}
#endif

	return outlen;
}
static const struct pstore_zbackend *zbackend __ro_after_init;

static void allocate_lzo(void)
static const struct pstore_zbackend zbackends[] = {
#if IS_ENABLED(CONFIG_PSTORE_DEFLATE_COMPRESS)
	{
	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)
		.zbufsize	= zbufsize_deflate,
		.name		= "deflate",
	},
#endif
#if IS_ENABLED(CONFIG_PSTORE_LZO_COMPRESS)
	{
	kfree(workspace);
	kfree(big_oops_buf);
	big_oops_buf = NULL;
	big_oops_buf_sz = 0;
}

static const struct pstore_zbackend backend_lzo = {
	.compress	= compress_lzo,
	.decompress	= decompress_lzo,
	.allocate	= allocate_lzo,
	.free		= free_lzo,
		.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
	{ }
};

#ifdef CONFIG_PSTORE_LZ4_COMPRESS
static int compress_lz4(const void *in, void *out, size_t inlen, size_t outlen)
static int pstore_compress(const void *in, void *out,
			   unsigned int inlen, unsigned int outlen)
{
	int ret;

	ret = LZ4_compress_default(in, out, inlen, outlen, workspace);
	if (!ret) {
		pr_err("LZ4_compress_default error; compression failed!\n");
		return -EIO;
	ret = crypto_comp_compress(tfm, in, inlen, out, &outlen);
	if (ret) {
		pr_err("crypto_comp_compress failed, ret = %d!\n", ret);
		return ret;
	}

	return ret;
	return outlen;
}

static int decompress_lz4(void *in, void *out, size_t inlen, size_t outlen)
static int pstore_decompress(void *in, void *out,
			     unsigned int inlen, unsigned int outlen)
{
	int ret;

	ret = LZ4_decompress_safe(in, out, inlen, outlen);
	if (ret < 0) {
		/*
		 * LZ4_decompress_safe will return an error code
		 * (< 0) if decompression failed
		 */
		pr_err("LZ4_decompress_safe error, ret = %d!\n", ret);
		return -EIO;
	}

	ret = crypto_comp_decompress(tfm, in, inlen, out, &outlen);
	if (ret) {
		pr_err("crypto_comp_decompress failed, ret = %d!\n", ret);
		return ret;
	}

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;
	}
	return outlen;
}

static void free_lz4(void)
static void allocate_buf_for_compression(void)
{
	kfree(workspace);
	kfree(big_oops_buf);
	big_oops_buf = NULL;
	big_oops_buf_sz = 0;
}

static const struct pstore_zbackend backend_lz4 = {
	.compress	= compress_lz4,
	.decompress	= decompress_lz4,
	.allocate	= allocate_lz4,
	.free		= free_lz4,
	.name		= "lz4",
};
#endif

static const 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
	if (!zbackend)
		return;

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;
	if (!crypto_has_comp(zbackend->name, 0, 0)) {
		pr_err("No %s compression\n", zbackend->name);
		return;
	}

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;
}
	big_oops_buf_sz = zbackend->zbufsize(psinfo->bufsize);
	if (big_oops_buf_sz <= 0)
		return;

static void allocate_buf_for_compression(void)
{
	if (zbackend) {
		pr_info("using %s compression\n", zbackend->name);
		zbackend->allocate();
	} else {
	big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL);
	if (!big_oops_buf) {
		pr_err("allocate compression buffer error!\n");
		return;
	}

	tfm = crypto_alloc_comp(zbackend->name, 0, 0);
	if (IS_ERR_OR_NULL(tfm)) {
		kfree(big_oops_buf);
		big_oops_buf = NULL;
		pr_err("crypto_alloc_comp() failed!\n");
		return;
	}
}

static void free_buf_for_compression(void)
{
	if (zbackend)
		zbackend->free();
	else
		pr_err("free compression buffer error!\n");
	if (!IS_ERR_OR_NULL(tfm))
		crypto_free_comp(tfm);
	kfree(big_oops_buf);
	big_oops_buf = NULL;
	big_oops_buf_sz = 0;
}

/*
@@ -901,5 +748,24 @@ static void pstore_timefunc(struct timer_list *unused)
			  jiffies + msecs_to_jiffies(pstore_update_ms));
}

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;
			pr_info("using %s compression\n", zbackend->name);
			return;
		}
	}
}

module_param(compress, charp, 0444);
MODULE_PARM_DESC(compress, "Pstore compression to use");

module_param(backend, charp, 0444);
MODULE_PARM_DESC(backend, "Pstore backend to use");
+1 −1
Original line number Diff line number Diff line
@@ -938,7 +938,7 @@ static int __init ramoops_init(void)
	ramoops_register_dummy();
	return platform_driver_register(&ramoops_driver);
}
postcore_initcall(ramoops_init);
late_initcall(ramoops_init);

static void __exit ramoops_exit(void)
{
Loading