2024-05-15 07:19:55

by Sergey Senozhatsky

[permalink] [raw]
Subject: [PATCHv4 00/21] zram: custom comp API and comp algorithms tunables


---8<---8<---
This series contains important patches which we either can apply
as is (if zram makes a switch to compression backends) or cherry-pick
(if zram sticks to crypto API), as compression drivers/backends still
need to be patches regardless of direction zram takes. Sending it out
so that the patches don't get lost.
---8<---8<---

This patch set moves zram from crypto API to a custom compression
API which allows us to tune and configure compression algorithms,
something that crypto API, unfortunately, doesn't support yet.
Basically, this series brings back the bits of compression "backends"
code that we had many years ago.

Currently, zram supports a pretty decent number of comp backends:
lzo, lzorle, lz4, lz4hc, 842, deflate, zstd

At this point we handle 2 parameters: a compression level and
a pre-trained compression dictionary. Which seems like a good enough
start. The list will be extended in the future.

Examples:

- tunes compression level
echo "algo=zstd level=11" > /sys/block/zram0/comp_algorithm

- uses a pre-trained dictionary and tunes compression level
echo "algo=zstd level=11 dict=/etc/dictionary" > /sys/block/zram0/comp_algorithm

Benchmarks
==========

*** zstd
/sys/block/zram0/mm_stat
1750302720 504600204 514416640 0 514416640 1 0 34204 34204

*** zstd level=5
/sys/block/zram0/mm_stat
1750331392 488449001 497905664 0 497905664 1 0 34204 34204

*** zstd dict=/etc/dictionary
/sys/block/zram0/mm_stat
1750335488 464838883 474210304 0 474210304 1 0 34204 34204

*** zstd level=5 dict=/etc/dictionary
/sys/block/zram0/mm_stat
1750319104 451907185 461299712 0 461299712 1 0 34204 34204

*** lz4
/sys/block/zram0/mm_stat
1750319104 664253635 676859904 0 676859904 1 0 34288 34288

*** lz4 dict=/etc/dictionary
/sys/block/zram0/mm_stat
1750319104 620602911 632705024 0 632705024 1 0 34288 34288

*** lz4hc
/sys/block/zram0/mm_stat
1750315008 609004936 621092864 0 621092864 1 0 34288 34288

*** lz4hc level=5 dict=/etc/dictionary
/sys/block/zram0/mm_stat
1750323200 501315128 511303680 0 511303680 1 0 34288 34288


v4:
-- fixed ZRAM=m build error (kernel test robot @intel)
-- added compression level tunable to lz4
-- export lz4hc symbol needed for dictionary support
-- export advanced zstd API needed for dictionary support

Sergey Senozhatsky (21):
zram: move from crypto API to custom comp backends API
zram: add lzo and lzorle compression backends support
zram: add lz4 compression backend support
zram: add lz4hc compression backend support
zram: add zstd compression backend support
zram: pass estimated src size hint to zstd
zram: add zlib compression backend support
zram: add 842 compression backend support
zram: check that backends array has at least one backend
zram: introduce zcomp_config structure
zram: extend comp_algorithm attr write handling
zram: support compression level comp config
zram: add support for dict comp config
lib/zstd: export API needed for dictionary support
zram: add dictionary support to zstd backend
zram: add config init/release backend callbacks
zram: share dictionaries between per-CPU contexts
zram: add dictionary support to lz4
lib/lz4hc: export LZ4_resetStreamHC symbol
zram: add dictionary support to lz4hc
Documentation/zram: add documentation for algorithm parameters

Documentation/admin-guide/blockdev/zram.rst | 38 +++-
drivers/block/zram/Kconfig | 76 +++++--
drivers/block/zram/Makefile | 8 +
drivers/block/zram/backend_842.c | 79 +++++++
drivers/block/zram/backend_842.h | 10 +
drivers/block/zram/backend_deflate.c | 143 +++++++++++++
drivers/block/zram/backend_deflate.h | 10 +
drivers/block/zram/backend_lz4.c | 128 +++++++++++
drivers/block/zram/backend_lz4.h | 10 +
drivers/block/zram/backend_lz4hc.c | 128 +++++++++++
drivers/block/zram/backend_lz4hc.h | 10 +
drivers/block/zram/backend_lzo.c | 55 +++++
drivers/block/zram/backend_lzo.h | 10 +
drivers/block/zram/backend_lzorle.c | 55 +++++
drivers/block/zram/backend_lzorle.h | 10 +
drivers/block/zram/backend_zstd.c | 223 ++++++++++++++++++++
drivers/block/zram/backend_zstd.h | 10 +
drivers/block/zram/zcomp.c | 155 +++++++-------
drivers/block/zram/zcomp.h | 42 +++-
drivers/block/zram/zram_drv.c | 120 ++++++++++-
drivers/block/zram/zram_drv.h | 1 +
include/linux/zstd.h | 165 +++++++++++++++
lib/lz4/lz4hc_compress.c | 1 +
lib/zstd/zstd_compress_module.c | 52 +++++
lib/zstd/zstd_decompress_module.c | 38 ++++
25 files changed, 1466 insertions(+), 111 deletions(-)
create mode 100644 drivers/block/zram/backend_842.c
create mode 100644 drivers/block/zram/backend_842.h
create mode 100644 drivers/block/zram/backend_deflate.c
create mode 100644 drivers/block/zram/backend_deflate.h
create mode 100644 drivers/block/zram/backend_lz4.c
create mode 100644 drivers/block/zram/backend_lz4.h
create mode 100644 drivers/block/zram/backend_lz4hc.c
create mode 100644 drivers/block/zram/backend_lz4hc.h
create mode 100644 drivers/block/zram/backend_lzo.c
create mode 100644 drivers/block/zram/backend_lzo.h
create mode 100644 drivers/block/zram/backend_lzorle.c
create mode 100644 drivers/block/zram/backend_lzorle.h
create mode 100644 drivers/block/zram/backend_zstd.c
create mode 100644 drivers/block/zram/backend_zstd.h

--
2.45.0.rc1.225.g2a3ae87e7f-goog



2024-05-15 07:20:02

by Sergey Senozhatsky

[permalink] [raw]
Subject: [PATCHv4 11/21] zram: extend comp_algorithm attr write handling

Previously comp_algorithm device attr would accept only
algorithm name param, however in order to enabled comp
configuration we need to extend comp_algorithm_store()
with param=value support.

Signed-off-by: Sergey Senozhatsky <[email protected]>
---
drivers/block/zram/zram_drv.c | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 75ecdb885d91..7eb500c8fdaa 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -1013,9 +1013,33 @@ static ssize_t comp_algorithm_store(struct device *dev,
size_t len)
{
struct zram *zram = dev_to_zram(dev);
+ char *args, *param, *val;
+ char *alg = NULL;
int ret;

- ret = __comp_algorithm_store(zram, ZRAM_PRIMARY_COMP, buf);
+ args = skip_spaces(buf);
+ while (*args) {
+ args = next_arg(args, &param, &val);
+
+ /*
+ * We need to support 'param' without value, which is an
+ * old format for this attr (algorithm name only).
+ */
+ if (!val || !*val) {
+ alg = param;
+ continue;
+ }
+
+ if (!strcmp(param, "algo")) {
+ alg = val;
+ continue;
+ }
+ }
+
+ if (!alg)
+ return -EINVAL;
+
+ ret = __comp_algorithm_store(zram, ZRAM_PRIMARY_COMP, alg);
return ret ? ret : len;
}

--
2.45.0.rc1.225.g2a3ae87e7f-goog


2024-05-15 07:20:43

by Sergey Senozhatsky

[permalink] [raw]
Subject: [PATCHv4 13/21] zram: add support for dict comp config

Handle dict=path param so that we can read a pre-trained
compression algorithm dictionary which we then pass to the
backend configuration.

Signed-off-by: Sergey Senozhatsky <[email protected]>
---
drivers/block/zram/zram_drv.c | 54 ++++++++++++++++++++++++++++-------
1 file changed, 43 insertions(+), 11 deletions(-)

diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 91706889b63f..216686a5f3f0 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -33,6 +33,7 @@
#include <linux/debugfs.h>
#include <linux/cpuhotplug.h>
#include <linux/part_stat.h>
+#include <linux/kernel_read_file.h>

#include "zram_drv.h"

@@ -998,9 +999,35 @@ static int __comp_algorithm_store(struct zram *zram, u32 prio, const char *buf)
return 0;
}

-static int comp_config_store(struct zram *zram, u32 prio, s32 level)
+static void __reset_comp_config(struct zram *zram, u32 prio)
{
+ struct zcomp_config *config = &zram->configs[prio];
+
+ vfree(config->dict);
+ config->level = ZCOMP_CONFIG_NO_LEVEL;
+ config->dict_sz = 0;
+ config->dict = NULL;
+}
+
+static int comp_config_store(struct zram *zram, u32 prio, s32 level,
+ const char *dict_path)
+{
+ ssize_t sz = 0;
+
+ __reset_comp_config(zram, prio);
+
+ if (dict_path) {
+ sz = kernel_read_file_from_path(dict_path, 0,
+ &zram->configs[prio].dict,
+ INT_MAX,
+ NULL,
+ READING_POLICY);
+ if (sz < 0)
+ return -EINVAL;
+ }
+
zram->configs[prio].level = level;
+ zram->configs[prio].dict_sz = sz;
return 0;
}

