1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 #define pr_fmt(fmt) "lz4hc: " fmt
4
5 #include <linux/kernel.h>
6 #include <linux/lz4.h>
7 #include <linux/slab.h>
8 #include <linux/vmalloc.h>
9
10 #include "backend_lz4hc.h"
11
12 struct lz4hc_ctx {
13 void *mem;
14
15 LZ4_streamDecode_t *dstrm;
16 LZ4_streamHC_t *cstrm;
17 };
18
lz4hc_release_params(struct zcomp_params * params)19 static void lz4hc_release_params(struct zcomp_params *params)
20 {
21 }
22
lz4hc_setup_params(struct zcomp_params * params)23 static int lz4hc_setup_params(struct zcomp_params *params)
24 {
25 if (params->level == ZCOMP_PARAM_NOT_SET) {
26 params->level = LZ4HC_DEFAULT_CLEVEL;
27 } else if (params->level < 1 || params->level > LZ4HC_MAX_CLEVEL) {
28 /*
29 * Use < 1 rather than < LZ4HC_MIN_CLEVEL here because
30 * LZ4HC_compress_generic() only clamps levels below 1
31 * (levels 1 and 2 are valid). LZ4HC_MIN_CLEVEL (3) is
32 * advisory and not enforced by the library.
33 */
34 pr_err("invalid compression level %d\n", params->level);
35 return -EINVAL;
36 }
37
38 return 0;
39 }
40
lz4hc_destroy(struct zcomp_ctx * ctx)41 static void lz4hc_destroy(struct zcomp_ctx *ctx)
42 {
43 struct lz4hc_ctx *zctx = ctx->context;
44
45 if (!zctx)
46 return;
47
48 kfree(zctx->dstrm);
49 kfree(zctx->cstrm);
50 vfree(zctx->mem);
51 kfree(zctx);
52 }
53
lz4hc_create(struct zcomp_params * params,struct zcomp_ctx * ctx)54 static int lz4hc_create(struct zcomp_params *params, struct zcomp_ctx *ctx)
55 {
56 struct lz4hc_ctx *zctx;
57
58 zctx = kzalloc_obj(*zctx);
59 if (!zctx)
60 return -ENOMEM;
61
62 ctx->context = zctx;
63 if (params->dict_sz == 0) {
64 zctx->mem = vmalloc(LZ4HC_MEM_COMPRESS);
65 if (!zctx->mem)
66 goto error;
67 } else {
68 zctx->dstrm = kzalloc_obj(*zctx->dstrm);
69 if (!zctx->dstrm)
70 goto error;
71
72 zctx->cstrm = kzalloc_obj(*zctx->cstrm);
73 if (!zctx->cstrm)
74 goto error;
75 }
76
77 return 0;
78
79 error:
80 lz4hc_destroy(ctx);
81 return -EINVAL;
82 }
83
lz4hc_compress(struct zcomp_params * params,struct zcomp_ctx * ctx,struct zcomp_req * req)84 static int lz4hc_compress(struct zcomp_params *params, struct zcomp_ctx *ctx,
85 struct zcomp_req *req)
86 {
87 struct lz4hc_ctx *zctx = ctx->context;
88 int ret;
89
90 if (!zctx->cstrm) {
91 ret = LZ4_compress_HC(req->src, req->dst, req->src_len,
92 req->dst_len, params->level,
93 zctx->mem);
94 } else {
95 /* Cstrm needs to be reset */
96 LZ4_resetStreamHC(zctx->cstrm, params->level);
97 ret = LZ4_loadDictHC(zctx->cstrm, params->dict,
98 params->dict_sz);
99 if (ret != params->dict_sz)
100 return -EINVAL;
101 ret = LZ4_compress_HC_continue(zctx->cstrm, req->src, req->dst,
102 req->src_len, req->dst_len);
103 }
104 if (!ret)
105 return -EINVAL;
106 req->dst_len = ret;
107 return 0;
108 }
109
lz4hc_decompress(struct zcomp_params * params,struct zcomp_ctx * ctx,struct zcomp_req * req)110 static int lz4hc_decompress(struct zcomp_params *params, struct zcomp_ctx *ctx,
111 struct zcomp_req *req)
112 {
113 struct lz4hc_ctx *zctx = ctx->context;
114 int ret;
115
116 if (!zctx->dstrm) {
117 ret = LZ4_decompress_safe(req->src, req->dst, req->src_len,
118 req->dst_len);
119 } else {
120 /* Dstrm needs to be reset */
121 ret = LZ4_setStreamDecode(zctx->dstrm, params->dict,
122 params->dict_sz);
123 if (!ret)
124 return -EINVAL;
125 ret = LZ4_decompress_safe_continue(zctx->dstrm, req->src,
126 req->dst, req->src_len,
127 req->dst_len);
128 }
129 if (ret < 0)
130 return -EINVAL;
131 return 0;
132 }
133
134 const struct zcomp_ops backend_lz4hc = {
135 .compress = lz4hc_compress,
136 .decompress = lz4hc_decompress,
137 .create_ctx = lz4hc_create,
138 .destroy_ctx = lz4hc_destroy,
139 .setup_params = lz4hc_setup_params,
140 .release_params = lz4hc_release_params,
141 .name = "lz4hc",
142 };
143