| From: Sergey Senozhatsky <senozhatsky@chromium.org> |
| Subject: zram: introduce custom comp backends API |
| Date: Mon, 2 Sep 2024 19:55:52 +0900 |
| |
| Moving to custom backends implementation gives us ability to have our own |
| minimalistic and extendable API, and algorithms tunings becomes possible. |
| |
| The list of compression backends is empty at this point, we will add |
| backends in the followup patches. |
| |
| Link: https://lkml.kernel.org/r/20240902105656.1383858-5-senozhatsky@chromium.org |
| Signed-off-by: Sergey Senozhatsky <senozhatsky@chromium.org> |
| Cc: Minchan Kim <minchan@kernel.org> |
| Cc: Nick Terrell <terrelln@fb.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| --- |
| |
| Documentation/admin-guide/blockdev/zram.rst | 11 - |
| drivers/block/zram/Kconfig | 39 ---- |
| drivers/block/zram/zcomp.c | 150 ++++++------------ |
| drivers/block/zram/zcomp.h | 29 ++- |
| drivers/block/zram/zram_drv.c | 9 - |
| 5 files changed, 78 insertions(+), 160 deletions(-) |
| |
| --- a/Documentation/admin-guide/blockdev/zram.rst~zram-introduce-custom-comp-backends-api |
| +++ a/Documentation/admin-guide/blockdev/zram.rst |
| @@ -102,15 +102,8 @@ 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. |
| |
| 4) Set Disksize |
| =============== |
| --- a/drivers/block/zram/Kconfig~zram-introduce-custom-comp-backends-api |
| +++ a/drivers/block/zram/Kconfig |
| @@ -2,7 +2,6 @@ |
| config ZRAM |
| tristate "Compressed RAM block device support" |
| depends on BLOCK && SYSFS && MMU |
| - depends on CRYPTO_LZO || CRYPTO_ZSTD || CRYPTO_LZ4 || CRYPTO_LZ4HC || CRYPTO_842 |
| select ZSMALLOC |
| help |
| Creates virtual block devices called /dev/zramX (X = 0, 1, ...). |
| @@ -15,45 +14,9 @@ config ZRAM |
| |
| See Documentation/admin-guide/blockdev/zram.rst for more information. |
| |
| -choice |
| - prompt "Default zram compressor" |
| - default ZRAM_DEF_COMP_LZORLE |
| - depends on ZRAM |
| - |
| -config ZRAM_DEF_COMP_LZORLE |
| - bool "lzo-rle" |
| - depends on CRYPTO_LZO |
| - |
| -config ZRAM_DEF_COMP_ZSTD |
| - bool "zstd" |
| - depends on CRYPTO_ZSTD |
| - |
| -config ZRAM_DEF_COMP_LZ4 |
| - bool "lz4" |
| - depends on CRYPTO_LZ4 |
| - |
| -config ZRAM_DEF_COMP_LZO |
| - bool "lzo" |
| - depends on CRYPTO_LZO |
| - |
| -config ZRAM_DEF_COMP_LZ4HC |
| - bool "lz4hc" |
| - depends on CRYPTO_LZ4HC |
| - |
| -config ZRAM_DEF_COMP_842 |
| - bool "842" |
| - depends on CRYPTO_842 |
| - |
| -endchoice |
| - |
| config ZRAM_DEF_COMP |
| string |
| - default "lzo-rle" if ZRAM_DEF_COMP_LZORLE |
| - default "zstd" if ZRAM_DEF_COMP_ZSTD |
| - default "lz4" if ZRAM_DEF_COMP_LZ4 |
| - default "lzo" if ZRAM_DEF_COMP_LZO |
| - default "lz4hc" if ZRAM_DEF_COMP_LZ4HC |
| - default "842" if ZRAM_DEF_COMP_842 |
| + default "unset-value" |
| |
| config ZRAM_WRITEBACK |
| bool "Write back incompressible or idle page to backing device" |
| --- a/drivers/block/zram/zcomp.c~zram-introduce-custom-comp-backends-api |
| +++ a/drivers/block/zram/zcomp.c |
| @@ -1,7 +1,4 @@ |
| // SPDX-License-Identifier: GPL-2.0-or-later |
| -/* |
| - * Copyright (C) 2014 Sergey Senozhatsky. |
| - */ |
| |
| #include <linux/kernel.h> |
| #include <linux/string.h> |
| @@ -15,91 +12,68 @@ |
| |
| #include "zcomp.h" |
| |
| -static const char * const backends[] = { |
| -#if IS_ENABLED(CONFIG_CRYPTO_LZO) |
| - "lzo", |
| - "lzo-rle", |
| -#endif |
| -#if IS_ENABLED(CONFIG_CRYPTO_LZ4) |
| - "lz4", |
| -#endif |
| -#if IS_ENABLED(CONFIG_CRYPTO_LZ4HC) |
| - "lz4hc", |
| -#endif |
| -#if IS_ENABLED(CONFIG_CRYPTO_842) |
| - "842", |
| -#endif |
| -#if IS_ENABLED(CONFIG_CRYPTO_ZSTD) |
| - "zstd", |
| -#endif |
| +static const struct zcomp_ops *backends[] = { |
| + NULL |
| }; |
| |
| -static void zcomp_strm_free(struct zcomp_strm *zstrm) |
| +static void zcomp_strm_free(struct zcomp *comp, struct zcomp_strm *zstrm) |
| { |
| - if (!IS_ERR_OR_NULL(zstrm->tfm)) |
| - crypto_free_comp(zstrm->tfm); |
| + if (zstrm->ctx) |
| + comp->ops->destroy_ctx(zstrm->ctx); |
| vfree(zstrm->buffer); |
| - zstrm->tfm = NULL; |
| + zstrm->ctx = NULL; |
| zstrm->buffer = NULL; |
| } |
| |
| -/* |
| - * Initialize zcomp_strm structure with ->tfm initialized by backend, and |
| - * ->buffer. Return a negative value on error. |
| - */ |
| -static int zcomp_strm_init(struct zcomp_strm *zstrm, struct zcomp *comp) |
| +static int zcomp_strm_init(struct zcomp *comp, struct zcomp_strm *zstrm) |
| { |
| - zstrm->tfm = crypto_alloc_comp(comp->name, 0, 0); |
| + zstrm->ctx = comp->ops->create_ctx(); |
| + |
| /* |
| * allocate 2 pages. 1 for compressed data, plus 1 extra for the |
| * case when compressed size is larger than the original one |
| */ |
| zstrm->buffer = vzalloc(2 * PAGE_SIZE); |
| - if (IS_ERR_OR_NULL(zstrm->tfm) || !zstrm->buffer) { |
| - zcomp_strm_free(zstrm); |
| + if (!zstrm->ctx || !zstrm->buffer) { |
| + zcomp_strm_free(comp, zstrm); |
| return -ENOMEM; |
| } |
| return 0; |
| } |
| |
| +static const struct zcomp_ops *lookup_backend_ops(const char *comp) |
| +{ |
| + int i = 0; |
| + |
| + while (backends[i]) { |
| + if (sysfs_streq(comp, backends[i]->name)) |
| + break; |
| + i++; |
| + } |
| + return backends[i]; |
| +} |
| + |
| bool zcomp_available_algorithm(const char *comp) |
| { |
| - /* |
| - * Crypto does not ignore a trailing new line symbol, |
| - * so make sure you don't supply a string containing |
| - * one. |
| - * This also means that we permit zcomp initialisation |
| - * with any compressing algorithm known to crypto api. |
| - */ |
| - return crypto_has_comp(comp, 0, 0) == 1; |
| + return lookup_backend_ops(comp) != NULL; |
| } |
| |
| /* show available compressors */ |
| ssize_t zcomp_available_show(const char *comp, char *buf) |
| { |
| - bool known_algorithm = false; |
| ssize_t sz = 0; |
| int i; |
| |
| - for (i = 0; i < ARRAY_SIZE(backends); i++) { |
| - if (!strcmp(comp, backends[i])) { |
| - known_algorithm = true; |
| + for (i = 0; i < ARRAY_SIZE(backends) - 1; i++) { |
| + if (!strcmp(comp, backends[i]->name)) { |
| sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, |
| - "[%s] ", backends[i]); |
| + "[%s] ", backends[i]->name); |
| } else { |
| sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, |
| - "%s ", backends[i]); |
| + "%s ", backends[i]->name); |
| } |
| } |
| |
| - /* |
| - * Out-of-tree module known to crypto api or a missing |
| - * entry in `backends'. |
| - */ |
| - if (!known_algorithm && crypto_has_comp(comp, 0, 0) == 1) |
| - sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, |
| - "[%s] ", comp); |
| - |
| sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); |
| return sz; |
| } |
| @@ -115,38 +89,25 @@ void zcomp_stream_put(struct zcomp *comp |
| local_unlock(&comp->stream->lock); |
| } |
| |
| -int zcomp_compress(struct zcomp_strm *zstrm, |
| - const void *src, unsigned int *dst_len) |
| +int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm, |
| + const void *src, unsigned int *dst_len) |
| { |
| - /* |
| - * Our dst memory (zstrm->buffer) is always `2 * PAGE_SIZE' sized |
| - * because sometimes we can endup having a bigger compressed data |
| - * due to various reasons: for example compression algorithms tend |
| - * to add some padding to the compressed buffer. Speaking of padding, |
| - * comp algorithm `842' pads the compressed length to multiple of 8 |
| - * and returns -ENOSP when the dst memory is not big enough, which |
| - * is not something that ZRAM wants to see. We can handle the |
| - * `compressed_size > PAGE_SIZE' case easily in ZRAM, but when we |
| - * receive -ERRNO from the compressing backend we can't help it |
| - * anymore. To make `842' happy we need to tell the exact size of |
| - * the dst buffer, zram_drv will take care of the fact that |
| - * compressed buffer is too big. |
| - */ |
| - *dst_len = PAGE_SIZE * 2; |
| + /* The dst buffer should always be 2 * PAGE_SIZE */ |
| + size_t dlen = 2 * PAGE_SIZE; |
| + int ret; |
| |
| - return crypto_comp_compress(zstrm->tfm, |
| - src, PAGE_SIZE, |
| - zstrm->buffer, dst_len); |
| + ret = comp->ops->compress(zstrm->ctx, src, PAGE_SIZE, |
| + zstrm->buffer, &dlen); |
| + if (!ret) |
| + *dst_len = dlen; |
| + return ret; |
| } |
| |
| -int zcomp_decompress(struct zcomp_strm *zstrm, |
| - const void *src, unsigned int src_len, void *dst) |
| +int zcomp_decompress(struct zcomp *comp, struct zcomp_strm *zstrm, |
| + const void *src, unsigned int src_len, void *dst) |
| { |
| - unsigned int dst_len = PAGE_SIZE; |
| - |
| - return crypto_comp_decompress(zstrm->tfm, |
| - src, src_len, |
| - dst, &dst_len); |
| + return comp->ops->decompress(zstrm->ctx, src, src_len, |
| + dst, PAGE_SIZE); |
| } |
| |
| int zcomp_cpu_up_prepare(unsigned int cpu, struct hlist_node *node) |
| @@ -158,7 +119,7 @@ int zcomp_cpu_up_prepare(unsigned int cp |
| zstrm = per_cpu_ptr(comp->stream, cpu); |
| local_lock_init(&zstrm->lock); |
| |
| - ret = zcomp_strm_init(zstrm, comp); |
| + ret = zcomp_strm_init(comp, zstrm); |
| if (ret) |
| pr_err("Can't allocate a compression stream\n"); |
| return ret; |
| @@ -170,7 +131,7 @@ int zcomp_cpu_dead(unsigned int cpu, str |
| struct zcomp_strm *zstrm; |
| |
| zstrm = per_cpu_ptr(comp->stream, cpu); |
| - zcomp_strm_free(zstrm); |
| + zcomp_strm_free(comp, zstrm); |
| return 0; |
| } |
| |
| @@ -199,32 +160,21 @@ void zcomp_destroy(struct zcomp *comp) |
| kfree(comp); |
| } |
| |
| -/* |
| - * search available compressors for requested algorithm. |
| - * allocate new zcomp and initialize it. return compressing |
| - * backend pointer or ERR_PTR if things went bad. ERR_PTR(-EINVAL) |
| - * if requested algorithm is not supported, ERR_PTR(-ENOMEM) in |
| - * case of allocation error, or any other error potentially |
| - * returned by zcomp_init(). |
| - */ |
| struct zcomp *zcomp_create(const char *alg) |
| { |
| struct zcomp *comp; |
| int error; |
| |
| - /* |
| - * Crypto API will execute /sbin/modprobe if the compression module |
| - * is not loaded yet. We must do it here, otherwise we are about to |
| - * call /sbin/modprobe under CPU hot-plug lock. |
| - */ |
| - if (!zcomp_available_algorithm(alg)) |
| - return ERR_PTR(-EINVAL); |
| - |
| comp = kzalloc(sizeof(struct zcomp), GFP_KERNEL); |
| if (!comp) |
| return ERR_PTR(-ENOMEM); |
| |
| - comp->name = alg; |
| + comp->ops = lookup_backend_ops(alg); |
| + if (!comp->ops) { |
| + kfree(comp); |
| + return ERR_PTR(-EINVAL); |
| + } |
| + |
| error = zcomp_init(comp); |
| if (error) { |
| kfree(comp); |
| --- a/drivers/block/zram/zcomp.h~zram-introduce-custom-comp-backends-api |
| +++ a/drivers/block/zram/zcomp.h |
| @@ -1,7 +1,4 @@ |
| /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| -/* |
| - * Copyright (C) 2014 Sergey Senozhatsky. |
| - */ |
| |
| #ifndef _ZCOMP_H_ |
| #define _ZCOMP_H_ |
| @@ -12,13 +9,26 @@ struct zcomp_strm { |
| local_lock_t lock; |
| /* compression/decompression buffer */ |
| void *buffer; |
| - struct crypto_comp *tfm; |
| + void *ctx; |
| +}; |
| + |
| +struct zcomp_ops { |
| + int (*compress)(void *ctx, const unsigned char *src, size_t src_len, |
| + unsigned char *dst, size_t *dst_len); |
| + |
| + int (*decompress)(void *ctx, const unsigned char *src, size_t src_len, |
| + unsigned char *dst, size_t dst_len); |
| + |
| + void *(*create_ctx)(void); |
| + void (*destroy_ctx)(void *ctx); |
| + |
| + const char *name; |
| }; |
| |
| /* dynamic per-device compression frontend */ |
| struct zcomp { |
| struct zcomp_strm __percpu *stream; |
| - const char *name; |
| + const struct zcomp_ops *ops; |
| struct hlist_node node; |
| }; |
| |
| @@ -33,10 +43,9 @@ void zcomp_destroy(struct zcomp *comp); |
| struct zcomp_strm *zcomp_stream_get(struct zcomp *comp); |
| void zcomp_stream_put(struct zcomp *comp); |
| |
| -int zcomp_compress(struct zcomp_strm *zstrm, |
| - const void *src, unsigned int *dst_len); |
| - |
| -int zcomp_decompress(struct zcomp_strm *zstrm, |
| - const void *src, unsigned int src_len, void *dst); |
| +int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm, |
| + const void *src, unsigned int *dst_len); |
| +int zcomp_decompress(struct zcomp *comp, struct zcomp_strm *zstrm, |
| + const void *src, unsigned int src_len, void *dst); |
| |
| #endif /* _ZCOMP_H_ */ |
| --- a/drivers/block/zram/zram_drv.c~zram-introduce-custom-comp-backends-api |
| +++ a/drivers/block/zram/zram_drv.c |
| @@ -1327,7 +1327,8 @@ static int zram_read_from_zspool(struct |
| ret = 0; |
| } else { |
| dst = kmap_local_page(page); |
| - ret = zcomp_decompress(zstrm, src, size, dst); |
| + ret = zcomp_decompress(zram->comps[prio], zstrm, |
| + src, size, dst); |
| kunmap_local(dst); |
| zcomp_stream_put(zram->comps[prio]); |
| } |
| @@ -1414,7 +1415,8 @@ static int zram_write_page(struct zram * |
| compress_again: |
| zstrm = zcomp_stream_get(zram->comps[ZRAM_PRIMARY_COMP]); |
| src = kmap_local_page(page); |
| - ret = zcomp_compress(zstrm, src, &comp_len); |
| + ret = zcomp_compress(zram->comps[ZRAM_PRIMARY_COMP], zstrm, |
| + src, &comp_len); |
| kunmap_local(src); |
| |
| if (unlikely(ret)) { |
| @@ -1601,7 +1603,8 @@ static int zram_recompress(struct zram * |
| num_recomps++; |
| zstrm = zcomp_stream_get(zram->comps[prio]); |
| src = kmap_local_page(page); |
| - ret = zcomp_compress(zstrm, src, &comp_len_new); |
| + ret = zcomp_compress(zram->comps[prio], zstrm, |
| + src, &comp_len_new); |
| kunmap_local(src); |
| |
| if (ret) { |
| _ |