12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2e7e1ef43SSergey Senozhatsky /*
3e7e1ef43SSergey Senozhatsky * Copyright (C) 2014 Sergey Senozhatsky.
4e7e1ef43SSergey Senozhatsky */
5e7e1ef43SSergey Senozhatsky
6e7e1ef43SSergey Senozhatsky #include <linux/kernel.h>
7e7e1ef43SSergey Senozhatsky #include <linux/string.h>
8fcfa8d95SSergey Senozhatsky #include <linux/err.h>
9e7e1ef43SSergey Senozhatsky #include <linux/slab.h>
10e7e1ef43SSergey Senozhatsky #include <linux/wait.h>
11e7e1ef43SSergey Senozhatsky #include <linux/sched.h>
12da9556a2SSergey Senozhatsky #include <linux/cpu.h>
13ebaf9ab5SSergey Senozhatsky #include <linux/crypto.h>
14e7e1ef43SSergey Senozhatsky
15e7e1ef43SSergey Senozhatsky #include "zcomp.h"
16e7e1ef43SSergey Senozhatsky
17ebaf9ab5SSergey Senozhatsky static const char * const backends[] = {
183d711a38SRui Salvaterra #if IS_ENABLED(CONFIG_CRYPTO_LZO)
19ebaf9ab5SSergey Senozhatsky "lzo",
2045ec975eSDave Rodgman "lzo-rle",
213d711a38SRui Salvaterra #endif
22ce1ed9f9SSergey Senozhatsky #if IS_ENABLED(CONFIG_CRYPTO_LZ4)
23ebaf9ab5SSergey Senozhatsky "lz4",
246e76668eSSergey Senozhatsky #endif
25eb9f56d8SSergey Senozhatsky #if IS_ENABLED(CONFIG_CRYPTO_LZ4HC)
26eb9f56d8SSergey Senozhatsky "lz4hc",
27eb9f56d8SSergey Senozhatsky #endif
28eb9f56d8SSergey Senozhatsky #if IS_ENABLED(CONFIG_CRYPTO_842)
29eb9f56d8SSergey Senozhatsky "842",
30eb9f56d8SSergey Senozhatsky #endif
315ef3a8b1SSergey Senozhatsky #if IS_ENABLED(CONFIG_CRYPTO_ZSTD)
325ef3a8b1SSergey Senozhatsky "zstd",
335ef3a8b1SSergey Senozhatsky #endif
34e46b8a03SSergey Senozhatsky };
35e46b8a03SSergey Senozhatsky
zcomp_strm_free(struct zcomp_strm * zstrm)36ebaf9ab5SSergey Senozhatsky static void zcomp_strm_free(struct zcomp_strm *zstrm)
37e7e1ef43SSergey Senozhatsky {
38ebaf9ab5SSergey Senozhatsky if (!IS_ERR_OR_NULL(zstrm->tfm))
39ebaf9ab5SSergey Senozhatsky crypto_free_comp(zstrm->tfm);
40e7e1ef43SSergey Senozhatsky free_pages((unsigned long)zstrm->buffer, 1);
41ed19f192SSebastian Andrzej Siewior zstrm->tfm = NULL;
42ed19f192SSebastian Andrzej Siewior zstrm->buffer = NULL;
43e7e1ef43SSergey Senozhatsky }
44e7e1ef43SSergey Senozhatsky
45e7e1ef43SSergey Senozhatsky /*
46ed19f192SSebastian Andrzej Siewior * Initialize zcomp_strm structure with ->tfm initialized by backend, and
47ed19f192SSebastian Andrzej Siewior * ->buffer. Return a negative value on error.
48e7e1ef43SSergey Senozhatsky */
zcomp_strm_init(struct zcomp_strm * zstrm,struct zcomp * comp)49ed19f192SSebastian Andrzej Siewior static int zcomp_strm_init(struct zcomp_strm *zstrm, struct zcomp *comp)
50e7e1ef43SSergey Senozhatsky {
51ebaf9ab5SSergey Senozhatsky zstrm->tfm = crypto_alloc_comp(comp->name, 0, 0);
52e7e1ef43SSergey Senozhatsky /*
53e7e1ef43SSergey Senozhatsky * allocate 2 pages. 1 for compressed data, plus 1 extra for the
54e7e1ef43SSergey Senozhatsky * case when compressed size is larger than the original one
55e7e1ef43SSergey Senozhatsky */
5616d37725SSergey Senozhatsky zstrm->buffer = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
57ebaf9ab5SSergey Senozhatsky if (IS_ERR_OR_NULL(zstrm->tfm) || !zstrm->buffer) {
58ebaf9ab5SSergey Senozhatsky zcomp_strm_free(zstrm);
59ed19f192SSebastian Andrzej Siewior return -ENOMEM;
60e7e1ef43SSergey Senozhatsky }
61ed19f192SSebastian Andrzej Siewior return 0;
62e7e1ef43SSergey Senozhatsky }
63e7e1ef43SSergey Senozhatsky
zcomp_available_algorithm(const char * comp)64415403beSSergey Senozhatsky bool zcomp_available_algorithm(const char *comp)
65e46b8a03SSergey Senozhatsky {
66415403beSSergey Senozhatsky /*
67415403beSSergey Senozhatsky * Crypto does not ignore a trailing new line symbol,
68415403beSSergey Senozhatsky * so make sure you don't supply a string containing
69415403beSSergey Senozhatsky * one.
70415403beSSergey Senozhatsky * This also means that we permit zcomp initialisation
71415403beSSergey Senozhatsky * with any compressing algorithm known to crypto api.
72415403beSSergey Senozhatsky */
73415403beSSergey Senozhatsky return crypto_has_comp(comp, 0, 0) == 1;
74e46b8a03SSergey Senozhatsky }
75e46b8a03SSergey Senozhatsky
76415403beSSergey Senozhatsky /* show available compressors */
zcomp_available_show(const char * comp,char * buf)77415403beSSergey Senozhatsky ssize_t zcomp_available_show(const char *comp, char *buf)
78d93435c3SSergey Senozhatsky {
79415403beSSergey Senozhatsky bool known_algorithm = false;
80415403beSSergey Senozhatsky ssize_t sz = 0;
81276aa42eSAndy Shevchenko int i;
82415403beSSergey Senozhatsky
83276aa42eSAndy Shevchenko for (i = 0; i < ARRAY_SIZE(backends); i++) {
84415403beSSergey Senozhatsky if (!strcmp(comp, backends[i])) {
85415403beSSergey Senozhatsky known_algorithm = true;
86415403beSSergey Senozhatsky sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2,
87415403beSSergey Senozhatsky "[%s] ", backends[i]);
88415403beSSergey Senozhatsky } else {
89415403beSSergey Senozhatsky sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2,
90415403beSSergey Senozhatsky "%s ", backends[i]);
91415403beSSergey Senozhatsky }
92415403beSSergey Senozhatsky }
93415403beSSergey Senozhatsky
94415403beSSergey Senozhatsky /*
95415403beSSergey Senozhatsky * Out-of-tree module known to crypto api or a missing
96415403beSSergey Senozhatsky * entry in `backends'.
97415403beSSergey Senozhatsky */
98415403beSSergey Senozhatsky if (!known_algorithm && crypto_has_comp(comp, 0, 0) == 1)
99415403beSSergey Senozhatsky sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2,
100415403beSSergey Senozhatsky "[%s] ", comp);
101415403beSSergey Senozhatsky
102415403beSSergey Senozhatsky sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n");
103415403beSSergey Senozhatsky return sz;
104d93435c3SSergey Senozhatsky }
105d93435c3SSergey Senozhatsky
zcomp_stream_get(struct zcomp * comp)1062aea8493SSergey Senozhatsky struct zcomp_strm *zcomp_stream_get(struct zcomp *comp)
107e7e1ef43SSergey Senozhatsky {
10819f545b6SMike Galbraith local_lock(&comp->stream->lock);
10919f545b6SMike Galbraith return this_cpu_ptr(comp->stream);
110e7e1ef43SSergey Senozhatsky }
111e7e1ef43SSergey Senozhatsky
zcomp_stream_put(struct zcomp * comp)1122aea8493SSergey Senozhatsky void zcomp_stream_put(struct zcomp *comp)
113e7e1ef43SSergey Senozhatsky {
11419f545b6SMike Galbraith local_unlock(&comp->stream->lock);
115e7e1ef43SSergey Senozhatsky }
116e7e1ef43SSergey Senozhatsky
zcomp_compress(struct zcomp_strm * zstrm,const void * src,unsigned int * dst_len)117ebaf9ab5SSergey Senozhatsky int zcomp_compress(struct zcomp_strm *zstrm,
118ebaf9ab5SSergey Senozhatsky const void *src, unsigned int *dst_len)
119e7e1ef43SSergey Senozhatsky {
120ebaf9ab5SSergey Senozhatsky /*
121ebaf9ab5SSergey Senozhatsky * Our dst memory (zstrm->buffer) is always `2 * PAGE_SIZE' sized
122ebaf9ab5SSergey Senozhatsky * because sometimes we can endup having a bigger compressed data
123ebaf9ab5SSergey Senozhatsky * due to various reasons: for example compression algorithms tend
124ebaf9ab5SSergey Senozhatsky * to add some padding to the compressed buffer. Speaking of padding,
125ebaf9ab5SSergey Senozhatsky * comp algorithm `842' pads the compressed length to multiple of 8
126ebaf9ab5SSergey Senozhatsky * and returns -ENOSP when the dst memory is not big enough, which
127ebaf9ab5SSergey Senozhatsky * is not something that ZRAM wants to see. We can handle the
128ebaf9ab5SSergey Senozhatsky * `compressed_size > PAGE_SIZE' case easily in ZRAM, but when we
129ebaf9ab5SSergey Senozhatsky * receive -ERRNO from the compressing backend we can't help it
130ebaf9ab5SSergey Senozhatsky * anymore. To make `842' happy we need to tell the exact size of
131ebaf9ab5SSergey Senozhatsky * the dst buffer, zram_drv will take care of the fact that
132ebaf9ab5SSergey Senozhatsky * compressed buffer is too big.
133ebaf9ab5SSergey Senozhatsky */
134ebaf9ab5SSergey Senozhatsky *dst_len = PAGE_SIZE * 2;
135ebaf9ab5SSergey Senozhatsky
136ebaf9ab5SSergey Senozhatsky return crypto_comp_compress(zstrm->tfm,
137ebaf9ab5SSergey Senozhatsky src, PAGE_SIZE,
138ebaf9ab5SSergey Senozhatsky zstrm->buffer, dst_len);
139e7e1ef43SSergey Senozhatsky }
140e7e1ef43SSergey Senozhatsky
zcomp_decompress(struct zcomp_strm * zstrm,const void * src,unsigned int src_len,void * dst)141ebaf9ab5SSergey Senozhatsky int zcomp_decompress(struct zcomp_strm *zstrm,
142ebaf9ab5SSergey Senozhatsky const void *src, unsigned int src_len, void *dst)
143e7e1ef43SSergey Senozhatsky {
144ebaf9ab5SSergey Senozhatsky unsigned int dst_len = PAGE_SIZE;
145ebaf9ab5SSergey Senozhatsky
146ebaf9ab5SSergey Senozhatsky return crypto_comp_decompress(zstrm->tfm,
147ebaf9ab5SSergey Senozhatsky src, src_len,
148ebaf9ab5SSergey Senozhatsky dst, &dst_len);
149e7e1ef43SSergey Senozhatsky }
150e7e1ef43SSergey Senozhatsky
zcomp_cpu_up_prepare(unsigned int cpu,struct hlist_node * node)1511dd6c834SAnna-Maria Gleixner int zcomp_cpu_up_prepare(unsigned int cpu, struct hlist_node *node)
152da9556a2SSergey Senozhatsky {
1531dd6c834SAnna-Maria Gleixner struct zcomp *comp = hlist_entry(node, struct zcomp, node);
154da9556a2SSergey Senozhatsky struct zcomp_strm *zstrm;
155ed19f192SSebastian Andrzej Siewior int ret;
156da9556a2SSergey Senozhatsky
157ed19f192SSebastian Andrzej Siewior zstrm = per_cpu_ptr(comp->stream, cpu);
15819f545b6SMike Galbraith local_lock_init(&zstrm->lock);
15919f545b6SMike Galbraith
160ed19f192SSebastian Andrzej Siewior ret = zcomp_strm_init(zstrm, comp);
161ed19f192SSebastian Andrzej Siewior if (ret)
162da9556a2SSergey Senozhatsky pr_err("Can't allocate a compression stream\n");
163ed19f192SSebastian Andrzej Siewior return ret;
1641dd6c834SAnna-Maria Gleixner }
1651dd6c834SAnna-Maria Gleixner
zcomp_cpu_dead(unsigned int cpu,struct hlist_node * node)1661dd6c834SAnna-Maria Gleixner int zcomp_cpu_dead(unsigned int cpu, struct hlist_node *node)
1671dd6c834SAnna-Maria Gleixner {
1681dd6c834SAnna-Maria Gleixner struct zcomp *comp = hlist_entry(node, struct zcomp, node);
1691dd6c834SAnna-Maria Gleixner struct zcomp_strm *zstrm;
1701dd6c834SAnna-Maria Gleixner
171ed19f192SSebastian Andrzej Siewior zstrm = per_cpu_ptr(comp->stream, cpu);
172ebaf9ab5SSergey Senozhatsky zcomp_strm_free(zstrm);
1731dd6c834SAnna-Maria Gleixner return 0;
174da9556a2SSergey Senozhatsky }
175da9556a2SSergey Senozhatsky
zcomp_init(struct zcomp * comp)176da9556a2SSergey Senozhatsky static int zcomp_init(struct zcomp *comp)
177da9556a2SSergey Senozhatsky {
178da9556a2SSergey Senozhatsky int ret;
179da9556a2SSergey Senozhatsky
180ed19f192SSebastian Andrzej Siewior comp->stream = alloc_percpu(struct zcomp_strm);
181da9556a2SSergey Senozhatsky if (!comp->stream)
182da9556a2SSergey Senozhatsky return -ENOMEM;
183da9556a2SSergey Senozhatsky
1841dd6c834SAnna-Maria Gleixner ret = cpuhp_state_add_instance(CPUHP_ZCOMP_PREPARE, &comp->node);
1851dd6c834SAnna-Maria Gleixner if (ret < 0)
186da9556a2SSergey Senozhatsky goto cleanup;
187da9556a2SSergey Senozhatsky return 0;
188da9556a2SSergey Senozhatsky
189da9556a2SSergey Senozhatsky cleanup:
1901dd6c834SAnna-Maria Gleixner free_percpu(comp->stream);
1911dd6c834SAnna-Maria Gleixner return ret;
192da9556a2SSergey Senozhatsky }
193da9556a2SSergey Senozhatsky
zcomp_destroy(struct zcomp * comp)194e7e1ef43SSergey Senozhatsky void zcomp_destroy(struct zcomp *comp)
195e7e1ef43SSergey Senozhatsky {
1961dd6c834SAnna-Maria Gleixner cpuhp_state_remove_instance(CPUHP_ZCOMP_PREPARE, &comp->node);
197da9556a2SSergey Senozhatsky free_percpu(comp->stream);
198e7e1ef43SSergey Senozhatsky kfree(comp);
199e7e1ef43SSergey Senozhatsky }
200e7e1ef43SSergey Senozhatsky
201e7e1ef43SSergey Senozhatsky /*
202e7e1ef43SSergey Senozhatsky * search available compressors for requested algorithm.
203fcfa8d95SSergey Senozhatsky * allocate new zcomp and initialize it. return compressing
204fcfa8d95SSergey Senozhatsky * backend pointer or ERR_PTR if things went bad. ERR_PTR(-EINVAL)
205fcfa8d95SSergey Senozhatsky * if requested algorithm is not supported, ERR_PTR(-ENOMEM) in
2063aaf14daSLuis Henriques * case of allocation error, or any other error potentially
207da9556a2SSergey Senozhatsky * returned by zcomp_init().
208e7e1ef43SSergey Senozhatsky */
zcomp_create(const char * alg)209*7ac07a26SSergey Senozhatsky struct zcomp *zcomp_create(const char *alg)
210e7e1ef43SSergey Senozhatsky {
211e7e1ef43SSergey Senozhatsky struct zcomp *comp;
2123aaf14daSLuis Henriques int error;
213e7e1ef43SSergey Senozhatsky
214dc899972SSergey Senozhatsky /*
215dc899972SSergey Senozhatsky * Crypto API will execute /sbin/modprobe if the compression module
216dc899972SSergey Senozhatsky * is not loaded yet. We must do it here, otherwise we are about to
217dc899972SSergey Senozhatsky * call /sbin/modprobe under CPU hot-plug lock.
218dc899972SSergey Senozhatsky */
219*7ac07a26SSergey Senozhatsky if (!zcomp_available_algorithm(alg))
220fcfa8d95SSergey Senozhatsky return ERR_PTR(-EINVAL);
221e7e1ef43SSergey Senozhatsky
222e7e1ef43SSergey Senozhatsky comp = kzalloc(sizeof(struct zcomp), GFP_KERNEL);
223e7e1ef43SSergey Senozhatsky if (!comp)
224fcfa8d95SSergey Senozhatsky return ERR_PTR(-ENOMEM);
225e7e1ef43SSergey Senozhatsky
226*7ac07a26SSergey Senozhatsky comp->name = alg;
227da9556a2SSergey Senozhatsky error = zcomp_init(comp);
2283aaf14daSLuis Henriques if (error) {
229e7e1ef43SSergey Senozhatsky kfree(comp);
2303aaf14daSLuis Henriques return ERR_PTR(error);
231e7e1ef43SSergey Senozhatsky }
232e7e1ef43SSergey Senozhatsky return comp;
233e7e1ef43SSergey Senozhatsky }
234