1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 #define pr_fmt(fmt) "lz4: " 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_lz4.h"
11
12 struct lz4_ctx {
13 void *mem;
14
15 LZ4_streamDecode_t *dstrm;
16 LZ4_stream_t *cstrm;
17 };
18
lz4_release_params(struct zcomp_params * params)19 static void lz4_release_params(struct zcomp_params *params)
20 {
21 LZ4_stream_t *dict_stream = params->drv_data;
22
23 params->drv_data = NULL;
24 if (!dict_stream)
25 return;
26
27 kfree(dict_stream);
28 }
29
lz4_setup_params(struct zcomp_params * params)30 static int lz4_setup_params(struct zcomp_params *params)
31 {
32 LZ4_stream_t *dict_stream;
33 int ret;
34
35 if (params->level == ZCOMP_PARAM_NOT_SET) {
36 params->level = LZ4_ACCELERATION_DEFAULT;
37 } else if (params->level < LZ4_ACCELERATION_DEFAULT) {
38 pr_err("invalid compression level %d\n", params->level);
39 return -EINVAL;
40 }
41
42 if (!params->dict || !params->dict_sz)
43 return 0;
44
45 dict_stream = kzalloc_obj(*dict_stream);
46 if (!dict_stream)
47 return -ENOMEM;
48
49 ret = LZ4_loadDict(dict_stream,
50 params->dict, params->dict_sz);
51 if (ret != params->dict_sz) {
52 kfree(dict_stream);
53 return -EINVAL;
54 }
55 params->drv_data = dict_stream;
56
57 return 0;
58 }
59
lz4_destroy(struct zcomp_ctx * ctx)60 static void lz4_destroy(struct zcomp_ctx *ctx)
61 {
62 struct lz4_ctx *zctx = ctx->context;
63
64 if (!zctx)
65 return;
66
67 vfree(zctx->mem);
68 kfree(zctx->dstrm);
69 kfree(zctx->cstrm);
70 kfree(zctx);
71 }
72
lz4_create(struct zcomp_params * params,struct zcomp_ctx * ctx)73 static int lz4_create(struct zcomp_params *params, struct zcomp_ctx *ctx)
74 {
75 struct lz4_ctx *zctx;
76
77 zctx = kzalloc_obj(*zctx);
78 if (!zctx)
79 return -ENOMEM;
80
81 ctx->context = zctx;
82 if (params->dict_sz == 0) {
83 zctx->mem = vmalloc(LZ4_MEM_COMPRESS);
84 if (!zctx->mem)
85 goto error;
86 } else {
87 zctx->dstrm = kzalloc_obj(*zctx->dstrm);
88 if (!zctx->dstrm)
89 goto error;
90
91 zctx->cstrm = kzalloc_obj(*zctx->cstrm);
92 if (!zctx->cstrm)
93 goto error;
94 }
95
96 return 0;
97
98 error:
99 lz4_destroy(ctx);
100 return -ENOMEM;
101 }
102
lz4_compress(struct zcomp_params * params,struct zcomp_ctx * ctx,struct zcomp_req * req)103 static int lz4_compress(struct zcomp_params *params, struct zcomp_ctx *ctx,
104 struct zcomp_req *req)
105 {
106 struct lz4_ctx *zctx = ctx->context;
107 int ret;
108
109 if (!zctx->cstrm) {
110 ret = LZ4_compress_fast(req->src, req->dst, req->src_len,
111 req->dst_len, params->level,
112 zctx->mem);
113 } else {
114 /* Cstrm needs to be reset */
115 memcpy(zctx->cstrm, params->drv_data, sizeof(*zctx->cstrm));
116 ret = LZ4_compress_fast_continue(zctx->cstrm, req->src,
117 req->dst, req->src_len,
118 req->dst_len, params->level);
119 }
120 if (!ret)
121 return -EINVAL;
122 req->dst_len = ret;
123 return 0;
124 }
125
lz4_decompress(struct zcomp_params * params,struct zcomp_ctx * ctx,struct zcomp_req * req)126 static int lz4_decompress(struct zcomp_params *params, struct zcomp_ctx *ctx,
127 struct zcomp_req *req)
128 {
129 struct lz4_ctx *zctx = ctx->context;
130 int ret;
131
132 if (!zctx->dstrm) {
133 ret = LZ4_decompress_safe(req->src, req->dst, req->src_len,
134 req->dst_len);
135 } else {
136 /* Dstrm needs to be reset */
137 ret = LZ4_setStreamDecode(zctx->dstrm, params->dict,
138 params->dict_sz);
139 if (!ret)
140 return -EINVAL;
141 ret = LZ4_decompress_safe_continue(zctx->dstrm, req->src,
142 req->dst, req->src_len,
143 req->dst_len);
144 }
145 if (ret < 0)
146 return -EINVAL;
147 return 0;
148 }
149
150 const struct zcomp_ops backend_lz4 = {
151 .compress = lz4_compress,
152 .decompress = lz4_decompress,
153 .create_ctx = lz4_create,
154 .destroy_ctx = lz4_destroy,
155 .setup_params = lz4_setup_params,
156 .release_params = lz4_release_params,
157 .name = "lz4",
158 };
159