@@ -1020,7 +1047,7 @@ static ssize_t comp_algorithm_store(struct device *dev,
{
struct zram *zram = dev_to_zram(dev);
char *args, *param, *val;
- char *alg = NULL;
+ char *alg = NULL, *dict_path = NULL;
s32 level = ZCOMP_CONFIG_NO_LEVEL;
int ret;

@@ -1048,12 +1075,17 @@ static ssize_t comp_algorithm_store(struct device *dev,
return ret;
continue;
}
+
+ if (!strcmp(param, "dict")) {
+ dict_path = val;
+ continue;
+ }
}

if (!alg)
return -EINVAL;

- ret = comp_config_store(zram, ZRAM_PRIMARY_COMP, level);
+ ret = comp_config_store(zram, ZRAM_PRIMARY_COMP, level, dict_path);
if (!ret)
ret = __comp_algorithm_store(zram, ZRAM_PRIMARY_COMP, alg);
return ret ? ret : len;
@@ -1087,7 +1119,7 @@ static ssize_t recomp_algorithm_store(struct device *dev,
struct zram *zram = dev_to_zram(dev);
int prio = ZRAM_SECONDARY_COMP;
char *args, *param, *val;
- char *alg = NULL;
+ char *alg = NULL, *dict_path = NULL;
s32 level = ZCOMP_CONFIG_NO_LEVEL;
int ret;

@@ -1116,6 +1148,11 @@ static ssize_t recomp_algorithm_store(struct device *dev,
return ret;
continue;
}
+
+ if (!strcmp(param, "dict")) {
+ dict_path = val;
+ continue;
+ }
}

if (!alg)
@@ -1124,7 +1161,7 @@ static ssize_t recomp_algorithm_store(struct device *dev,
if (prio < ZRAM_SECONDARY_COMP || prio >= ZRAM_MAX_COMPS)
return -EINVAL;

- ret = comp_config_store(zram, prio, level);
+ ret = comp_config_store(zram, prio, level, dict_path);
if (!ret)
ret = __comp_algorithm_store(zram, prio, alg);
return ret ? ret : len;
@@ -2034,12 +2071,7 @@ static void zram_reset_comp_configs(struct zram *zram)
u32 prio;

for (prio = 0; prio < ZRAM_MAX_COMPS; prio++) {
- struct zcomp_config *config = &zram->configs[prio];
-
- vfree(config->dict);
- config->level = ZCOMP_CONFIG_NO_LEVEL;
- config->dict_sz = 0;
- config->dict = NULL;
+ __reset_comp_config(zram, prio);
}
}

--
2.45.0.rc1.225.g2a3ae87e7f-goog


2024-05-15 07:21:01

by Sergey Senozhatsky

[permalink] [raw]
Subject: [PATCHv4 14/21] lib/zstd: export API needed for dictionary support

We need to export a number of API functions that enable
advanced zstd usage - C/D dictionaries, dictionaries
sharing between contexts, etc.

Signed-off-by: Sergey Senozhatsky <[email protected]>
Cc: Nick Terrell <[email protected]>
---
include/linux/zstd.h | 165 ++++++++++++++++++++++++++++++
lib/zstd/zstd_compress_module.c | 52 ++++++++++
lib/zstd/zstd_decompress_module.c | 38 +++++++
3 files changed, 255 insertions(+)

diff --git a/include/linux/zstd.h b/include/linux/zstd.h
index f109d49f43f8..18260a354231 100644
--- a/include/linux/zstd.h
+++ b/include/linux/zstd.h
@@ -77,6 +77,30 @@ int zstd_min_clevel(void);
*/
int zstd_max_clevel(void);

+/**
+ * zstd_default_clevel() - default compression level
+ *
+ * Return: Default compression level.
+ */
+int zstd_default_clevel(void);
+
+/**
+ * struct zstd_custom_mem - custom memory allocation
+ */
+typedef ZSTD_customMem zstd_custom_mem;
+
+/**
+ * struct zstd_dict_load_method - Dictionary load method.
+ * See zstd_lib.h.
+ */
+typedef ZSTD_dictLoadMethod_e zstd_dict_load_method;
+
+/**
+ * struct zstd_dict_content_type - Dictionary context type.
+ * See zstd_lib.h.
+ */
+typedef ZSTD_dictContentType_e zstd_dict_content_type;
+
/* ====== Parameter Selection ====== */

/**
@@ -136,6 +160,19 @@ typedef ZSTD_parameters zstd_parameters;
zstd_parameters zstd_get_params(int level,
unsigned long long estimated_src_size);

+
+/**
+ * zstd_get_cparams() - returns zstd_compression_parameters for selected level
+ * @level: The compression level
+ * @estimated_src_size: The estimated source size to compress or 0
+ * if unknown.
+ * @dict_size: Dictionary size.
+ *
+ * Return: The selected zstd_compression_parameters.
+ */
+zstd_compression_parameters zstd_get_cparams(int level,
+ unsigned long long estimated_src_size, size_t dict_size);
+
/* ====== Single-pass Compression ====== */

typedef ZSTD_CCtx zstd_cctx;
@@ -180,6 +217,70 @@ zstd_cctx *zstd_init_cctx(void *workspace, size_t workspace_size);
size_t zstd_compress_cctx(zstd_cctx *cctx, void *dst, size_t dst_capacity,
const void *src, size_t src_size, const zstd_parameters *parameters);

+/**
+ * zstd_create_cctx_advanced() - Create compression context
+ * @custom_mem: Custom allocator.
+ *
+ * Return: NULL on error, pointer to compression context otherwise.
+ */
+zstd_cctx *zstd_create_cctx_advanced(zstd_custom_mem custom_mem);
+
+/**
+ * zstd_free_cctx() - Free compression context
+ * @cdict: Pointer to compression context.
+ *
+ * Return: Always 0.
+ */
+size_t zstd_free_cctx(zstd_cctx* cctx);
+
+/**
+ * struct zstd_cdict - Compression dictionary.
+ * See zstd_lib.h.
+ */
+typedef ZSTD_CDict zstd_cdict;
+
+/**
+ * zstd_create_cdict_advanced() - Create compression dictionary
+ * @dict: Pointer to dictionary buffer.
+ * @dict_size: Size of the dictionary buffer.
+ * @dict_load_method: Dictionary load method.
+ * @dict_content_type: Dictionary content type.
+ * @custom_mem: Memory allocator.
+ *
+ * Return: NULL on error, pointer to compression dictionary
+ * otherwise.
+ */
+zstd_cdict *zstd_create_cdict_advanced(const void *dict, size_t dict_size,
+ zstd_dict_load_method dict_load_method,
+ zstd_dict_content_type dict_content_type,
+ zstd_compression_parameters cparams,
+ zstd_custom_mem custom_mem);
+
+/**
+ * zstd_free_cdict() - Free compression dictionary
+ * @cdict: Pointer to compression dictionary.
+ *
+ * Return: Always 0.
+ */
+size_t zstd_free_cdict(zstd_cdict* cdict);
+
+/**
+ * zstd_compress_using_cdict() - compress src into dst using a dictionary
+ * @cctx: The context. Must have been initialized with zstd_init_cctx().
+ * @dst: The buffer to compress src into.
+ * @dst_capacity: The size of the destination buffer. May be any size, but
+ * ZSTD_compressBound(srcSize) is guaranteed to be large enough.
+ * @src: The data to compress.
+ * @src_size: The size of the data to compress.
+ * @cdict: The dictionary to be used.
+ *
+ * Return: The compressed size or an error, which can be checked using
+ * zstd_is_error().
+ */
+size_t zstd_compress_using_cdict(zstd_cctx *cctx, void *dst,
+ size_t dst_capacity, const void *src, size_t src_size,
+ const zstd_cdict *cdict);
+
/* ====== Single-pass Decompression ====== */

typedef ZSTD_DCtx zstd_dctx;
@@ -220,6 +321,70 @@ zstd_dctx *zstd_init_dctx(void *workspace, size_t workspace_size);
size_t zstd_decompress_dctx(zstd_dctx *dctx, void *dst, size_t dst_capacity,
const void *src, size_t src_size);

+/**
+ * struct zstd_ddict - Decompression dictionary.
+ * See zstd_lib.h.
+ */
+typedef ZSTD_DDict zstd_ddict;
+
+/**
+ * zstd_create_ddict_advanced() - Create decompression dictionary
+ * @dict: Pointer to dictionary buffer.
+ * @dict_size: Size of the dictionary buffer.
+ * @dict_load_method: Dictionary load method.
+ * @dict_content_type: Dictionary content type.
+ * @custom_mem: Memory allocator.
+ *
+ * Return: NULL on error, pointer to decompression dictionary
+ * otherwise.
+ */
+zstd_ddict *zstd_create_ddict_advanced(const void *dict, size_t dict_size,
+ zstd_dict_load_method dict_load_method,
+ zstd_dict_content_type dict_content_type,
+ zstd_custom_mem custom_mem);
+/**
+ * zstd_free_ddict() - Free decompression dictionary
+ * @dict: Pointer to the dictionary.
+ *
+ * Return: Always 0.
+ */
+size_t zstd_free_ddict(zstd_ddict *ddict);
+
+/**
+ * zstd_create_dctx_advanced() - Create decompression context
+ * @custom_mem: Custom allocator.
+ *
+ * Return: NULL on error, pointer to decompression context otherwise.
+ */
+zstd_dctx *zstd_create_dctx_advanced(zstd_custom_mem custom_mem);
+
+/**
+ * zstd_free_dctx() -- Free decompression context
+ * @dctx: Pointer to decompression context.
+ * Return: Always 0.
+ */
+size_t zstd_free_dctx(zstd_dctx *dctx);
+
+/**
+ * zstd_decompress_using_ddict() - decompress src into dst using a dictionary
+ * @dctx: The decompression context.
+ * @dst: The buffer to decompress src into.
+ * @dst_capacity: The size of the destination buffer. Must be at least as large
+ * as the decompressed size. If the caller cannot upper bound the
+ * decompressed size, then it's better to use the streaming API.
+ * @src: The zstd compressed data to decompress. Multiple concatenated
+ * frames and skippable frames are allowed.
+ * @src_size: The exact size of the data to decompress.
+ * @ddict: The dictionary to be used.
+ *
+ * Return: The decompressed size or an error, which can be checked using
+ * zstd_is_error().
+ */
+size_t zstd_decompress_using_ddict(zstd_dctx *dctx,
+ void *dst, size_t dst_capacity, const void *src, size_t src_size,
+ const zstd_ddict *ddict);
+
+
/* ====== Streaming Buffers ====== */

/**
diff --git a/lib/zstd/zstd_compress_module.c b/lib/zstd/zstd_compress_module.c
index 8ecf43226af2..a5edf59334df 100644
--- a/lib/zstd/zstd_compress_module.c
+++ b/lib/zstd/zstd_compress_module.c
@@ -66,6 +66,12 @@ int zstd_max_clevel(void)
}
EXPORT_SYMBOL(zstd_max_clevel);

+int zstd_default_clevel(void)
+{
+ return ZSTD_defaultCLevel();
+}
+EXPORT_SYMBOL(zstd_default_clevel);
+
size_t zstd_compress_bound(size_t src_size)
{
return ZSTD_compressBound(src_size);
@@ -79,6 +85,13 @@ zstd_parameters zstd_get_params(int level,
}
EXPORT_SYMBOL(zstd_get_params);

+zstd_compression_parameters zstd_get_cparams(int level,
+ unsigned long long estimated_src_size, size_t dict_size)
+{
+ return ZSTD_getCParams(level, estimated_src_size, dict_size);
+}
+EXPORT_SYMBOL(zstd_get_cparams);
+
size_t zstd_cctx_workspace_bound(const zstd_compression_parameters *cparams)
{
return ZSTD_estimateCCtxSize_usingCParams(*cparams);
@@ -93,6 +106,36 @@ zstd_cctx *zstd_init_cctx(void *workspace, size_t workspace_size)
}
EXPORT_SYMBOL(zstd_init_cctx);

+zstd_cctx *zstd_create_cctx_advanced(zstd_custom_mem custom_mem)
+{
+ return ZSTD_createCCtx_advanced(custom_mem);
+}
+EXPORT_SYMBOL(zstd_create_cctx_advanced);
+
+size_t zstd_free_cctx(zstd_cctx *cctx)
+{
+ return ZSTD_freeCCtx(cctx);
+}
+EXPORT_SYMBOL(zstd_free_cctx);
+
+zstd_cdict *zstd_create_cdict_advanced(const void *dict, size_t dict_size,
+ zstd_dict_load_method dict_load_method,
+ zstd_dict_content_type dict_content_type,
+ zstd_compression_parameters cparams,
+ zstd_custom_mem custom_mem)
+{
+ return ZSTD_createCDict_advanced(dict, dict_size, dict_load_method,
+ dict_content_type, cparams,
+ custom_mem);
+}
+EXPORT_SYMBOL(zstd_create_cdict_advanced);
+
+size_t zstd_free_cdict(zstd_cdict *cdict)
+{
+ return ZSTD_freeCDict(cdict);
+}
+EXPORT_SYMBOL(zstd_free_cdict);
+
size_t zstd_compress_cctx(zstd_cctx *cctx, void *dst, size_t dst_capacity,
const void *src, size_t src_size, const zstd_parameters *parameters)
{
@@ -101,6 +144,15 @@ size_t zstd_compress_cctx(zstd_cctx *cctx, void *dst, size_t dst_capacity,
}
EXPORT_SYMBOL(zstd_compress_cctx);

+size_t zstd_compress_using_cdict(zstd_cctx *cctx, void *dst,
+ size_t dst_capacity, const void *src, size_t src_size,
+ const ZSTD_CDict *cdict)
+{
+ return ZSTD_compress_usingCDict(cctx, dst, dst_capacity,
+ src, src_size, cdict);
+}
+EXPORT_SYMBOL(zstd_compress_using_cdict);
+
size_t zstd_cstream_workspace_bound(const zstd_compression_parameters *cparams)
{
return ZSTD_estimateCStreamSize_usingCParams(*cparams);
diff --git a/lib/zstd/zstd_decompress_module.c b/lib/zstd/zstd_decompress_module.c
index 7d31518e9d5a..6a0f464609bc 100644
--- a/lib/zstd/zstd_decompress_module.c
+++ b/lib/zstd/zstd_decompress_module.c
@@ -44,6 +44,35 @@ size_t zstd_dctx_workspace_bound(void)
}
EXPORT_SYMBOL(zstd_dctx_workspace_bound);

+zstd_dctx *zstd_create_dctx_advanced(zstd_custom_mem custom_mem)
+{
+ return ZSTD_createDCtx_advanced(custom_mem);
+}
+EXPORT_SYMBOL(zstd_create_dctx_advanced);
+
+size_t zstd_free_dctx(zstd_dctx *dctx)
+{
+ return ZSTD_freeDCtx(dctx);
+}
+EXPORT_SYMBOL(zstd_free_dctx);
+
+zstd_ddict *zstd_create_ddict_advanced(const void *dict, size_t dict_size,
+ zstd_dict_load_method dict_load_method,
+ zstd_dict_content_type dict_content_type,
+ zstd_custom_mem custom_mem)
+{
+ return ZSTD_createDDict_advanced(dict, dict_size, dict_load_method,
+ dict_content_type, custom_mem);
+
+}
+EXPORT_SYMBOL(zstd_create_ddict_advanced);
+
+size_t zstd_free_ddict(zstd_ddict *ddict)
+{
+ return ZSTD_freeDDict(ddict);
+}
+EXPORT_SYMBOL(zstd_free_ddict);
+
zstd_dctx *zstd_init_dctx(void *workspace, size_t workspace_size)
{
if (workspace == NULL)
@@ -59,6 +88,15 @@ size_t zstd_decompress_dctx(zstd_dctx *dctx, void *dst, size_t dst_capacity,
}
EXPORT_SYMBOL(zstd_decompress_dctx);

+size_t zstd_decompress_using_ddict(zstd_dctx *dctx,
+ void *dst, size_t dst_capacity, const void* src, size_t src_size,
+ const zstd_ddict* ddict)
+{
+ return ZSTD_decompress_usingDDict(dctx, dst, dst_capacity, src,
+ src_size, ddict);
+}
+EXPORT_SYMBOL(zstd_decompress_using_ddict);
+
size_t zstd_dstream_workspace_bound(size_t max_window_size)
{
return ZSTD_estimateDStreamSize(max_window_size);
--
2.45.0.rc1.225.g2a3ae87e7f-goog


2024-05-15 07:22:03

by Sergey Senozhatsky

[permalink] [raw]
Subject: [PATCHv4 17/21] zram: share dictionaries between per-CPU contexts

zstd's CDict and DDict are accessed read-only during compression
and decompression, so instead of allocation per-context dictionaries
we can create just one CDict and DDict in init_config() and make
per-CPU contexts use them. This saves quite a lot of memory: on my
system CDict requires 408128 bytes and DDict requires 27352 bytes.

Signed-off-by: Sergey Senozhatsky <[email protected]>
---
drivers/block/zram/backend_zstd.c | 108 ++++++++++++++++++++----------
1 file changed, 72 insertions(+), 36 deletions(-)

diff --git a/drivers/block/zram/backend_zstd.c b/drivers/block/zram/backend_zstd.c
index 9d2cc27d4c9d..836de2af7c11 100644
--- a/drivers/block/zram/backend_zstd.c
+++ b/drivers/block/zram/backend_zstd.c
@@ -7,14 +7,18 @@

#include "backend_zstd.h"

+struct zstd_ctx_data {
+ zstd_custom_mem ctx_mem;
+ zstd_cdict *cdict;
+ zstd_ddict *ddict;
+};
+
struct zstd_ctx {
zstd_cctx *cctx;
zstd_dctx *dctx;
void *cctx_mem;
void *dctx_mem;
- zstd_custom_mem ctx_mem;
- zstd_cdict *cdict;
- zstd_ddict *ddict;
+ struct zstd_ctx_data *ctx_data;
s32 level;
};

@@ -38,32 +42,81 @@ static void zstd_ctx_free(void *opaque, void *address)

static int zstd_init_config(struct zcomp_config *config)
{
+ struct zstd_ctx_data *ctx_data = config->private;
+ zstd_compression_parameters params;
+
+ /* Already initialized */
+ if (ctx_data)
+ return 0;
+
if (config->level == ZCOMP_CONFIG_NO_LEVEL)
config->level = zstd_default_clevel();

+ if (config->dict_sz == 0)
+ return 0;
+
+ ctx_data = kzalloc(sizeof(*ctx_data), GFP_KERNEL);
+ if (!ctx_data)
+ return -ENOMEM;
+
+ ctx_data->ctx_mem.customAlloc = zstd_ctx_alloc;
+ ctx_data->ctx_mem.customFree = zstd_ctx_free;
+
+ params = zstd_get_cparams(config->level, PAGE_SIZE, config->dict_sz);
+
+ ctx_data->cdict = zstd_create_cdict_advanced(config->dict,
+ config->dict_sz,
+ ZSTD_dlm_byRef,
+ ZSTD_dct_auto,
+ params,
+ ctx_data->ctx_mem);
+ if (!ctx_data->cdict)
+ goto error;
+
+ ctx_data->ddict = zstd_create_ddict_advanced(config->dict,
+ config->dict_sz,
+ ZSTD_dlm_byRef,
+ ZSTD_dct_auto,
+ ctx_data->ctx_mem);
+ if (!ctx_data->ddict)
+ goto error;
+
+ config->private = ctx_data;
return 0;
+
+error:
+ zstd_free_cdict(ctx_data->cdict);
+ zstd_free_ddict(ctx_data->ddict);
+ kfree(ctx_data);
+ return -EINVAL;
}

