xref: /linux/drivers/block/zram/backend_lz4hc.c (revision 3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d)
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 
19 static void lz4hc_release_params(struct zcomp_params *params)
20 {
21 }
22 
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 
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 
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 
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 
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