xref: /linux/drivers/block/zram/backend_zstd.c (revision 6a559ecd6e7ec26b691add74eab8ee3eb5dd2a87)
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  */
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 
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 
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 
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 
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 
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 
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 
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