static void zstd_release_config(struct zcomp_config *config)
{
+ struct zstd_ctx_data *ctx_data = config->private;
+
+ if (!ctx_data)
+ return;
+
+ config->private = NULL;
+ zstd_free_cdict(ctx_data->cdict);
+ zstd_free_ddict(ctx_data->ddict);
+ kfree(ctx_data);
}

static void zstd_destroy(void *ctx)
{
struct zstd_ctx *zctx = ctx;

+ /* Don't free zctx->ctx_data, it's done in release_config() */
if (zctx->cctx_mem)
vfree(zctx->cctx_mem);
else
zstd_free_cctx(zctx->cctx);
-
if (zctx->dctx_mem)
vfree(zctx->dctx_mem);
else
zstd_free_dctx(zctx->dctx);
-
- zstd_free_cdict(zctx->cdict);
- zstd_free_ddict(zctx->ddict);
kfree(zctx);
}

@@ -75,9 +128,8 @@ static void *zstd_create(struct zcomp_config *config)
if (!ctx)
return NULL;

+ ctx->ctx_data = config->private;
ctx->level = config->level;
- ctx->ctx_mem.customAlloc = zstd_ctx_alloc;
- ctx->ctx_mem.customFree = zstd_ctx_free;

if (config->dict_sz == 0) {
zstd_parameters params;
@@ -102,35 +154,15 @@ static void *zstd_create(struct zcomp_config *config)
if (!ctx->dctx)
goto error;
} else {
- zstd_compression_parameters params;
+ struct zstd_ctx_data *ctx_data = ctx->ctx_data;

- ctx->cctx = zstd_create_cctx_advanced(ctx->ctx_mem);
+ ctx->cctx = zstd_create_cctx_advanced(ctx_data->ctx_mem);
if (!ctx->cctx)
goto error;

- ctx->dctx = zstd_create_dctx_advanced(ctx->ctx_mem);
+ ctx->dctx = zstd_create_dctx_advanced(ctx_data->ctx_mem);
if (!ctx->dctx)
goto error;
-
- params = zstd_get_cparams(ctx->level, PAGE_SIZE,
- config->dict_sz);
-
- ctx->cdict = zstd_create_cdict_advanced(config->dict,
- config->dict_sz,
- ZSTD_dlm_byRef,
- ZSTD_dct_auto,
- params,
- ctx->ctx_mem);
- if (!ctx->cdict)
- goto error;
-
- ctx->ddict = zstd_create_ddict_advanced(config->dict,
- config->dict_sz,
- ZSTD_dlm_byRef,
- ZSTD_dct_auto,
- ctx->ctx_mem);
- if (!ctx->ddict)
- goto error;
}

