blob: c084a7f9763d0b3a61d43e1d5bd2e3fa72c10a55 [file] [log] [blame]
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -07001/*
2 * Copyright (C) 2014 Sergey Senozhatsky.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 */
9
10#include <linux/kernel.h>
11#include <linux/string.h>
Sergey Senozhatskyfcfa8d92014-04-07 15:38:20 -070012#include <linux/err.h>
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -070013#include <linux/slab.h>
14#include <linux/wait.h>
15#include <linux/sched.h>
Sergey Senozhatskyda9556a2016-05-20 16:59:51 -070016#include <linux/cpu.h>
Sergey Senozhatskyebaf9ab2016-07-26 15:22:45 -070017#include <linux/crypto.h>
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -070018
19#include "zcomp.h"
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -070020
Sergey Senozhatskyebaf9ab2016-07-26 15:22:45 -070021static const char * const backends[] = {
22 "lzo",
Sergey Senozhatskyce1ed9f2016-07-26 15:22:54 -070023#if IS_ENABLED(CONFIG_CRYPTO_LZ4)
Sergey Senozhatskyebaf9ab2016-07-26 15:22:45 -070024 "lz4",
Sergey Senozhatsky6e766682014-04-07 15:38:18 -070025#endif
Sergey Senozhatskyeb9f56d2016-07-26 15:22:56 -070026#if IS_ENABLED(CONFIG_CRYPTO_DEFLATE)
27 "deflate",
28#endif
29#if IS_ENABLED(CONFIG_CRYPTO_LZ4HC)
30 "lz4hc",
31#endif
32#if IS_ENABLED(CONFIG_CRYPTO_842)
33 "842",
34#endif
Sergey Senozhatskyb3873ca2017-11-15 17:33:49 -080035#if IS_ENABLED(CONFIG_CRYPTO_ZSTD)
36 "zstd",
37#endif
Sergey Senozhatskye46b8a02014-04-07 15:38:17 -070038 NULL
39};
40
Sergey Senozhatskyebaf9ab2016-07-26 15:22:45 -070041static void zcomp_strm_free(struct zcomp_strm *zstrm)
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -070042{
Sergey Senozhatskyebaf9ab2016-07-26 15:22:45 -070043 if (!IS_ERR_OR_NULL(zstrm->tfm))
44 crypto_free_comp(zstrm->tfm);
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -070045 free_pages((unsigned long)zstrm->buffer, 1);
46 kfree(zstrm);
47}
48
49/*
Sergey Senozhatskyebaf9ab2016-07-26 15:22:45 -070050 * allocate new zcomp_strm structure with ->tfm initialized by
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -070051 * backend, return NULL on error
52 */
Sergey Senozhatsky16d37722016-07-26 15:22:59 -070053static struct zcomp_strm *zcomp_strm_alloc(struct zcomp *comp)
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -070054{
Sergey Senozhatsky16d37722016-07-26 15:22:59 -070055 struct zcomp_strm *zstrm = kmalloc(sizeof(*zstrm), GFP_KERNEL);
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -070056 if (!zstrm)
57 return NULL;
58
Sergey Senozhatskyebaf9ab2016-07-26 15:22:45 -070059 zstrm->tfm = crypto_alloc_comp(comp->name, 0, 0);
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -070060 /*
61 * allocate 2 pages. 1 for compressed data, plus 1 extra for the
62 * case when compressed size is larger than the original one
63 */
Sergey Senozhatsky16d37722016-07-26 15:22:59 -070064 zstrm->buffer = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
Sergey Senozhatskyebaf9ab2016-07-26 15:22:45 -070065 if (IS_ERR_OR_NULL(zstrm->tfm) || !zstrm->buffer) {
66 zcomp_strm_free(zstrm);
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -070067 zstrm = NULL;
68 }
69 return zstrm;
70}
71
Sergey Senozhatsky415403b2016-07-26 15:22:48 -070072bool zcomp_available_algorithm(const char *comp)
Sergey Senozhatskye46b8a02014-04-07 15:38:17 -070073{
Sergey Senozhatskye46b8a02014-04-07 15:38:17 -070074 int i = 0;
75
76 while (backends[i]) {
Sergey Senozhatsky415403b2016-07-26 15:22:48 -070077 if (sysfs_streq(comp, backends[i]))
78 return true;
Sergey Senozhatskye46b8a02014-04-07 15:38:17 -070079 i++;
80 }
Sergey Senozhatsky415403b2016-07-26 15:22:48 -070081
82 /*
83 * Crypto does not ignore a trailing new line symbol,
84 * so make sure you don't supply a string containing
85 * one.
86 * This also means that we permit zcomp initialisation
87 * with any compressing algorithm known to crypto api.
88 */
89 return crypto_has_comp(comp, 0, 0) == 1;
Sergey Senozhatskye46b8a02014-04-07 15:38:17 -070090}
91
Sergey Senozhatsky415403b2016-07-26 15:22:48 -070092/* show available compressors */
93ssize_t zcomp_available_show(const char *comp, char *buf)
Sergey Senozhatskyd93435c2015-06-25 15:00:32 -070094{
Sergey Senozhatsky415403b2016-07-26 15:22:48 -070095 bool known_algorithm = false;
96 ssize_t sz = 0;
97 int i = 0;
98
99 for (; backends[i]; i++) {
100 if (!strcmp(comp, backends[i])) {
101 known_algorithm = true;
102 sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2,
103 "[%s] ", backends[i]);
104 } else {
105 sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2,
106 "%s ", backends[i]);
107 }
108 }
109
110 /*
111 * Out-of-tree module known to crypto api or a missing
112 * entry in `backends'.
113 */
114 if (!known_algorithm && crypto_has_comp(comp, 0, 0) == 1)
115 sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2,
116 "[%s] ", comp);
117
118 sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n");
119 return sz;
Sergey Senozhatskyd93435c2015-06-25 15:00:32 -0700120}
121
Sergey Senozhatsky2aea8492016-07-26 15:22:42 -0700122struct zcomp_strm *zcomp_stream_get(struct zcomp *comp)
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -0700123{
Sergey Senozhatskyda9556a2016-05-20 16:59:51 -0700124 return *get_cpu_ptr(comp->stream);
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -0700125}
126
Sergey Senozhatsky2aea8492016-07-26 15:22:42 -0700127void zcomp_stream_put(struct zcomp *comp)
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -0700128{
Sergey Senozhatskyda9556a2016-05-20 16:59:51 -0700129 put_cpu_ptr(comp->stream);
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -0700130}
131
Sergey Senozhatskyebaf9ab2016-07-26 15:22:45 -0700132int zcomp_compress(struct zcomp_strm *zstrm,
133 const void *src, unsigned int *dst_len)
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -0700134{
Sergey Senozhatskyebaf9ab2016-07-26 15:22:45 -0700135 /*
136 * Our dst memory (zstrm->buffer) is always `2 * PAGE_SIZE' sized
137 * because sometimes we can endup having a bigger compressed data
138 * due to various reasons: for example compression algorithms tend
139 * to add some padding to the compressed buffer. Speaking of padding,
140 * comp algorithm `842' pads the compressed length to multiple of 8
141 * and returns -ENOSP when the dst memory is not big enough, which
142 * is not something that ZRAM wants to see. We can handle the
143 * `compressed_size > PAGE_SIZE' case easily in ZRAM, but when we
144 * receive -ERRNO from the compressing backend we can't help it
145 * anymore. To make `842' happy we need to tell the exact size of
146 * the dst buffer, zram_drv will take care of the fact that
147 * compressed buffer is too big.
148 */
149 *dst_len = PAGE_SIZE * 2;
150
151 return crypto_comp_compress(zstrm->tfm,
152 src, PAGE_SIZE,
153 zstrm->buffer, dst_len);
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -0700154}
155
Sergey Senozhatskyebaf9ab2016-07-26 15:22:45 -0700156int zcomp_decompress(struct zcomp_strm *zstrm,
157 const void *src, unsigned int src_len, void *dst)
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -0700158{
Sergey Senozhatskyebaf9ab2016-07-26 15:22:45 -0700159 unsigned int dst_len = PAGE_SIZE;
160
161 return crypto_comp_decompress(zstrm->tfm,
162 src, src_len,
163 dst, &dst_len);
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -0700164}
165
Sergey Senozhatskyda9556a2016-05-20 16:59:51 -0700166static int __zcomp_cpu_notifier(struct zcomp *comp,
167 unsigned long action, unsigned long cpu)
168{
169 struct zcomp_strm *zstrm;
170
171 switch (action) {
172 case CPU_UP_PREPARE:
173 if (WARN_ON(*per_cpu_ptr(comp->stream, cpu)))
174 break;
Sergey Senozhatsky16d37722016-07-26 15:22:59 -0700175 zstrm = zcomp_strm_alloc(comp);
Sergey Senozhatskyda9556a2016-05-20 16:59:51 -0700176 if (IS_ERR_OR_NULL(zstrm)) {
177 pr_err("Can't allocate a compression stream\n");
178 return NOTIFY_BAD;
179 }
180 *per_cpu_ptr(comp->stream, cpu) = zstrm;
181 break;
182 case CPU_DEAD:
183 case CPU_UP_CANCELED:
184 zstrm = *per_cpu_ptr(comp->stream, cpu);
185 if (!IS_ERR_OR_NULL(zstrm))
Sergey Senozhatskyebaf9ab2016-07-26 15:22:45 -0700186 zcomp_strm_free(zstrm);
Sergey Senozhatskyda9556a2016-05-20 16:59:51 -0700187 *per_cpu_ptr(comp->stream, cpu) = NULL;
188 break;
189 default:
190 break;
191 }
192 return NOTIFY_OK;
193}
194
195static int zcomp_cpu_notifier(struct notifier_block *nb,
196 unsigned long action, void *pcpu)
197{
198 unsigned long cpu = (unsigned long)pcpu;
199 struct zcomp *comp = container_of(nb, typeof(*comp), notifier);
200
201 return __zcomp_cpu_notifier(comp, action, cpu);
202}
203
204static int zcomp_init(struct zcomp *comp)
205{
206 unsigned long cpu;
207 int ret;
208
209 comp->notifier.notifier_call = zcomp_cpu_notifier;
210
211 comp->stream = alloc_percpu(struct zcomp_strm *);
212 if (!comp->stream)
213 return -ENOMEM;
214
215 cpu_notifier_register_begin();
216 for_each_online_cpu(cpu) {
217 ret = __zcomp_cpu_notifier(comp, CPU_UP_PREPARE, cpu);
218 if (ret == NOTIFY_BAD)
219 goto cleanup;
220 }
221 __register_cpu_notifier(&comp->notifier);
222 cpu_notifier_register_done();
223 return 0;
224
225cleanup:
226 for_each_online_cpu(cpu)
227 __zcomp_cpu_notifier(comp, CPU_UP_CANCELED, cpu);
228 cpu_notifier_register_done();
229 return -ENOMEM;
230}
231
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -0700232void zcomp_destroy(struct zcomp *comp)
233{
Sergey Senozhatskyda9556a2016-05-20 16:59:51 -0700234 unsigned long cpu;
235
236 cpu_notifier_register_begin();
237 for_each_online_cpu(cpu)
238 __zcomp_cpu_notifier(comp, CPU_UP_CANCELED, cpu);
239 __unregister_cpu_notifier(&comp->notifier);
240 cpu_notifier_register_done();
241
242 free_percpu(comp->stream);
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -0700243 kfree(comp);
244}
245
246/*
247 * search available compressors for requested algorithm.
Sergey Senozhatskyfcfa8d92014-04-07 15:38:20 -0700248 * allocate new zcomp and initialize it. return compressing
249 * backend pointer or ERR_PTR if things went bad. ERR_PTR(-EINVAL)
250 * if requested algorithm is not supported, ERR_PTR(-ENOMEM) in
Luis Henriques3aaf14d2015-09-17 16:01:40 -0700251 * case of allocation error, or any other error potentially
Sergey Senozhatskyda9556a2016-05-20 16:59:51 -0700252 * returned by zcomp_init().
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -0700253 */
Sergey Senozhatskyda9556a2016-05-20 16:59:51 -0700254struct zcomp *zcomp_create(const char *compress)
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -0700255{
256 struct zcomp *comp;
Luis Henriques3aaf14d2015-09-17 16:01:40 -0700257 int error;
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -0700258
Sergey Senozhatsky415403b2016-07-26 15:22:48 -0700259 if (!zcomp_available_algorithm(compress))
Sergey Senozhatskyfcfa8d92014-04-07 15:38:20 -0700260 return ERR_PTR(-EINVAL);
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -0700261
262 comp = kzalloc(sizeof(struct zcomp), GFP_KERNEL);
263 if (!comp)
Sergey Senozhatskyfcfa8d92014-04-07 15:38:20 -0700264 return ERR_PTR(-ENOMEM);
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -0700265
Sergey Senozhatsky415403b2016-07-26 15:22:48 -0700266 comp->name = compress;
Sergey Senozhatskyda9556a2016-05-20 16:59:51 -0700267 error = zcomp_init(comp);
Luis Henriques3aaf14d2015-09-17 16:01:40 -0700268 if (error) {
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -0700269 kfree(comp);
Luis Henriques3aaf14d2015-09-17 16:01:40 -0700270 return ERR_PTR(error);
Sergey Senozhatskye7e1ef42014-04-07 15:38:11 -0700271 }
272 return comp;
273}