1// SPDX-License-Identifier: GPL-2.0-or-later
2#include <linux/scatterlist.h>
3#include <crypto/acompress.h>
4#include "compress.h"
5
6static int __z_erofs_crypto_decompress(struct z_erofs_decompress_req *rq,
7 struct crypto_acomp *tfm)
8{
9 struct sg_table st_src, st_dst;
10 struct acomp_req *req;
11 struct crypto_wait wait;
12 const char *reason;
13 u8 *headpage;
14 int ret;
15
16 headpage = kmap_local_page(page: *rq->in);
17 reason = z_erofs_fixup_insize(rq, padbuf: headpage + rq->pageofs_in,
18 min_t(unsigned int, rq->inputsize,
19 rq->sb->s_blocksize - rq->pageofs_in));
20 kunmap_local(headpage);
21 if (reason)
22 return IS_ERR(ptr: reason) ? PTR_ERR(ptr: reason) : -EFSCORRUPTED;
23
24 req = acomp_request_alloc(tfm);
25 if (!req)
26 return -ENOMEM;
27
28 ret = sg_alloc_table_from_pages_segment(sgt: &st_src, pages: rq->in, n_pages: rq->inpages,
29 offset: rq->pageofs_in, size: rq->inputsize, UINT_MAX, GFP_KERNEL);
30 if (ret < 0)
31 goto failed_src_alloc;
32
33 ret = sg_alloc_table_from_pages_segment(sgt: &st_dst, pages: rq->out, n_pages: rq->outpages,
34 offset: rq->pageofs_out, size: rq->outputsize, UINT_MAX, GFP_KERNEL);
35 if (ret < 0)
36 goto failed_dst_alloc;
37
38 acomp_request_set_params(req, src: st_src.sgl,
39 dst: st_dst.sgl, slen: rq->inputsize, dlen: rq->outputsize);
40
41 crypto_init_wait(wait: &wait);
42 acomp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
43 cmpl: crypto_req_done, data: &wait);
44
45 ret = crypto_wait_req(err: crypto_acomp_decompress(req), wait: &wait);
46 if (ret) {
47 erofs_err(rq->sb, "failed to decompress %d in[%u, %u] out[%u]",
48 ret, rq->inputsize, rq->pageofs_in, rq->outputsize);
49 ret = -EIO;
50 }
51
52 sg_free_table(&st_dst);
53failed_dst_alloc:
54 sg_free_table(&st_src);
55failed_src_alloc:
56 acomp_request_free(req);
57 return ret;
58}
59
60struct z_erofs_crypto_engine {
61 char *crypto_name;
62 struct crypto_acomp *tfm;
63};
64
65struct z_erofs_crypto_engine *z_erofs_crypto[Z_EROFS_COMPRESSION_MAX] = {
66 [Z_EROFS_COMPRESSION_LZ4] = (struct z_erofs_crypto_engine[]) {
67 {},
68 },
69 [Z_EROFS_COMPRESSION_LZMA] = (struct z_erofs_crypto_engine[]) {
70 {},
71 },
72 [Z_EROFS_COMPRESSION_DEFLATE] = (struct z_erofs_crypto_engine[]) {
73 { .crypto_name = "qat_deflate", },
74 {},
75 },
76 [Z_EROFS_COMPRESSION_ZSTD] = (struct z_erofs_crypto_engine[]) {
77 {},
78 },
79};
80static DECLARE_RWSEM(z_erofs_crypto_rwsem);
81
82static struct crypto_acomp *z_erofs_crypto_get_engine(int alg)
83{
84 struct z_erofs_crypto_engine *e;
85
86 for (e = z_erofs_crypto[alg]; e->crypto_name; ++e)
87 if (e->tfm)
88 return e->tfm;
89 return NULL;
90}
91
92int z_erofs_crypto_decompress(struct z_erofs_decompress_req *rq,
93 struct page **pgpl)
94{
95 struct crypto_acomp *tfm;
96 int i, err;
97
98 down_read(sem: &z_erofs_crypto_rwsem);
99 tfm = z_erofs_crypto_get_engine(alg: rq->alg);
100 if (!tfm) {
101 err = -EOPNOTSUPP;
102 goto out;
103 }
104
105 for (i = 0; i < rq->outpages; i++) {
106 struct page *const page = rq->out[i];
107 struct page *victim;
108
109 if (!page) {
110 victim = __erofs_allocpage(pagepool: pgpl, gfp: rq->gfp, tryrsv: true);
111 if (!victim) {
112 err = -ENOMEM;
113 goto out;
114 }
115 set_page_private(page: victim, Z_EROFS_SHORTLIVED_PAGE);
116 rq->out[i] = victim;
117 }
118 }
119 err = __z_erofs_crypto_decompress(rq, tfm);
120out:
121 up_read(sem: &z_erofs_crypto_rwsem);
122 return err;
123}
124
125int z_erofs_crypto_enable_engine(const char *name, int len)
126{
127 struct z_erofs_crypto_engine *e;
128 struct crypto_acomp *tfm;
129 int alg;
130
131 down_write(sem: &z_erofs_crypto_rwsem);
132 for (alg = 0; alg < Z_EROFS_COMPRESSION_MAX; ++alg) {
133 for (e = z_erofs_crypto[alg]; e->crypto_name; ++e) {
134 if (!strncmp(name, e->crypto_name, len)) {
135 if (e->tfm)
136 break;
137 tfm = crypto_alloc_acomp(alg_name: e->crypto_name, type: 0, mask: 0);
138 if (IS_ERR(ptr: tfm)) {
139 up_write(sem: &z_erofs_crypto_rwsem);
140 return -EOPNOTSUPP;
141 }
142 e->tfm = tfm;
143 break;
144 }
145 }
146 }
147 up_write(sem: &z_erofs_crypto_rwsem);
148 return 0;
149}
150
151void z_erofs_crypto_disable_all_engines(void)
152{
153 struct z_erofs_crypto_engine *e;
154 int alg;
155
156 down_write(sem: &z_erofs_crypto_rwsem);
157 for (alg = 0; alg < Z_EROFS_COMPRESSION_MAX; ++alg) {
158 for (e = z_erofs_crypto[alg]; e->crypto_name; ++e) {
159 if (!e->tfm)
160 continue;
161 crypto_free_acomp(tfm: e->tfm);
162 e->tfm = NULL;
163 }
164 }
165 up_write(sem: &z_erofs_crypto_rwsem);
166}
167
168int z_erofs_crypto_show_engines(char *buf, int size, char sep)
169{
170 struct z_erofs_crypto_engine *e;
171 int alg, len = 0;
172
173 for (alg = 0; alg < Z_EROFS_COMPRESSION_MAX; ++alg) {
174 for (e = z_erofs_crypto[alg]; e->crypto_name; ++e) {
175 if (!e->tfm)
176 continue;
177 len += scnprintf(buf: buf + len, size: size - len, fmt: "%s%c",
178 e->crypto_name, sep);
179 }
180 }
181 return len;
182}
183

source code of linux/fs/erofs/decompressor_crypto.c