173e7d81aSSergey Senozhatsky // SPDX-License-Identifier: GPL-2.0-or-later
273e7d81aSSergey Senozhatsky
373e7d81aSSergey Senozhatsky #include <linux/kernel.h>
473e7d81aSSergey Senozhatsky #include <linux/slab.h>
573e7d81aSSergey Senozhatsky #include <linux/vmalloc.h>
673e7d81aSSergey Senozhatsky #include <linux/zstd.h>
773e7d81aSSergey Senozhatsky
873e7d81aSSergey Senozhatsky #include "backend_zstd.h"
973e7d81aSSergey Senozhatsky
1073e7d81aSSergey Senozhatsky struct zstd_ctx {
1173e7d81aSSergey Senozhatsky zstd_cctx *cctx;
1273e7d81aSSergey Senozhatsky zstd_dctx *dctx;
1373e7d81aSSergey Senozhatsky void *cctx_mem;
1473e7d81aSSergey Senozhatsky void *dctx_mem;
1573e7d81aSSergey Senozhatsky };
1673e7d81aSSergey Senozhatsky
17b8f03cb7SSergey Senozhatsky struct zstd_params {
18*6a559ecdSSergey Senozhatsky zstd_custom_mem custom_mem;
19*6a559ecdSSergey Senozhatsky zstd_cdict *cdict;
20*6a559ecdSSergey Senozhatsky zstd_ddict *ddict;
21b8f03cb7SSergey Senozhatsky zstd_parameters cprm;
22b8f03cb7SSergey Senozhatsky };
23b8f03cb7SSergey Senozhatsky
24*6a559ecdSSergey Senozhatsky /*
25*6a559ecdSSergey Senozhatsky * For C/D dictionaries we need to provide zstd with zstd_custom_mem,
26*6a559ecdSSergey Senozhatsky * which zstd uses internally to allocate/free memory when needed.
27*6a559ecdSSergey Senozhatsky *
28*6a559ecdSSergey Senozhatsky * This means that allocator.customAlloc() can be called from zcomp_compress()
29*6a559ecdSSergey Senozhatsky * under local-lock (per-CPU compression stream), in which case we must use
30*6a559ecdSSergey Senozhatsky * GFP_ATOMIC.
31*6a559ecdSSergey Senozhatsky *
32*6a559ecdSSergey Senozhatsky * Another complication here is that we can be configured as a swap device.
33*6a559ecdSSergey Senozhatsky */
zstd_custom_alloc(void * opaque,size_t size)34*6a559ecdSSergey Senozhatsky static void *zstd_custom_alloc(void *opaque, size_t size)
35*6a559ecdSSergey Senozhatsky {
36*6a559ecdSSergey Senozhatsky if (!preemptible())
37*6a559ecdSSergey Senozhatsky return kvzalloc(size, GFP_ATOMIC);
38*6a559ecdSSergey Senozhatsky
39*6a559ecdSSergey Senozhatsky return kvzalloc(size, __GFP_KSWAPD_RECLAIM | __GFP_NOWARN);
40*6a559ecdSSergey Senozhatsky }
41*6a559ecdSSergey Senozhatsky
zstd_custom_free(void * opaque,void * address)42*6a559ecdSSergey Senozhatsky static void zstd_custom_free(void *opaque, void *address)
43*6a559ecdSSergey Senozhatsky {
44*6a559ecdSSergey Senozhatsky kvfree(address);
45*6a559ecdSSergey Senozhatsky }
46*6a559ecdSSergey Senozhatsky
zstd_release_params(struct zcomp_params * params)47b8f03cb7SSergey Senozhatsky static void zstd_release_params(struct zcomp_params *params)
48b8f03cb7SSergey Senozhatsky {
49*6a559ecdSSergey Senozhatsky struct zstd_params *zp = params->drv_data;
50*6a559ecdSSergey Senozhatsky
51*6a559ecdSSergey Senozhatsky params->drv_data = NULL;
52*6a559ecdSSergey Senozhatsky if (!zp)
53*6a559ecdSSergey Senozhatsky return;
54*6a559ecdSSergey Senozhatsky
55*6a559ecdSSergey Senozhatsky zstd_free_cdict(zp->cdict);
56*6a559ecdSSergey Senozhatsky zstd_free_ddict(zp->ddict);
57*6a559ecdSSergey Senozhatsky kfree(zp);
58b8f03cb7SSergey Senozhatsky }
59b8f03cb7SSergey Senozhatsky
zstd_setup_params(struct zcomp_params * params)60b8f03cb7SSergey Senozhatsky static int zstd_setup_params(struct zcomp_params *params)
61b8f03cb7SSergey Senozhatsky {
62*6a559ecdSSergey Senozhatsky zstd_compression_parameters prm;
63b8f03cb7SSergey Senozhatsky struct zstd_params *zp;
64b8f03cb7SSergey Senozhatsky
65b8f03cb7SSergey Senozhatsky zp = kzalloc(sizeof(*zp), GFP_KERNEL);
66b8f03cb7SSergey Senozhatsky if (!zp)
67b8f03cb7SSergey Senozhatsky return -ENOMEM;
68b8f03cb7SSergey Senozhatsky
69*6a559ecdSSergey Senozhatsky params->drv_data = zp;
70b8f03cb7SSergey Senozhatsky if (params->level == ZCOMP_PARAM_NO_LEVEL)
71b8f03cb7SSergey Senozhatsky params->level = zstd_default_clevel();
72b8f03cb7SSergey Senozhatsky
73b8f03cb7SSergey Senozhatsky zp->cprm = zstd_get_params(params->level, PAGE_SIZE);
74*6a559ecdSSergey Senozhatsky
75*6a559ecdSSergey Senozhatsky zp->custom_mem.customAlloc = zstd_custom_alloc;
76*6a559ecdSSergey Senozhatsky zp->custom_mem.customFree = zstd_custom_free;
77*6a559ecdSSergey Senozhatsky
78*6a559ecdSSergey Senozhatsky prm = zstd_get_cparams(params->level, PAGE_SIZE,
79*6a559ecdSSergey Senozhatsky params->dict_sz);
80*6a559ecdSSergey Senozhatsky
81*6a559ecdSSergey Senozhatsky zp->cdict = zstd_create_cdict_byreference(params->dict,
82*6a559ecdSSergey Senozhatsky params->dict_sz,
83*6a559ecdSSergey Senozhatsky prm,
84*6a559ecdSSergey Senozhatsky zp->custom_mem);
85*6a559ecdSSergey Senozhatsky if (!zp->cdict)
86*6a559ecdSSergey Senozhatsky goto error;
87*6a559ecdSSergey Senozhatsky
88*6a559ecdSSergey Senozhatsky zp->ddict = zstd_create_ddict_byreference(params->dict,
89*6a559ecdSSergey Senozhatsky params->dict_sz,
90*6a559ecdSSergey Senozhatsky zp->custom_mem);
91*6a559ecdSSergey Senozhatsky if (!zp->ddict)
92*6a559ecdSSergey Senozhatsky goto error;
93b8f03cb7SSergey Senozhatsky
94b8f03cb7SSergey Senozhatsky return 0;
95*6a559ecdSSergey Senozhatsky
96*6a559ecdSSergey Senozhatsky error:
97*6a559ecdSSergey Senozhatsky zstd_release_params(params);
98*6a559ecdSSergey Senozhatsky return -EINVAL;
99b8f03cb7SSergey Senozhatsky }
100b8f03cb7SSergey Senozhatsky
zstd_destroy(struct zcomp_ctx * ctx)1016a81bdfeSSergey Senozhatsky static void zstd_destroy(struct zcomp_ctx *ctx)
10273e7d81aSSergey Senozhatsky {
1036a81bdfeSSergey Senozhatsky struct zstd_ctx *zctx = ctx->context;
1046a81bdfeSSergey Senozhatsky
1056a81bdfeSSergey Senozhatsky if (!zctx)
1066a81bdfeSSergey Senozhatsky return;
10773e7d81aSSergey Senozhatsky
108*6a559ecdSSergey Senozhatsky /*
109*6a559ecdSSergey Senozhatsky * If ->cctx_mem and ->dctx_mem were allocated then we didn't use
110*6a559ecdSSergey Senozhatsky * C/D dictionary and ->cctx / ->dctx were "embedded" into these
111*6a559ecdSSergey Senozhatsky * buffers.
112*6a559ecdSSergey Senozhatsky *
113*6a559ecdSSergey Senozhatsky * If otherwise then we need to explicitly release ->cctx / ->dctx.
114*6a559ecdSSergey Senozhatsky */
115*6a559ecdSSergey Senozhatsky if (zctx->cctx_mem)
11673e7d81aSSergey Senozhatsky vfree(zctx->cctx_mem);
117*6a559ecdSSergey Senozhatsky else
118*6a559ecdSSergey Senozhatsky zstd_free_cctx(zctx->cctx);
119*6a559ecdSSergey Senozhatsky
120*6a559ecdSSergey Senozhatsky if (zctx->dctx_mem)
12173e7d81aSSergey Senozhatsky vfree(zctx->dctx_mem);
122*6a559ecdSSergey Senozhatsky else
123*6a559ecdSSergey Senozhatsky zstd_free_dctx(zctx->dctx);
124*6a559ecdSSergey Senozhatsky
12573e7d81aSSergey Senozhatsky kfree(zctx);
12673e7d81aSSergey Senozhatsky }
12773e7d81aSSergey Senozhatsky
zstd_create(struct zcomp_params * params,struct zcomp_ctx * ctx)1286a81bdfeSSergey Senozhatsky static int zstd_create(struct zcomp_params *params, struct zcomp_ctx *ctx)
12973e7d81aSSergey Senozhatsky {
1306a81bdfeSSergey Senozhatsky struct zstd_ctx *zctx;
131f2bac7adSSergey Senozhatsky zstd_parameters prm;
13273e7d81aSSergey Senozhatsky size_t sz;
13373e7d81aSSergey Senozhatsky
1346a81bdfeSSergey Senozhatsky zctx = kzalloc(sizeof(*zctx), GFP_KERNEL);
1356a81bdfeSSergey Senozhatsky if (!zctx)
1366a81bdfeSSergey Senozhatsky return -ENOMEM;
13773e7d81aSSergey Senozhatsky
1386a81bdfeSSergey Senozhatsky ctx->context = zctx;
139*6a559ecdSSergey Senozhatsky if (params->dict_sz == 0) {
140b8f03cb7SSergey Senozhatsky prm = zstd_get_params(params->level, PAGE_SIZE);
141f2bac7adSSergey Senozhatsky sz = zstd_cctx_workspace_bound(&prm.cParams);
1426a81bdfeSSergey Senozhatsky zctx->cctx_mem = vzalloc(sz);
1436a81bdfeSSergey Senozhatsky if (!zctx->cctx_mem)
14473e7d81aSSergey Senozhatsky goto error;
14573e7d81aSSergey Senozhatsky
1466a81bdfeSSergey Senozhatsky zctx->cctx = zstd_init_cctx(zctx->cctx_mem, sz);
1476a81bdfeSSergey Senozhatsky if (!zctx->cctx)
14873e7d81aSSergey Senozhatsky goto error;
14973e7d81aSSergey Senozhatsky
15073e7d81aSSergey Senozhatsky sz = zstd_dctx_workspace_bound();
1516a81bdfeSSergey Senozhatsky zctx->dctx_mem = vzalloc(sz);
1526a81bdfeSSergey Senozhatsky if (!zctx->dctx_mem)
15373e7d81aSSergey Senozhatsky goto error;
15473e7d81aSSergey Senozhatsky
1556a81bdfeSSergey Senozhatsky zctx->dctx = zstd_init_dctx(zctx->dctx_mem, sz);
1566a81bdfeSSergey Senozhatsky if (!zctx->dctx)
15773e7d81aSSergey Senozhatsky goto error;
158*6a559ecdSSergey Senozhatsky } else {
159*6a559ecdSSergey Senozhatsky struct zstd_params *zp = params->drv_data;
160*6a559ecdSSergey Senozhatsky
161*6a559ecdSSergey Senozhatsky zctx->cctx = zstd_create_cctx_advanced(zp->custom_mem);
162*6a559ecdSSergey Senozhatsky if (!zctx->cctx)
163*6a559ecdSSergey Senozhatsky goto error;
164*6a559ecdSSergey Senozhatsky
165*6a559ecdSSergey Senozhatsky zctx->dctx = zstd_create_dctx_advanced(zp->custom_mem);
166*6a559ecdSSergey Senozhatsky if (!zctx->dctx)
167*6a559ecdSSergey Senozhatsky goto error;
168*6a559ecdSSergey Senozhatsky }
16973e7d81aSSergey Senozhatsky
1706a81bdfeSSergey Senozhatsky return 0;
17173e7d81aSSergey Senozhatsky
17273e7d81aSSergey Senozhatsky error:
173*6a559ecdSSergey Senozhatsky zstd_release_params(params);
17473e7d81aSSergey Senozhatsky zstd_destroy(ctx);
1756a81bdfeSSergey Senozhatsky return -EINVAL;
17673e7d81aSSergey Senozhatsky }
17773e7d81aSSergey Senozhatsky
zstd_compress(struct zcomp_params * params,struct zcomp_ctx * ctx,struct zcomp_req * req)178b8f03cb7SSergey Senozhatsky static int zstd_compress(struct zcomp_params *params, struct zcomp_ctx *ctx,
179b8f03cb7SSergey Senozhatsky struct zcomp_req *req)
18073e7d81aSSergey Senozhatsky {
181b8f03cb7SSergey Senozhatsky struct zstd_params *zp = params->drv_data;
1826a81bdfeSSergey Senozhatsky struct zstd_ctx *zctx = ctx->context;
18373e7d81aSSergey Senozhatsky size_t ret;
18473e7d81aSSergey Senozhatsky
185*6a559ecdSSergey Senozhatsky if (params->dict_sz == 0)
18652c7b4e2SSergey Senozhatsky ret = zstd_compress_cctx(zctx->cctx, req->dst, req->dst_len,
187b8f03cb7SSergey Senozhatsky req->src, req->src_len, &zp->cprm);
188*6a559ecdSSergey Senozhatsky else
189*6a559ecdSSergey Senozhatsky ret = zstd_compress_using_cdict(zctx->cctx, req->dst,
190*6a559ecdSSergey Senozhatsky req->dst_len, req->src,
191*6a559ecdSSergey Senozhatsky req->src_len,
192*6a559ecdSSergey Senozhatsky zp->cdict);
19373e7d81aSSergey Senozhatsky if (zstd_is_error(ret))
19473e7d81aSSergey Senozhatsky return -EINVAL;
19552c7b4e2SSergey Senozhatsky req->dst_len = ret;
19673e7d81aSSergey Senozhatsky return 0;
19773e7d81aSSergey Senozhatsky }
19873e7d81aSSergey Senozhatsky
zstd_decompress(struct zcomp_params * params,struct zcomp_ctx * ctx,struct zcomp_req * req)199b8f03cb7SSergey Senozhatsky static int zstd_decompress(struct zcomp_params *params, struct zcomp_ctx *ctx,
200b8f03cb7SSergey Senozhatsky struct zcomp_req *req)
20173e7d81aSSergey Senozhatsky {
202*6a559ecdSSergey Senozhatsky struct zstd_params *zp = params->drv_data;
2036a81bdfeSSergey Senozhatsky struct zstd_ctx *zctx = ctx->context;
20473e7d81aSSergey Senozhatsky size_t ret;
20573e7d81aSSergey Senozhatsky
206*6a559ecdSSergey Senozhatsky if (params->dict_sz == 0)
20752c7b4e2SSergey Senozhatsky ret = zstd_decompress_dctx(zctx->dctx, req->dst, req->dst_len,
20852c7b4e2SSergey Senozhatsky req->src, req->src_len);
209*6a559ecdSSergey Senozhatsky else
210*6a559ecdSSergey Senozhatsky ret = zstd_decompress_using_ddict(zctx->dctx, req->dst,
211*6a559ecdSSergey Senozhatsky req->dst_len, req->src,
212*6a559ecdSSergey Senozhatsky req->src_len, zp->ddict);
21373e7d81aSSergey Senozhatsky if (zstd_is_error(ret))
21473e7d81aSSergey Senozhatsky return -EINVAL;
21573e7d81aSSergey Senozhatsky return 0;
21673e7d81aSSergey Senozhatsky }
21773e7d81aSSergey Senozhatsky
21873e7d81aSSergey Senozhatsky const struct zcomp_ops backend_zstd = {
21973e7d81aSSergey Senozhatsky .compress = zstd_compress,
22073e7d81aSSergey Senozhatsky .decompress = zstd_decompress,
22173e7d81aSSergey Senozhatsky .create_ctx = zstd_create,
22273e7d81aSSergey Senozhatsky .destroy_ctx = zstd_destroy,
223b8f03cb7SSergey Senozhatsky .setup_params = zstd_setup_params,
224b8f03cb7SSergey Senozhatsky .release_params = zstd_release_params,
22573e7d81aSSergey Senozhatsky .name = "zstd",
22673e7d81aSSergey Senozhatsky };
227