[5/5] pstore: Use zstd directly by default for compression

Message ID 20221018020815.2872331-5-keescook@chromium.org
State New
Headers
Series pstore: Use zstd directly by default for compression |

Commit Message

Kees Cook Oct. 18, 2022, 2:08 a.m. UTC
  If compression is desired, use zstd directly to avoid Crypto API
overhead.

Cc: Tony Luck <tony.luck@intel.com>
Cc: "Guilherme G. Piccoli" <gpiccoli@igalia.com>
Cc: Nick Terrell <terrelln@fb.com>
Cc: linux-hardening@vger.kernel.org
Signed-off-by: Kees Cook <keescook@chromium.org>
---
 fs/pstore/Kconfig    | 11 +++++-
 fs/pstore/platform.c | 93 +++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 97 insertions(+), 7 deletions(-)
  

Patch

diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig
index a95b3981cb0e..1f05312c7479 100644
--- a/fs/pstore/Kconfig
+++ b/fs/pstore/Kconfig
@@ -25,7 +25,7 @@  config PSTORE_DEFAULT_KMSG_BYTES
 choice
 	prompt "Panic dump compression"
 	depends on PSTORE
-	default PSTORE_COMPRESS_CRYPTO
+	default PSTORE_COMPRESS
 	help
 	  Choose whether and how to compress the panic dump output. This
 	  is usually only needed for very storage-constrained backends.
@@ -38,6 +38,15 @@  choice
 		  available to from the Crypto API. Note that this may reserve
 		  non-trivial amounts of per-CPU memory.
 
+	config PSTORE_COMPRESS
+		bool "Use recommended best compression algorithm"
+		select CRYPTO_ZSTD
+		help
+		  Use the compression routines currently deemed best suited
+		  for panic dump compression. Currently, this is "zstd".
+		  As this compression is used directly through its library
+		  interface, no per-CPU memory is allocated by the Crypto API.
+
 	config PSTORE_COMPRESS_NONE
 		bool "Do not compress panic dumps"
 		help
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index 4d883dc2e8a7..51d2801fc880 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -23,6 +23,7 @@ 
 #include <linux/uaccess.h>
 #include <linux/jiffies.h>
 #include <linux/workqueue.h>
+#include <linux/zstd.h>
 
 #include "internal.h"
 
@@ -72,13 +73,17 @@  module_param(backend, charp, 0444);
 MODULE_PARM_DESC(backend, "specific backend to use");
 
 static char *compress __ro_after_init =
-#ifdef CONFIG_PSTORE_COMPRESS_CRYPTO_DEFAULT
-		CONFIG_PSTORE_COMPRESS_CRYPTO_DEFAULT;
+#ifdef CONFIG_PSTORE_COMPRESS
+		"zstd";
 #else
-		NULL;
-#endif
+# ifdef CONFIG_PSTORE_COMPRESS_CRYPTO_DEFAULT
+		CONFIG_PSTORE_COMPRESS_CRYPTO_DEFAULT;
 module_param(compress, charp, 0444);
 MODULE_PARM_DESC(compress, "compression to use");
+# else
+		NULL;
+# endif
+#endif
 
 /* How much of the kernel log to snapshot */
 unsigned long kmsg_bytes = CONFIG_PSTORE_DEFAULT_KMSG_BYTES;
@@ -88,6 +93,12 @@  MODULE_PARM_DESC(kmsg_bytes, "amount of kernel log to snapshot (in bytes)");
 /* Compression parameters */
 static struct crypto_comp *tfm;
 
+static zstd_cctx *cctx;
+static zstd_dctx *dctx;
+static void *cwksp;
+static void *dwksp;
+zstd_parameters zparams;
+
 static char *big_oops_buf;
 static size_t big_oops_buf_sz;
 
@@ -175,6 +186,14 @@  static int pstore_compress(const void *in, void *out,
 		return 0;
 	}
 
+	if (IS_ENABLED(CONFIG_PSTORE_COMPRESS)) {
+		*outlen = zstd_compress_cctx(cctx, out, *outlen, in, inlen,
+					    &zparams);
+		if (zstd_is_error(*outlen))
+			return -EINVAL;
+		return 0;
+	}
+
 	return -EINVAL;
 }
 
@@ -203,12 +222,56 @@  static int allocate_crypto_buf(void)
 	return 0;
 }
 
+static int allocate_zstd_buf(void)
+{
+	size_t csize, dsize;
+
+	/* Skip if compression init already done. */
+	if (cctx)
+		return 0;
+
+	zparams = zstd_get_params(3, 0);
+	csize = zstd_cctx_workspace_bound(&zparams.cParams);
+	dsize = zstd_dctx_workspace_bound();
+
+#define init_ctx(dir, name)	do {					\
+	dir##wksp = kzalloc(dir##size, GFP_KERNEL);			\
+	if (!dir##wksp) {						\
+		pr_err("Failed %zu byte %s " #name " allocation\n",	\
+		       dir##size, compress);				\
+		return -ENOMEM;						\
+	}								\
+	dir##ctx = zstd_init_##dir##ctx(dir##wksp, dir##size);		\
+	if (!dir##ctx) {						\
+		pr_err("Failed %s " #name " context init\n", compress);	\
+		return -EINVAL;						\
+	}								\
+} while (0)
+
+	init_ctx(c, compress);
+	init_ctx(d, decompress);
+
+#undef init_wksp
+
+	pr_info("Using crash dump compression: built-in %s\n", compress);
+	return 0;
+}
+
 static void free_buf_for_compression(void)
 {
 	if (IS_ENABLED(CONFIG_PSTORE_COMPRESS_CRYPTO) && tfm) {
 		crypto_free_comp(tfm);
 		tfm = NULL;
 	}
+	if (IS_ENABLED(CONFIG_PSTORE_COMPRESS) && cctx) {
+		cctx = NULL;
+		dctx = NULL;
+		kfree(cwksp);
+		cwksp = NULL;
+		kfree(dwksp);
+		dwksp = NULL;
+
+	}
 	kfree(big_oops_buf);
 	big_oops_buf = NULL;
 	big_oops_buf_sz = 0;
@@ -228,7 +291,10 @@  static void allocate_buf_for_compression(void)
 		return;
 
 	/* Initialize compression routines. */
-	rc = allocate_crypto_buf();
+	if (IS_ENABLED(CONFIG_PSTORE_COMPRESS_CRYPTO))
+		rc = allocate_crypto_buf();
+	else
+		rc = allocate_zstd_buf();
 	if (rc)
 		goto fail;
 
@@ -598,6 +664,16 @@  static int pstore_decompress_crypto(struct pstore_record *record, char *workspac
 	return 0;
 }
 
+static int pstore_decompress_zstd(struct pstore_record *record,
+				  char *workspace, size_t *outlen)
+{
+	*outlen = zstd_decompress_dctx(dctx, workspace, *outlen,
+				       record->buf, record->size);
+	if (zstd_is_error(*outlen))
+		return -EINVAL;
+	return 0;
+}
+
 static void decompress_record(struct pstore_record *record)
 {
 	size_t unzipped_len;
@@ -626,7 +702,12 @@  static void decompress_record(struct pstore_record *record)
 	if (!workspace)
 		return;
 
-	rc = pstore_decompress_crypto(record, workspace, &unzipped_len);
+	if (IS_ENABLED(CONFIG_PSTORE_COMPRESS_CRYPTO))
+		rc = pstore_decompress_crypto(record, workspace,
+					      &unzipped_len);
+	else
+		rc = pstore_decompress_zstd(record, workspace,
+					    &unzipped_len);
 	if (rc) {
 		kfree(workspace);
 		return;