return ctx;
@@ -144,15 +176,17 @@ static int zstd_compress(void *ctx, const unsigned char *src,
unsigned char *dst, size_t *dst_len)
{
struct zstd_ctx *zctx = ctx;
+ struct zstd_ctx_data *ctx_data = zctx->ctx_data;
const zstd_parameters params = zstd_get_params(zctx->level, PAGE_SIZE);
size_t ret;

- if (!zctx->cdict)
+ if (!ctx_data)
ret = zstd_compress_cctx(zctx->cctx, dst, *dst_len,
src, PAGE_SIZE, &params);
else
ret = zstd_compress_using_cdict(zctx->cctx, dst, *dst_len,
- src, PAGE_SIZE, zctx->cdict);
+ src, PAGE_SIZE,
+ ctx_data->cdict);
if (zstd_is_error(ret))
return -EINVAL;
*dst_len = ret;
@@ -163,14 +197,16 @@ static int zstd_decompress(void *ctx, const unsigned char *src, size_t src_len,
unsigned char *dst)
{
struct zstd_ctx *zctx = ctx;
+ struct zstd_ctx_data *ctx_data = zctx->ctx_data;
size_t ret;

- if (!zctx->ddict)
+ if (!ctx_data)
ret = zstd_decompress_dctx(zctx->dctx, dst, PAGE_SIZE,
src, src_len);
else
ret = zstd_decompress_using_ddict(zctx->dctx, dst, PAGE_SIZE,
- src, src_len, zctx->ddict);
+ src, src_len,
+ ctx_data->ddict);
if (zstd_is_error(ret))
return -EINVAL;
return 0;
--
2.45.0.rc1.225.g2a3ae87e7f-goog


2024-05-15 07:22:35

by Sergey Senozhatsky

[permalink] [raw]
Subject: [PATCHv4 19/21] lib/lz4hc: export LZ4_resetStreamHC symbol

This symbol is needed to enable lz4hc dictionary support.

Signed-off-by: Sergey Senozhatsky <[email protected]>
Cc: Nick Terrell <[email protected]>
---
lib/lz4/lz4hc_compress.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/lib/lz4/lz4hc_compress.c b/lib/lz4/lz4hc_compress.c
index e7ac8694b797..bc45594ad2a8 100644
--- a/lib/lz4/lz4hc_compress.c
+++ b/lib/lz4/lz4hc_compress.c
@@ -621,6 +621,7 @@ void LZ4_resetStreamHC(LZ4_streamHC_t *LZ4_streamHCPtr, int compressionLevel)
LZ4_streamHCPtr->internal_donotuse.base = NULL;
LZ4_streamHCPtr->internal_donotuse.compressionLevel = (unsigned int)compressionLevel;
}
+EXPORT_SYMBOL(LZ4_resetStreamHC);

