| // SPDX-License-Identifier: GPL-2.0-or-later | |
| #define pr_fmt(fmt) "lz4: " fmt | |
| #include <linux/kernel.h> | |
| #include <linux/lz4.h> | |
| #include <linux/slab.h> | |
| #include <linux/vmalloc.h> | |
| #include "backend_lz4.h" | |
| struct lz4_ctx { | |
| void *mem; | |
| LZ4_streamDecode_t *dstrm; | |
| LZ4_stream_t *cstrm; | |
| }; | |
| static void lz4_release_params(struct zcomp_params *params) | |
| { | |
| LZ4_stream_t *dict_stream = params->drv_data; | |
| params->drv_data = NULL; | |
| if (!dict_stream) | |
| return; | |
| kfree(dict_stream); | |
| } | |
| static int lz4_setup_params(struct zcomp_params *params) | |
| { | |
| LZ4_stream_t *dict_stream; | |
| int ret; | |
| if (params->level == ZCOMP_PARAM_NOT_SET) { | |
| params->level = LZ4_ACCELERATION_DEFAULT; | |
| } else if (params->level < LZ4_ACCELERATION_DEFAULT) { | |
| pr_err("invalid compression level %d\n", params->level); | |
| return -EINVAL; | |
| } | |
| if (!params->dict || !params->dict_sz) | |
| return 0; | |
| dict_stream = kzalloc_obj(*dict_stream); | |
| if (!dict_stream) | |
| return -ENOMEM; | |
| ret = LZ4_loadDict(dict_stream, | |
| params->dict, params->dict_sz); | |
| if (ret != params->dict_sz) { | |
| kfree(dict_stream); | |
| return -EINVAL; | |
| } | |
| params->drv_data = dict_stream; | |
| return 0; | |
| } | |
| static void lz4_destroy(struct zcomp_ctx *ctx) | |
| { | |
| struct lz4_ctx *zctx = ctx->context; | |
| if (!zctx) | |
| return; | |
| vfree(zctx->mem); | |
| kfree(zctx->dstrm); | |
| kfree(zctx->cstrm); | |
| kfree(zctx); | |
| } | |
| static int lz4_create(struct zcomp_params *params, struct zcomp_ctx *ctx) | |
| { | |
| struct lz4_ctx *zctx; | |
| zctx = kzalloc_obj(*zctx); | |
| if (!zctx) | |
| return -ENOMEM; | |
| ctx->context = zctx; | |
| if (params->dict_sz == 0) { | |
| zctx->mem = vmalloc(LZ4_MEM_COMPRESS); | |
| if (!zctx->mem) | |
| goto error; | |
| } else { | |
| zctx->dstrm = kzalloc_obj(*zctx->dstrm); | |
| if (!zctx->dstrm) | |
| goto error; | |
| zctx->cstrm = kzalloc_obj(*zctx->cstrm); | |
| if (!zctx->cstrm) | |
| goto error; | |
| } | |
| return 0; | |
| error: | |
| lz4_destroy(ctx); | |
| return -ENOMEM; | |
| } | |
| static int lz4_compress(struct zcomp_params *params, struct zcomp_ctx *ctx, | |
| struct zcomp_req *req) | |
| { | |
| struct lz4_ctx *zctx = ctx->context; | |
| int ret; | |
| if (!zctx->cstrm) { | |
| ret = LZ4_compress_fast(req->src, req->dst, req->src_len, | |
| req->dst_len, params->level, | |
| zctx->mem); | |
| } else { | |
| /* Cstrm needs to be reset */ | |
| memcpy(zctx->cstrm, params->drv_data, sizeof(*zctx->cstrm)); | |
| ret = LZ4_compress_fast_continue(zctx->cstrm, req->src, | |
| req->dst, req->src_len, | |
| req->dst_len, params->level); | |
| } | |
| if (!ret) | |
| return -EINVAL; | |
| req->dst_len = ret; | |
| return 0; | |
| } | |
| static int lz4_decompress(struct zcomp_params *params, struct zcomp_ctx *ctx, | |
| struct zcomp_req *req) | |
| { | |
| struct lz4_ctx *zctx = ctx->context; | |
| int ret; | |
| if (!zctx->dstrm) { | |
| ret = LZ4_decompress_safe(req->src, req->dst, req->src_len, | |
| req->dst_len); | |
| } else { | |
| /* Dstrm needs to be reset */ | |
| ret = LZ4_setStreamDecode(zctx->dstrm, params->dict, | |
| params->dict_sz); | |
| if (!ret) | |
| return -EINVAL; | |
| ret = LZ4_decompress_safe_continue(zctx->dstrm, req->src, | |
| req->dst, req->src_len, | |
| req->dst_len); | |
| } | |
| if (ret < 0) | |
| return -EINVAL; | |
| return 0; | |
| } | |
| const struct zcomp_ops backend_lz4 = { | |
| .compress = lz4_compress, | |
| .decompress = lz4_decompress, | |
| .create_ctx = lz4_create, | |
| .destroy_ctx = lz4_destroy, | |
| .setup_params = lz4_setup_params, | |
| .release_params = lz4_release_params, | |
| .name = "lz4", | |
| }; |