xref: /linux/drivers/block/zram/zcomp.c (revision c60a4ef544464f03368ef399ccb8fc115ba9f070)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 
3 #include <linux/kernel.h>
4 #include <linux/string.h>
5 #include <linux/err.h>
6 #include <linux/slab.h>
7 #include <linux/wait.h>
8 #include <linux/sched.h>
9 #include <linux/cpu.h>
10 #include <linux/crypto.h>
11 #include <linux/vmalloc.h>
12 
13 #include "zcomp.h"
14 
15 #include "backend_lzo.h"
16 #include "backend_lzorle.h"
17 #include "backend_lz4.h"
18 #include "backend_lz4hc.h"
19 
20 static const struct zcomp_ops *backends[] = {
21 #if IS_ENABLED(CONFIG_ZRAM_BACKEND_LZO)
22 	&backend_lzorle,
23 	&backend_lzo,
24 #endif
25 #if IS_ENABLED(CONFIG_ZRAM_BACKEND_LZ4)
26 	&backend_lz4,
27 #endif
28 #if IS_ENABLED(CONFIG_ZRAM_BACKEND_LZ4HC)
29 	&backend_lz4hc,
30 #endif
31 	NULL
32 };
33 
34 static void zcomp_strm_free(struct zcomp *comp, struct zcomp_strm *zstrm)
35 {
36 	if (zstrm->ctx)
37 		comp->ops->destroy_ctx(zstrm->ctx);
38 	vfree(zstrm->buffer);
39 	zstrm->ctx = NULL;
40 	zstrm->buffer = NULL;
41 }
42 
43 static int zcomp_strm_init(struct zcomp *comp, struct zcomp_strm *zstrm)
44 {
45 	zstrm->ctx = comp->ops->create_ctx();
46 
47 	/*
48 	 * allocate 2 pages. 1 for compressed data, plus 1 extra for the
49 	 * case when compressed size is larger than the original one
50 	 */
51 	zstrm->buffer = vzalloc(2 * PAGE_SIZE);
52 	if (!zstrm->ctx || !zstrm->buffer) {
53 		zcomp_strm_free(comp, zstrm);
54 		return -ENOMEM;
55 	}
56 	return 0;
57 }
58 
59 static const struct zcomp_ops *lookup_backend_ops(const char *comp)
60 {
61 	int i = 0;
62 
63 	while (backends[i]) {
64 		if (sysfs_streq(comp, backends[i]->name))
65 			break;
66 		i++;
67 	}
68 	return backends[i];
69 }
70 
71 bool zcomp_available_algorithm(const char *comp)
72 {
73 	return lookup_backend_ops(comp) != NULL;
74 }
75 
76 /* show available compressors */
77 ssize_t zcomp_available_show(const char *comp, char *buf)
78 {
79 	ssize_t sz = 0;
80 	int i;
81 
82 	for (i = 0; i < ARRAY_SIZE(backends) - 1; i++) {
83 		if (!strcmp(comp, backends[i]->name)) {
84 			sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2,
85 					"[%s] ", backends[i]->name);
86 		} else {
87 			sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2,
88 					"%s ", backends[i]->name);
89 		}
90 	}
91 
92 	sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n");
93 	return sz;
94 }
95 
96 struct zcomp_strm *zcomp_stream_get(struct zcomp *comp)
97 {
98 	local_lock(&comp->stream->lock);
99 	return this_cpu_ptr(comp->stream);
100 }
101 
102 void zcomp_stream_put(struct zcomp *comp)
103 {
104 	local_unlock(&comp->stream->lock);
105 }
106 
107 int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm,
108 		   const void *src, unsigned int *dst_len)
109 {
110 	/* The dst buffer should always be 2 * PAGE_SIZE */
111 	size_t dlen = 2 * PAGE_SIZE;
112 	int ret;
113 
114 	ret = comp->ops->compress(zstrm->ctx, src, PAGE_SIZE,
115 				  zstrm->buffer, &dlen);
116 	if (!ret)
117 		*dst_len = dlen;
118 	return ret;
119 }
120 
121 int zcomp_decompress(struct zcomp *comp, struct zcomp_strm *zstrm,
122 		     const void *src, unsigned int src_len, void *dst)
123 {
124 	return comp->ops->decompress(zstrm->ctx, src, src_len,
125 				     dst, PAGE_SIZE);
126 }
127 
128 int zcomp_cpu_up_prepare(unsigned int cpu, struct hlist_node *node)
129 {
130 	struct zcomp *comp = hlist_entry(node, struct zcomp, node);
131 	struct zcomp_strm *zstrm;
132 	int ret;
133 
134 	zstrm = per_cpu_ptr(comp->stream, cpu);
135 	local_lock_init(&zstrm->lock);
136 
137 	ret = zcomp_strm_init(comp, zstrm);
138 	if (ret)
139 		pr_err("Can't allocate a compression stream\n");
140 	return ret;
141 }
142 
143 int zcomp_cpu_dead(unsigned int cpu, struct hlist_node *node)
144 {
145 	struct zcomp *comp = hlist_entry(node, struct zcomp, node);
146 	struct zcomp_strm *zstrm;
147 
148 	zstrm = per_cpu_ptr(comp->stream, cpu);
149 	zcomp_strm_free(comp, zstrm);
150 	return 0;
151 }
152 
153 static int zcomp_init(struct zcomp *comp)
154 {
155 	int ret;
156 
157 	comp->stream = alloc_percpu(struct zcomp_strm);
158 	if (!comp->stream)
159 		return -ENOMEM;
160 
161 	ret = cpuhp_state_add_instance(CPUHP_ZCOMP_PREPARE, &comp->node);
162 	if (ret < 0)
163 		goto cleanup;
164 	return 0;
165 
166 cleanup:
167 	free_percpu(comp->stream);
168 	return ret;
169 }
170 
171 void zcomp_destroy(struct zcomp *comp)
172 {
173 	cpuhp_state_remove_instance(CPUHP_ZCOMP_PREPARE, &comp->node);
174 	free_percpu(comp->stream);
175 	kfree(comp);
176 }
177 
178 struct zcomp *zcomp_create(const char *alg)
179 {
180 	struct zcomp *comp;
181 	int error;
182 
183 	comp = kzalloc(sizeof(struct zcomp), GFP_KERNEL);
184 	if (!comp)
185 		return ERR_PTR(-ENOMEM);
186 
187 	comp->ops = lookup_backend_ops(alg);
188 	if (!comp->ops) {
189 		kfree(comp);
190 		return ERR_PTR(-EINVAL);
191 	}
192 
193 	error = zcomp_init(comp);
194 	if (error) {
195 		kfree(comp);
196 		return ERR_PTR(error);
197 	}
198 	return comp;
199 }
200