int LZ4_loadDictHC(LZ4_streamHC_t *LZ4_streamHCPtr,
const char *dictionary,
--
2.45.0.rc1.225.g2a3ae87e7f-goog


2024-05-15 07:22:42

by Sergey Senozhatsky

[permalink] [raw]
Subject: [PATCHv4 18/21] zram: add dictionary support to lz4

Support pre-trained dictionary param. lz4 doesn't
mandate specific format of the dictionary and even
zstd --train can be used to train a dictionary for
lz4, according to [1].

TEST
====

- default lz4

/sys/block/zram0/mm_stat
1750323200 664258735 676990976 0 676990976 2 0 34288 34288

- lz4 dict=/etc/dictionary

/sys/block/zram0/mm_stat
1750310912 620608254 632852480 0 632852480 1 0 34288 34288

[1] https://github.com/lz4/lz4/issues/557

Signed-off-by: Sergey Senozhatsky <[email protected]>
---
drivers/block/zram/backend_lz4.c | 54 ++++++++++++++++++++++++++++----
1 file changed, 48 insertions(+), 6 deletions(-)

diff --git a/drivers/block/zram/backend_lz4.c b/drivers/block/zram/backend_lz4.c
index cf39bfc30f5b..47d0e719d5d7 100644
--- a/drivers/block/zram/backend_lz4.c
+++ b/drivers/block/zram/backend_lz4.c
@@ -8,6 +8,12 @@
struct lz4_ctx {
void *mem;
s32 level;
+ LZ4_streamDecode_t *dstrm;
+ LZ4_stream_t *cstrm;
+
+ /* Shared between C/D streams */
+ void *dict;
+ size_t dict_sz;
};

static int lz4_init_config(struct zcomp_config *config)
@@ -26,6 +32,8 @@ static void lz4_destroy(void *ctx)
{
struct lz4_ctx *zctx = ctx;

+ kfree(zctx->dstrm);
+ kfree(zctx->cstrm);
vfree(zctx->mem);
kfree(zctx);
}
@@ -39,9 +47,22 @@ static void *lz4_create(struct zcomp_config *config)
return NULL;

ctx->level = config->level;
- ctx->mem = vmalloc(LZ4_MEM_COMPRESS);
- if (!ctx->mem)
- goto error;
+ if (!config->dict) {
+ ctx->mem = vmalloc(LZ4_MEM_COMPRESS);
+ if (!ctx->mem)
+ goto error;
+ } else {
+ ctx->dstrm = kzalloc(sizeof(*ctx->dstrm), GFP_KERNEL);
+ if (!ctx->dstrm)
+ goto error;
+
+ ctx->cstrm = kzalloc(sizeof(*ctx->cstrm), GFP_KERNEL);
+ if (!ctx->cstrm)
+ goto error;
+
+ ctx->dict = config->dict;
+ ctx->dict_sz = config->dict_sz;
+ }

return ctx;
error:
@@ -55,8 +76,18 @@ static int lz4_compress(void *ctx, const unsigned char *src,
struct lz4_ctx *zctx = ctx;
int ret;

- ret = LZ4_compress_fast(src, dst, PAGE_SIZE, *dst_len,
- zctx->level, zctx->mem);
+ if (!zctx->cstrm) {
+ ret = LZ4_compress_fast(src, dst, PAGE_SIZE, *dst_len,
+ zctx->level, zctx->mem);
+ } else {
+ /* Cstrm needs to be reset */
+ ret = LZ4_loadDict(zctx->cstrm, zctx->dict, zctx->dict_sz);
+ if (ret != zctx->dict_sz)
+ return -EINVAL;
+ ret = LZ4_compress_fast_continue(zctx->cstrm, src, dst,
+ PAGE_SIZE, *dst_len,
+ zctx->level);
+ }
if (!ret)
return -EINVAL;
*dst_len = ret;
@@ -66,10 +97,21 @@ static int lz4_compress(void *ctx, const unsigned char *src,
static int lz4_decompress(void *ctx, const unsigned char *src,
size_t src_len, unsigned char *dst)
{
+ struct lz4_ctx *zctx = ctx;
int dst_len = PAGE_SIZE;
int ret;

- ret = LZ4_decompress_safe(src, dst, src_len, dst_len);
+ if (!zctx->dstrm) {
+ ret = LZ4_decompress_safe(src, dst, src_len, dst_len);
+ } else {
+ /* Dstrm needs to be reset */
+ ret = LZ4_setStreamDecode(zctx->dstrm, zctx->dict,
+ zctx->dict_sz);
+ if (!ret)
+ return -EINVAL;
+ ret = LZ4_decompress_safe_continue(zctx->dstrm, src, dst,
+ src_len, dst_len);
+ }
if (ret < 0)
return -EINVAL;
return 0;
--
2.45.0.rc1.225.g2a3ae87e7f-goog


2024-05-15 07:23:05

by Sergey Senozhatsky

[permalink] [raw]
Subject: [PATCHv4 21/21] Documentation/zram: add documentation for algorithm parameters

Document brief description of compression algorithms' parameters:
compression level and pre-trained dictionary.

Signed-off-by: Sergey Senozhatsky <[email protected]>
---
Documentation/admin-guide/blockdev/zram.rst | 38 ++++++++++++++++-----
1 file changed, 29 insertions(+), 9 deletions(-)

diff --git a/Documentation/admin-guide/blockdev/zram.rst b/Documentation/admin-guide/blockdev/zram.rst
index 091e8bb38887..58d79f9099e3 100644
--- a/Documentation/admin-guide/blockdev/zram.rst
+++ b/Documentation/admin-guide/blockdev/zram.rst
@@ -102,15 +102,26 @@ Examples::
#select lzo compression algorithm
echo lzo > /sys/block/zram0/comp_algorithm

-For the time being, the `comp_algorithm` content does not necessarily
-show every compression algorithm supported by the kernel. We keep this
-list primarily to simplify device configuration and one can configure
-a new device with a compression algorithm that is not listed in
-`comp_algorithm`. The thing is that, internally, ZRAM uses Crypto API
-and, if some of the algorithms were built as modules, it's impossible
-to list all of them using, for instance, /proc/crypto or any other
-method. This, however, has an advantage of permitting the usage of
-custom crypto compression modules (implementing S/W or H/W compression).
+For the time being, the `comp_algorithm` content shows only compression
+algorithms that are supported by zram.
+
+It is also possible to pass algorithm specific configuration parameters::
+
+ #set compression level to 8
+ echo "zstd level=8" > /sys/block/zram0/comp_algorithm
+
+Note that `comp_algorithm` also supports `algo=name` format::
+
+ #set compression level to 8
+ echo "algo=zstd level=8" > /sys/block/zram0/comp_algorithm
+
+Certain compression algorithms support pre-trained dictionaries, which
+significantly change algorithms' characteristics. In order to configure
+compression algorithm to use external pre-trained dictionary, pass full
+path to the dictionary along with other parameters::
+
+ #pass path to pre-trained dictionary
+ echo "algo=zstd dict=/etc/dictioary" > /sys/block/zram0/comp_algorithm

4) Set Disksize
===============
@@ -442,6 +453,15 @@ configuration:::
#select deflate recompression algorithm, priority 2
echo "algo=deflate priority=2" > /sys/block/zramX/recomp_algorithm

+The `recomp_algorithm` also supports algorithm configuration parameters, e.g.
+compression level and pre-trained dircionary::
+
+ #pass compression level
+ echo "algo=zstd level=8" > /sys/block/zramX/recomp_algorithm
+
+ #pass path to pre-trained dictionary
+ echo "algo=zstd dict=/etc/dictioary" > /sys/block/zramX/recomp_algorithm
+
Another device attribute that CONFIG_ZRAM_MULTI_COMP enables is recompress,
which controls recompression.

--
2.45.0.rc1.225.g2a3ae87e7f-goog


2024-05-15 07:23:31

by Sergey Senozhatsky

[permalink] [raw]
Subject: [PATCHv4 08/21] zram: add 842 compression backend support

Add s/w 842 compression support.

Signed-off-by: Sergey Senozhatsky <[email protected]>
---
drivers/block/zram/Kconfig | 11 ++++++
drivers/block/zram/Makefile | 1 +
drivers/block/zram/backend_842.c | 68 ++++++++++++++++++++++++++++++++
drivers/block/zram/backend_842.h | 10 +++++
drivers/block/zram/zcomp.c | 4 ++
5 files changed, 94 insertions(+)
create mode 100644 drivers/block/zram/backend_842.c
create mode 100644 drivers/block/zram/backend_842.h

diff --git a/drivers/block/zram/Kconfig b/drivers/block/zram/Kconfig
index 9dedd2edfb28..1e0e7e5910b8 100644
--- a/drivers/block/zram/Kconfig
+++ b/drivers/block/zram/Kconfig
@@ -44,6 +44,12 @@ config ZRAM_BACKEND_DEFLATE
select ZLIB_DEFLATE
select ZLIB_INFLATE

+config ZRAM_BACKEND_842
+ bool "842 compression support"
+ depends on ZRAM
+ select 842_COMPRESS
+ select 842_DECOMPRESS
+
choice
prompt "Default zram compressor"
default ZRAM_DEF_COMP_LZORLE
@@ -73,6 +79,10 @@ config ZRAM_DEF_COMP_DEFLATE
bool "deflate"
depends on ZRAM_BACKEND_DEFLATE

+config ZRAM_DEF_COMP_842
+ bool "842"
+ depends on ZRAM_BACKEND_842
+
endchoice

config ZRAM_DEF_COMP
@@ -83,6 +93,7 @@ config ZRAM_DEF_COMP
default "lz4hc" if ZRAM_DEF_COMP_LZ4HC
default "zstd" if ZRAM_DEF_COMP_ZSTD
default "deflate" if ZRAM_DEF_COMP_DEFLATE
+ default "842" if ZRAM_DEF_COMP_842
default "unset-value"

config ZRAM_WRITEBACK
diff --git a/drivers/block/zram/Makefile b/drivers/block/zram/Makefile
index 266430548437..0fdefd576691 100644
--- a/drivers/block/zram/Makefile
+++ b/drivers/block/zram/Makefile
@@ -7,5 +7,6 @@ zram-$(CONFIG_ZRAM_BACKEND_LZ4) += backend_lz4.o
zram-$(CONFIG_ZRAM_BACKEND_LZ4HC) += backend_lz4hc.o
zram-$(CONFIG_ZRAM_BACKEND_ZSTD) += backend_zstd.o
zram-$(CONFIG_ZRAM_BACKEND_DEFLATE) += backend_deflate.o
+zram-$(CONFIG_ZRAM_BACKEND_842) += backend_842.o

obj-$(CONFIG_ZRAM) += zram.o
diff --git a/drivers/block/zram/backend_842.c b/drivers/block/zram/backend_842.c
new file mode 100644
index 000000000000..8ea7a230b890
--- /dev/null
+++ b/drivers/block/zram/backend_842.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sw842.h>
+#include <linux/vmalloc.h>
+
+#include "backend_842.h"
+
+struct sw842_ctx {
+ void *mem;
+};
+
+static void destroy_842(void *ctx)
+{
+ struct sw842_ctx *zctx = ctx;
+
+ kfree(zctx->mem);
+ kfree(zctx);
+}
+
+static void *create_842(void)
+{
+ struct sw842_ctx *ctx;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return NULL;
+
+ ctx->mem = kmalloc(SW842_MEM_COMPRESS, GFP_KERNEL);
+ if (!ctx->mem)
+ goto error;
+
+ return ctx;
+
+error:
+ destroy_842(ctx);
+ return NULL;
+}
+
+static int compress_842(void *ctx, const unsigned char *src,
+ unsigned char *dst, size_t *dst_len)
+{
+ struct sw842_ctx *zctx = ctx;
+ unsigned int dlen = *dst_len;
+ int ret;
+
+ ret = sw842_compress(src, PAGE_SIZE, dst, &dlen, zctx->mem);
+ if (ret == 0)
+ *dst_len = dlen;
+ return ret;
+}
+
+static int decompress_842(void *ctx, const unsigned char *src, size_t src_len,
+ unsigned char *dst)
+{
+ unsigned int dlen = PAGE_SIZE;
+
+ return sw842_decompress(src, src_len, dst, &dlen);
+}
+
+struct zcomp_backend backend_842 = {
+ .compress = compress_842,
+ .decompress = decompress_842,
+ .create_ctx = create_842,
+ .destroy_ctx = destroy_842,
+ .name = "842",
+};
diff --git a/drivers/block/zram/backend_842.h b/drivers/block/zram/backend_842.h
new file mode 100644
index 000000000000..c03a2396d7b2
--- /dev/null
+++ b/drivers/block/zram/backend_842.h
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifndef __BACKEND_842_H__
+#define __BACKEND_842_H__
+
+#include "zcomp.h"
+
+extern struct zcomp_backend backend_842;
+
+#endif /* __BACKEND_842_H__ */
diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c
index 9fc5477a6259..2a38126f4da3 100644
--- a/drivers/block/zram/zcomp.c
+++ b/drivers/block/zram/zcomp.c
@@ -21,6 +21,7 @@
#include "backend_lz4hc.h"
#include "backend_zstd.h"
#include "backend_deflate.h"
+#include "backend_842.h"

static struct zcomp_backend *backends[] = {
#if IS_ENABLED(CONFIG_ZRAM_BACKEND_LZO)
@@ -38,6 +39,9 @@ static struct zcomp_backend *backends[] = {
#endif
#if IS_ENABLED(CONFIG_ZRAM_BACKEND_DEFLATE)
&backend_deflate,
+#endif
+#if IS_ENABLED(CONFIG_ZRAM_BACKEND_842)
+ &backend_842,
#endif
NULL
};
--
2.45.0.rc1.225.g2a3ae87e7f-goog


2024-05-15 07:24:06

by Sergey Senozhatsky

[permalink] [raw]
Subject: [PATCHv4 09/21] zram: check that backends array has at least one backend

Make sure that backends array has anything apart from the
sentinel NULL value.

We also select LZO_BACKEND if none backends were selected.

Signed-off-by: Sergey Senozhatsky <[email protected]>
---
drivers/block/zram/Kconfig | 19 +++++++++++++------
drivers/block/zram/zcomp.c | 8 ++++++++
2 files changed, 21 insertions(+), 6 deletions(-)

diff --git a/drivers/block/zram/Kconfig b/drivers/block/zram/Kconfig
index 1e0e7e5910b8..6aea609b795c 100644
--- a/drivers/block/zram/Kconfig
+++ b/drivers/block/zram/Kconfig
@@ -14,12 +14,6 @@ config ZRAM

See Documentation/admin-guide/blockdev/zram.rst for more information.

-config ZRAM_BACKEND_LZO
- bool "lzo and lzo-rle compression support"
- depends on ZRAM
- select LZO_COMPRESS
- select LZO_DECOMPRESS
-
config ZRAM_BACKEND_LZ4
bool "lz4 compression support"
depends on ZRAM
@@ -50,6 +44,19 @@ config ZRAM_BACKEND_842
select 842_COMPRESS
select 842_DECOMPRESS

+config ZRAM_BACKEND_FORCE_LZO
+ depends on ZRAM
+ def_bool !ZRAM_BACKEND_LZ4 && !ZRAM_BACKEND_LZ4HC && \
+ !ZRAM_BACKEND_ZSTD && !ZRAM_BACKEND_DEFLATE && \
+ !ZRAM_BACKEND_842
+
+config ZRAM_BACKEND_LZO
+ bool "lzo and lzo-rle compression support" if !ZRAM_BACKEND_FORCE_LZO
+ depends on ZRAM
+ default ZRAM_BACKEND_FORCE_LZO
+ select LZO_COMPRESS
+ select LZO_DECOMPRESS
+
choice
prompt "Default zram compressor"
default ZRAM_DEF_COMP_LZORLE
diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c
index 2a38126f4da3..d49791f724e9 100644
--- a/drivers/block/zram/zcomp.c
+++ b/drivers/block/zram/zcomp.c
@@ -209,6 +209,14 @@ struct zcomp *zcomp_create(const char *alg)
struct zcomp *comp;
int error;

+ /*
+ * The backends array has a sentinel NULL value, so the minimum
+ * size is 1. In order to be valid the array, apart from the
+ * sentinel NULL element, should have at least one compression
+ * backend selected.
+ */
+ BUILD_BUG_ON(ARRAY_SIZE(backends) <= 1);
+
comp = kzalloc(sizeof(struct zcomp), GFP_KERNEL);
if (!comp)
return ERR_PTR(-ENOMEM);
--
2.45.0.rc1.225.g2a3ae87e7f-goog


2024-05-15 07:25:34

by Sergey Senozhatsky

[permalink] [raw]
Subject: [PATCHv4 12/21] zram: support compression level comp config

Add support for compression level level=N comp configuration
to comp_algorithm and recomp_algorithm knobs.

Note that zram cannot verify ranges, it's a task of
corresponding backends to make sure that level makes
sense.

Signed-off-by: Sergey Senozhatsky <[email protected]>
---
drivers/block/zram/zram_drv.c | 30 ++++++++++++++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)

diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 7eb500c8fdaa..91706889b63f 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -998,6 +998,12 @@ static int __comp_algorithm_store(struct zram *zram, u32 prio, const char *buf)
return 0;
}

+static int comp_config_store(struct zram *zram, u32 prio, s32 level)
+{
+ zram->configs[prio].level = level;
+ return 0;
+}
+
static ssize_t comp_algorithm_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -1015,6 +1021,7 @@ static ssize_t comp_algorithm_store(struct device *dev,
struct zram *zram = dev_to_zram(dev);
char *args, *param, *val;
char *alg = NULL;
+ s32 level = ZCOMP_CONFIG_NO_LEVEL;
int ret;

args = skip_spaces(buf);
@@ -1034,12 +1041,21 @@ static ssize_t comp_algorithm_store(struct device *dev,
alg = val;
continue;
}
+
+ if (!strcmp(param, "level")) {
+ ret = kstrtoint(val, 10, &level);
+ if (ret)
+ return ret;
+ continue;
+ }
}

if (!alg)
return -EINVAL;

- ret = __comp_algorithm_store(zram, ZRAM_PRIMARY_COMP, alg);
+ ret = comp_config_store(zram, ZRAM_PRIMARY_COMP, level);
+ if (!ret)
+ ret = __comp_algorithm_store(zram, ZRAM_PRIMARY_COMP, alg);
return ret ? ret : len;
}

@@ -1072,6 +1088,7 @@ static ssize_t recomp_algorithm_store(struct device *dev,
int prio = ZRAM_SECONDARY_COMP;
char *args, *param, *val;
char *alg = NULL;
+ s32 level = ZCOMP_CONFIG_NO_LEVEL;
int ret;

args = skip_spaces(buf);
@@ -1092,6 +1109,13 @@ static ssize_t recomp_algorithm_store(struct device *dev,
return ret;
continue;
}
+
+ if (!strcmp(param, "level")) {
+ ret = kstrtoint(val, 10, &level);
+ if (ret)
+ return ret;
+ continue;
+ }
}

if (!alg)
@@ -1100,7 +1124,9 @@ static ssize_t recomp_algorithm_store(struct device *dev,
if (prio < ZRAM_SECONDARY_COMP || prio >= ZRAM_MAX_COMPS)
return -EINVAL;

- ret = __comp_algorithm_store(zram, prio, alg);
+ ret = comp_config_store(zram, prio, level);
+ if (!ret)
+ ret = __comp_algorithm_store(zram, prio, alg);
return ret ? ret : len;
}
#endif
--
2.45.0.rc1.225.g2a3ae87e7f-goog


2024-05-15 07:26:55

by Sergey Senozhatsky

[permalink] [raw]
Subject: [PATCHv4 15/21] zram: add dictionary support to zstd backend

This adds support for pre-trained zstd dictionaries [1]
Dictionary is loaded once (per-config) and then loaded to Cctx
and Dctx by reference, so we don't allocate extra memory.

Regarding GFP_ATOMIC in Cctx customAlloc(), we probably would
want to do something about it. Either make sure that we always
(somehow) fully setup all Cctx contexts from non-atomic context
before we attempt to use them, come up with some sort of custom
allocator or stop calling zcomp_compress() from atomic context.

TEST
====

- default zstd

/sys/block/zram0/mm_stat
1750315008 504602831 514256896 0 514256896 1 0 34204 34204

- zstd level=7 dict=/etc/dictionary

/sys/block/zram0/mm_stat
1750310912 432540606 441712640 0 441712640 1 0 34187 34187

[1] https://github.com/facebook/zstd/blob/dev/programs/zstd.1.md#dictionary-builder

Signed-off-by: Sergey Senozhatsky <[email protected]>
---
drivers/block/zram/backend_zstd.c | 126 ++++++++++++++++++++++++------
1 file changed, 101 insertions(+), 25 deletions(-)

diff --git a/drivers/block/zram/backend_zstd.c b/drivers/block/zram/backend_zstd.c
index d392d364ad2c..4be149370e0b 100644
--- a/drivers/block/zram/backend_zstd.c
+++ b/drivers/block/zram/backend_zstd.c
@@ -12,23 +12,52 @@ struct zstd_ctx {
zstd_dctx *dctx;
void *cctx_mem;
void *dctx_mem;
+ zstd_custom_mem ctx_mem;
+ zstd_cdict *cdict;
+ zstd_ddict *ddict;
s32 level;
};

+/*
+ * Cctx allocator.customAlloc() can be called from zcomp_compress() under
+ * local-lock (per-CPU compression stream), in which case we must use
+ * GFP_ATOMIC.
+ */
+static void *zstd_ctx_alloc(void *opaque, size_t size)
+{
+ if (!preemptible())
+ return kvzalloc(size, GFP_ATOMIC);
+
+ return kvzalloc(size, GFP_KERNEL);
+}
+
+static void zstd_ctx_free(void *opaque, void *address)
+{
+ kvfree(address);
+}
+
static void zstd_destroy(void *ctx)
{
struct zstd_ctx *zctx = ctx;

- vfree(zctx->cctx_mem);
- vfree(zctx->dctx_mem);
+ if (zctx->cctx_mem)
+ vfree(zctx->cctx_mem);
+ else
+ zstd_free_cctx(zctx->cctx);
+
+ if (zctx->dctx_mem)
+ vfree(zctx->dctx_mem);
+ else
+ zstd_free_dctx(zctx->dctx);
+
+ zstd_free_cdict(zctx->cdict);
+ zstd_free_ddict(zctx->ddict);
kfree(zctx);
}

static void *zstd_create(struct zcomp_config *config)
{
- zstd_parameters params;
struct zstd_ctx *ctx;
- size_t sz;

ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
@@ -39,24 +68,62 @@ static void *zstd_create(struct zcomp_config *config)
else
ctx->level = zstd_default_clevel();

- params = zstd_get_params(ctx->level, PAGE_SIZE);
- sz = zstd_cctx_workspace_bound(&params.cParams);
- ctx->cctx_mem = vzalloc(sz);
- if (!ctx->cctx_mem)
- goto error;
-
- ctx->cctx = zstd_init_cctx(ctx->cctx_mem, sz);
- if (!ctx->cctx)
- goto error;
-
- sz = zstd_dctx_workspace_bound();
- ctx->dctx_mem = vzalloc(sz);
- if (!ctx->dctx_mem)
- goto error;
-
- ctx->dctx = zstd_init_dctx(ctx->dctx_mem, sz);
- if (!ctx->dctx)
- goto error;
+ ctx->ctx_mem.customAlloc = zstd_ctx_alloc;
+ ctx->ctx_mem.customFree = zstd_ctx_free;
+
+ if (config->dict_sz == 0) {
+ zstd_parameters params;
+ size_t sz;
+
+ params = zstd_get_params(ctx->level, PAGE_SIZE);
+ sz = zstd_cctx_workspace_bound(&params.cParams);
+ ctx->cctx_mem = vzalloc(sz);
+ if (!ctx->cctx_mem)
+ goto error;
+
+ ctx->cctx = zstd_init_cctx(ctx->cctx_mem, sz);
+ if (!ctx->cctx)
+ goto error;
+
+ sz = zstd_dctx_workspace_bound();
+ ctx->dctx_mem = vzalloc(sz);
+ if (!ctx->dctx_mem)
+ goto error;
+
+ ctx->dctx = zstd_init_dctx(ctx->dctx_mem, sz);
+ if (!ctx->dctx)
+ goto error;
+ } else {
+ zstd_compression_parameters params;
+
+ ctx->cctx = zstd_create_cctx_advanced(ctx->ctx_mem);
+ if (!ctx->cctx)
+ goto error;
+
+ ctx->dctx = zstd_create_dctx_advanced(ctx->ctx_mem);
+ if (!ctx->dctx)
+ goto error;
+
+ params = zstd_get_cparams(ctx->level, PAGE_SIZE,
+ config->dict_sz);
+
+ ctx->cdict = zstd_create_cdict_advanced(config->dict,
+ config->dict_sz,
+ ZSTD_dlm_byRef,
+ ZSTD_dct_auto,
+ params,
+ ctx->ctx_mem);
+ if (!ctx->cdict)
+ goto error;
+
+ ctx->ddict = zstd_create_ddict_advanced(config->dict,
+ config->dict_sz,
+ ZSTD_dlm_byRef,
+ ZSTD_dct_auto,
+ ctx->ctx_mem);
+ if (!ctx->ddict)
+ goto error;
+ }

return ctx;

@@ -72,8 +139,12 @@ static int zstd_compress(void *ctx, const unsigned char *src,
const zstd_parameters params = zstd_get_params(zctx->level, PAGE_SIZE);
size_t ret;

- ret = zstd_compress_cctx(zctx->cctx, dst, *dst_len,
- src, PAGE_SIZE, &params);
+ if (!zctx->cdict)
+ ret = zstd_compress_cctx(zctx->cctx, dst, *dst_len,
+ src, PAGE_SIZE, &params);
+ else
+ ret = zstd_compress_using_cdict(zctx->cctx, dst, *dst_len,
+ src, PAGE_SIZE, zctx->cdict);
if (zstd_is_error(ret))
return -EINVAL;
*dst_len = ret;
@@ -86,7 +157,12 @@ static int zstd_decompress(void *ctx, const unsigned char *src, size_t src_len,
struct zstd_ctx *zctx = ctx;
size_t ret;

- ret = zstd_decompress_dctx(zctx->dctx, dst, PAGE_SIZE, src, src_len);
+ if (!zctx->ddict)
+ ret = zstd_decompress_dctx(zctx->dctx, dst, PAGE_SIZE,
+ src, src_len);
+ else
+ ret = zstd_decompress_using_ddict(zctx->dctx, dst, PAGE_SIZE,
+ src, src_len, zctx->ddict);
if (zstd_is_error(ret))
return -EINVAL;
return 0;
--
2.45.0.rc1.225.g2a3ae87e7f-goog


2024-05-15 07:29:43

by Sergey Senozhatsky

[permalink] [raw]
Subject: [PATCHv4 16/21] zram: add config init/release backend callbacks

Some backends can create a backend-specific private data
for comp config, e.g. parse a dictionary and use it to
init all of the ctx-s later on. Introduce two zcomp_config
callbacks to create and destroy per-config backend private
data. This is also the place where config can be validated.

Signed-off-by: Sergey Senozhatsky <[email protected]>
---
drivers/block/zram/backend_842.c | 11 +++++++++++
drivers/block/zram/backend_deflate.c | 20 +++++++++++++++-----
drivers/block/zram/backend_lz4.c | 20 +++++++++++++++-----
drivers/block/zram/backend_lz4hc.c | 20 +++++++++++++++-----
drivers/block/zram/backend_lzo.c | 11 +++++++++++
drivers/block/zram/backend_lzorle.c | 11 +++++++++++
drivers/block/zram/backend_zstd.c | 20 +++++++++++++++-----
drivers/block/zram/zcomp.c | 6 ++++++
drivers/block/zram/zcomp.h | 4 ++++
drivers/block/zram/zram_drv.c | 3 +++
10 files changed, 106 insertions(+), 20 deletions(-)

diff --git a/drivers/block/zram/backend_842.c b/drivers/block/zram/backend_842.c
index 12e716deb763..1522aa88ac35 100644
--- a/drivers/block/zram/backend_842.c
+++ b/drivers/block/zram/backend_842.c
@@ -11,6 +11,15 @@ struct sw842_ctx {
void *mem;
};

+static int init_config_842(struct zcomp_config *config)
+{
+ return 0;
+}
+
+static void release_config_842(struct zcomp_config *config)
+{
+}
+
static void destroy_842(void *ctx)
{
struct sw842_ctx *zctx = ctx;
@@ -64,5 +73,7 @@ struct zcomp_backend backend_842 = {
.decompress = decompress_842,
.create_ctx = create_842,
.destroy_ctx = destroy_842,
+ .init_config = init_config_842,
+ .release_config = release_config_842,
.name = "842",
};
diff --git a/drivers/block/zram/backend_deflate.c b/drivers/block/zram/backend_deflate.c
index 83c660adc722..2e166b507f5a 100644
--- a/drivers/block/zram/backend_deflate.c
+++ b/drivers/block/zram/backend_deflate.c
@@ -17,6 +17,18 @@ struct deflate_ctx {
s32 level;
};

+static int deflate_init_config(struct zcomp_config *config)
+{
+ if (config->level == ZCOMP_CONFIG_NO_LEVEL)
+ config->level = Z_DEFAULT_COMPRESSION;
+
+ return 0;
+}
+
+static void deflate_release_config(struct zcomp_config *config)
+{
+}
+
static void deflate_destroy(void *ctx)
{
struct deflate_ctx *zctx = ctx;
@@ -42,11 +54,7 @@ static void *deflate_create(struct zcomp_config *config)
if (!ctx)
return NULL;

- if (config->level != ZCOMP_CONFIG_NO_LEVEL)
- ctx->level = config->level;
- else
- ctx->level = Z_DEFAULT_COMPRESSION;
-
+ ctx->level = config->level;
sz = zlib_deflate_workspacesize(-DEFLATE_DEF_WINBITS, MAX_MEM_LEVEL);
ctx->cctx.workspace = vzalloc(sz);
if (!ctx->cctx.workspace)
@@ -129,5 +137,7 @@ struct zcomp_backend backend_deflate = {
.decompress = deflate_decompress,
.create_ctx = deflate_create,
.destroy_ctx = deflate_destroy,
+ .init_config = deflate_init_config,
+ .release_config = deflate_release_config,
.name = "deflate",
};
diff --git a/drivers/block/zram/backend_lz4.c b/drivers/block/zram/backend_lz4.c
index df0753fd8180..cf39bfc30f5b 100644
--- a/drivers/block/zram/backend_lz4.c
+++ b/drivers/block/zram/backend_lz4.c
@@ -10,6 +10,18 @@ struct lz4_ctx {
s32 level;
};

+static int lz4_init_config(struct zcomp_config *config)
+{
+ if (config->level == ZCOMP_CONFIG_NO_LEVEL)
+ config->level = LZ4_ACCELERATION_DEFAULT;
+
+ return 0;
+}
+
+static void lz4_release_config(struct zcomp_config *config)
+{
+}
+
static void lz4_destroy(void *ctx)
{
struct lz4_ctx *zctx = ctx;
@@ -26,11 +38,7 @@ static void *lz4_create(struct zcomp_config *config)
if (!ctx)
return NULL;

- if (config->level != ZCOMP_CONFIG_NO_LEVEL)
- ctx->level = config->level;
- else
- ctx->level = LZ4_ACCELERATION_DEFAULT;
-
+ ctx->level = config->level;
ctx->mem = vmalloc(LZ4_MEM_COMPRESS);
if (!ctx->mem)
goto error;
@@ -72,5 +80,7 @@ struct zcomp_backend backend_lz4 = {
.decompress = lz4_decompress,
.create_ctx = lz4_create,
.destroy_ctx = lz4_destroy,
+ .init_config = lz4_init_config,
+ .release_config = lz4_release_config,
.name = "lz4",
};
diff --git a/drivers/block/zram/backend_lz4hc.c b/drivers/block/zram/backend_lz4hc.c
index 2f03ca5809c7..428b393371d7 100644
--- a/drivers/block/zram/backend_lz4hc.c
+++ b/drivers/block/zram/backend_lz4hc.c
@@ -10,6 +10,18 @@ struct lz4hc_ctx {
s32 level;
};

+static int lz4hc_init_config(struct zcomp_config *config)
+{
+ if (config->level == ZCOMP_CONFIG_NO_LEVEL)
+ config->level = LZ4HC_DEFAULT_CLEVEL;
+
+ return 0;
+}
+
+static void lz4hc_release_config(struct zcomp_config *config)
+{
+}
+
static void lz4hc_destroy(void *ctx)
{
struct lz4hc_ctx *zctx = ctx;
@@ -26,11 +38,7 @@ static void *lz4hc_create(struct zcomp_config *config)
if (!ctx)
return NULL;

- if (config->level != ZCOMP_CONFIG_NO_LEVEL)
- ctx->level = config->level;
- else
- ctx->level = LZ4HC_DEFAULT_CLEVEL;
-
+ ctx->level = config->level;
ctx->mem = vmalloc(LZ4HC_MEM_COMPRESS);
if (!ctx->mem)
goto error;
@@ -72,5 +80,7 @@ struct zcomp_backend backend_lz4hc = {
.decompress = lz4hc_decompress,
.create_ctx = lz4hc_create,
.destroy_ctx = lz4hc_destroy,
+ .init_config = lz4hc_init_config,
+ .release_config = lz4hc_release_config,
.name = "lz4hc",
};
diff --git a/drivers/block/zram/backend_lzo.c b/drivers/block/zram/backend_lzo.c
index 79ecfae9c4c5..75e0e3d297aa 100644
--- a/drivers/block/zram/backend_lzo.c
+++ b/drivers/block/zram/backend_lzo.c
@@ -6,6 +6,15 @@

#include "backend_lzo.h"

+static int lzo_init_config(struct zcomp_config *config)
+{
+ return 0;
+}
+
+static void lzo_release_config(struct zcomp_config *config)
+{
+}
+
static void *lzo_create(struct zcomp_config *config)
{
return kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
@@ -40,5 +49,7 @@ struct zcomp_backend backend_lzo = {
.decompress = lzo_decompress,
.create_ctx = lzo_create,
.destroy_ctx = lzo_destroy,
+ .init_config = lzo_init_config,
+ .release_config = lzo_release_config,
.name = "lzo",
};
diff --git a/drivers/block/zram/backend_lzorle.c b/drivers/block/zram/backend_lzorle.c
index b0937103b5fb..d1dc2e35061b 100644
--- a/drivers/block/zram/backend_lzorle.c
+++ b/drivers/block/zram/backend_lzorle.c
@@ -6,6 +6,15 @@

#include "backend_lzorle.h"

+static int lzorle_init_config(struct zcomp_config *config)
+{
+ return 0;
+}
+
+static void lzorle_release_config(struct zcomp_config *config)
+{
+}
+
static void *lzorle_create(struct zcomp_config *config)
{
return kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
@@ -40,5 +49,7 @@ struct zcomp_backend backend_lzorle = {
.decompress = lzorle_decompress,
.create_ctx = lzorle_create,
.destroy_ctx = lzorle_destroy,
+ .init_config = lzorle_init_config,
+ .release_config = lzorle_release_config,
.name = "lzo-rle",
};
diff --git a/drivers/block/zram/backend_zstd.c b/drivers/block/zram/backend_zstd.c
index 4be149370e0b..9d2cc27d4c9d 100644
--- a/drivers/block/zram/backend_zstd.c
+++ b/drivers/block/zram/backend_zstd.c
@@ -36,6 +36,18 @@ static void zstd_ctx_free(void *opaque, void *address)
kvfree(address);
}

+static int zstd_init_config(struct zcomp_config *config)
+{
+ if (config->level == ZCOMP_CONFIG_NO_LEVEL)
+ config->level = zstd_default_clevel();
+
+ return 0;
+}
+
+static void zstd_release_config(struct zcomp_config *config)
+{
+}
+
static void zstd_destroy(void *ctx)
{
struct zstd_ctx *zctx = ctx;
@@ -63,11 +75,7 @@ static void *zstd_create(struct zcomp_config *config)
if (!ctx)
return NULL;

- if (config->level != ZCOMP_CONFIG_NO_LEVEL)
- ctx->level = config->level;
- else
- ctx->level = zstd_default_clevel();
-
+ ctx->level = config->level;
ctx->ctx_mem.customAlloc = zstd_ctx_alloc;
ctx->ctx_mem.customFree = zstd_ctx_free;

@@ -173,5 +181,7 @@ struct zcomp_backend backend_zstd = {
.decompress = zstd_decompress,
.create_ctx = zstd_create,
.destroy_ctx = zstd_destroy,
+ .init_config = zstd_init_config,
+ .release_config = zstd_release_config,
.name = "zstd",
};
diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c
index 3f990a715487..a7013a4b6575 100644
--- a/drivers/block/zram/zcomp.c
+++ b/drivers/block/zram/zcomp.c
@@ -187,18 +187,24 @@ static int zcomp_init(struct zcomp *comp)
if (!comp->stream)
return -ENOMEM;

+ ret = comp->backend->init_config(comp->config);
+ if (ret)
+ goto cleanup;
+
ret = cpuhp_state_add_instance(CPUHP_ZCOMP_PREPARE, &comp->node);
if (ret < 0)
goto cleanup;
return 0;

cleanup:
+ comp->backend->release_config(comp->config);
free_percpu(comp->stream);
return ret;
}

void zcomp_destroy(struct zcomp *comp)
{
+ comp->backend->release_config(comp->config);
cpuhp_state_remove_instance(CPUHP_ZCOMP_PREPARE, &comp->node);
free_percpu(comp->stream);
kfree(comp);
diff --git a/drivers/block/zram/zcomp.h b/drivers/block/zram/zcomp.h
index 345c78bc76db..aa604e5db7ad 100644
--- a/drivers/block/zram/zcomp.h
+++ b/drivers/block/zram/zcomp.h
@@ -22,6 +22,7 @@ struct zcomp_config {
s32 level;
size_t dict_sz;
void *dict;
+ void *private;
};

struct zcomp_backend {
@@ -34,6 +35,9 @@ struct zcomp_backend {
void *(*create_ctx)(struct zcomp_config *config);
void (*destroy_ctx)(void *ctx);

+ int (*init_config)(struct zcomp_config *config);
+ void (*release_config)(struct zcomp_config *config);
+
const char *name;
};

diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 216686a5f3f0..ce63c3f12f58 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -1003,6 +1003,9 @@ static void __reset_comp_config(struct zram *zram, u32 prio)
{
struct zcomp_config *config = &zram->configs[prio];

+ /* config->private should be freed by the backend */
+ WARN_ON_ONCE(config->private);
+
vfree(config->dict);
config->level = ZCOMP_CONFIG_NO_LEVEL;
config->dict_sz = 0;
--
2.45.0.rc1.225.g2a3ae87e7f-goog


2024-05-15 07:31:42

by Sergey Senozhatsky

[permalink] [raw]
Subject: [PATCHv4 20/21] zram: add dictionary support to lz4hc

Support pre-trained dictionary param. Just like lz4,
lz4hc doesn't mandate specific format of the dictionary
and zstd --train can be used to train a dictionary for
lz4, according to [1].

TEST
====

- default lz4hc

/sys/block/zram0/mm_stat
1750315008 609010918 621006848 0 621006848 2 0 34288 34288

- lz4hc dict=/etc/dictionary

/sys/block/zram0/mm_stat
1750319104 499629401 509485056 0 509485056 1 0 34288 34288

[1] https://github.com/lz4/lz4/issues/557

Signed-off-by: Sergey Senozhatsky <[email protected]>
---
drivers/block/zram/backend_lz4hc.c | 54 ++++++++++++++++++++++++++----
1 file changed, 48 insertions(+), 6 deletions(-)

diff --git a/drivers/block/zram/backend_lz4hc.c b/drivers/block/zram/backend_lz4hc.c
index 428b393371d7..c928f94f30df 100644
--- a/drivers/block/zram/backend_lz4hc.c
+++ b/drivers/block/zram/backend_lz4hc.c
@@ -8,6 +8,12 @@
struct lz4hc_ctx {
void *mem;
s32 level;
+ LZ4_streamDecode_t *dstrm;
+ LZ4_streamHC_t *cstrm;
+
+ /* Shared between C/D streams */
+ void *dict;
+ size_t dict_sz;
};

static int lz4hc_init_config(struct zcomp_config *config)
@@ -26,6 +32,8 @@ static void lz4hc_destroy(void *ctx)
{
struct lz4hc_ctx *zctx = ctx;

+ kfree(zctx->dstrm);
+ kfree(zctx->cstrm);
vfree(zctx->mem);
kfree(zctx);
}
@@ -39,9 +47,22 @@ static void *lz4hc_create(struct zcomp_config *config)
return NULL;

ctx->level = config->level;
- ctx->mem = vmalloc(LZ4HC_MEM_COMPRESS);
- if (!ctx->mem)
- goto error;
+ if (!config->dict) {
+ ctx->mem = vmalloc(LZ4HC_MEM_COMPRESS);
+ if (!ctx->mem)
+ goto error;
+ } else {
+ ctx->dstrm = kzalloc(sizeof(*ctx->dstrm), GFP_KERNEL);
+ if (!ctx->dstrm)
+ goto error;
+
+ ctx->cstrm = kzalloc(sizeof(*ctx->cstrm), GFP_KERNEL);
+ if (!ctx->cstrm)
+ goto error;
+
+ ctx->dict = config->dict;
+ ctx->dict_sz = config->dict_sz;
+ }

return ctx;
error:
@@ -55,8 +76,18 @@ static int lz4hc_compress(void *ctx, const unsigned char *src,
struct lz4hc_ctx *zctx = ctx;
int ret;

- ret = LZ4_compress_HC(src, dst, PAGE_SIZE, *dst_len,
- zctx->level, zctx->mem);
+ if (!zctx->cstrm) {
+ ret = LZ4_compress_HC(src, dst, PAGE_SIZE, *dst_len,
+ zctx->level, zctx->mem);
+ } else {
+ /* Cstrm needs to be reset */
+ LZ4_resetStreamHC(zctx->cstrm, zctx->level);
+ ret = LZ4_loadDictHC(zctx->cstrm, zctx->dict, zctx->dict_sz);
+ if (ret != zctx->dict_sz)
+ return -EINVAL;
+ ret = LZ4_compress_HC_continue(zctx->cstrm, src, dst,
+ PAGE_SIZE, *dst_len);
+ }
if (!ret)
return -EINVAL;
*dst_len = ret;
@@ -66,10 +97,21 @@ static int lz4hc_compress(void *ctx, const unsigned char *src,
static int lz4hc_decompress(void *ctx, const unsigned char *src,
size_t src_len, unsigned char *dst)
{
+ struct lz4hc_ctx *zctx = ctx;
int dst_len = PAGE_SIZE;
int ret;

- ret = LZ4_decompress_safe(src, dst, src_len, dst_len);
+ if (!zctx->dstrm) {
+ ret = LZ4_decompress_safe(src, dst, src_len, dst_len);
+ } else {
+ /* Dstrm needs to be reset */
+ ret = LZ4_setStreamDecode(zctx->dstrm, zctx->dict,
+ zctx->dict_sz);
+ if (!ret)
+ return -EINVAL;
+ ret = LZ4_decompress_safe_continue(zctx->dstrm, src, dst,
+ src_len, dst_len);
+ }
if (ret < 0)
return -EINVAL;
return 0;
--
2.45.0.rc1.225.g2a3ae87e7f-goog