mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-29 17:25:38 +00:00
zram: introduce custom comp backends API
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>
This commit is contained in:
parent
f3c11cf5ca
commit
917a59e81c
@ -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
|
||||
===============
|
||||
|
@ -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"
|
||||
|
@ -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 cpu, struct hlist_node *node)
|
||||
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, struct hlist_node *node)
|
||||
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);
|
||||
|
@ -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_ */
|
||||
|
@ -1327,7 +1327,8 @@ static int zram_read_from_zspool(struct zram *zram, struct page *page,
|
||||
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 *zram, struct page *page, u32 index)
|
||||
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 *zram, u32 index, struct page *page,
|
||||
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) {
|
||||
|
Loading…
Reference in New Issue
Block a user