summaryrefslogtreecommitdiff
path: root/fs/erofs/decompressor_crypto.c
blob: 97b77ab6443211f320deb7f38d3a6b696ba4b9ea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/scatterlist.h>
#include <crypto/acompress.h>
#include "compress.h"

static int __z_erofs_crypto_decompress(struct z_erofs_decompress_req *rq,
				       struct crypto_acomp *tfm)
{
	struct sg_table st_src, st_dst;
	struct acomp_req *req;
	struct crypto_wait wait;
	u8 *headpage;
	int ret;

	headpage = kmap_local_page(*rq->in);
	ret = z_erofs_fixup_insize(rq, headpage + rq->pageofs_in,
				min_t(unsigned int, rq->inputsize,
				      rq->sb->s_blocksize - rq->pageofs_in));
	kunmap_local(headpage);
	if (ret)
		return ret;

	req = acomp_request_alloc(tfm);
	if (!req)
		return -ENOMEM;

	ret = sg_alloc_table_from_pages_segment(&st_src, rq->in, rq->inpages,
			rq->pageofs_in, rq->inputsize, UINT_MAX, GFP_KERNEL);
	if (ret < 0)
		goto failed_src_alloc;

	ret = sg_alloc_table_from_pages_segment(&st_dst, rq->out, rq->outpages,
			rq->pageofs_out, rq->outputsize, UINT_MAX, GFP_KERNEL);
	if (ret < 0)
		goto failed_dst_alloc;

	acomp_request_set_params(req, st_src.sgl,
				 st_dst.sgl, rq->inputsize, rq->outputsize);

	crypto_init_wait(&wait);
	acomp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
				   crypto_req_done, &wait);

	ret = crypto_wait_req(crypto_acomp_decompress(req), &wait);
	if (ret) {
		erofs_err(rq->sb, "failed to decompress %d in[%u, %u] out[%u]",
			  ret, rq->inputsize, rq->pageofs_in, rq->outputsize);
		ret = -EIO;
	}

	sg_free_table(&st_dst);
failed_dst_alloc:
	sg_free_table(&st_src);
failed_src_alloc:
	acomp_request_free(req);
	return ret;
}

struct z_erofs_crypto_engine {
	char *crypto_name;
	struct crypto_acomp *tfm;
};

struct z_erofs_crypto_engine *z_erofs_crypto[Z_EROFS_COMPRESSION_MAX] = {
	[Z_EROFS_COMPRESSION_LZ4] = (struct z_erofs_crypto_engine[]) {
		{},
	},
	[Z_EROFS_COMPRESSION_LZMA] = (struct z_erofs_crypto_engine[]) {
		{},
	},
	[Z_EROFS_COMPRESSION_DEFLATE] = (struct z_erofs_crypto_engine[]) {
		{ .crypto_name = "qat_deflate", },
		{},
	},
	[Z_EROFS_COMPRESSION_ZSTD] = (struct z_erofs_crypto_engine[]) {
		{},
	},
};
static DECLARE_RWSEM(z_erofs_crypto_rwsem);

static struct crypto_acomp *z_erofs_crypto_get_engine(int alg)
{
	struct z_erofs_crypto_engine *e;

	for (e = z_erofs_crypto[alg]; e->crypto_name; ++e)
		if (e->tfm)
			return e->tfm;
	return NULL;
}

int z_erofs_crypto_decompress(struct z_erofs_decompress_req *rq,
			      struct page **pgpl)
{
	struct crypto_acomp *tfm;
	int i, err;

	down_read(&z_erofs_crypto_rwsem);
	tfm = z_erofs_crypto_get_engine(rq->alg);
	if (!tfm) {
		err = -EOPNOTSUPP;
		goto out;
	}

	for (i = 0; i < rq->outpages; i++) {
		struct page *const page = rq->out[i];
		struct page *victim;

		if (!page) {
			victim = __erofs_allocpage(pgpl, rq->gfp, true);
			if (!victim) {
				err = -ENOMEM;
				goto out;
			}
			set_page_private(victim, Z_EROFS_SHORTLIVED_PAGE);
			rq->out[i] = victim;
		}
	}
	err = __z_erofs_crypto_decompress(rq, tfm);
out:
	up_read(&z_erofs_crypto_rwsem);
	return err;
}

int z_erofs_crypto_enable_engine(const char *name, int len)
{
	struct z_erofs_crypto_engine *e;
	struct crypto_acomp *tfm;
	int alg;

	down_write(&z_erofs_crypto_rwsem);
	for (alg = 0; alg < Z_EROFS_COMPRESSION_MAX; ++alg) {
		for (e = z_erofs_crypto[alg]; e->crypto_name; ++e) {
			if (!strncmp(name, e->crypto_name, len)) {
				if (e->tfm)
					break;
				tfm = crypto_alloc_acomp(e->crypto_name, 0, 0);
				if (IS_ERR(tfm)) {
					up_write(&z_erofs_crypto_rwsem);
					return -EOPNOTSUPP;
				}
				e->tfm = tfm;
				break;
			}
		}
	}
	up_write(&z_erofs_crypto_rwsem);
	return 0;
}

void z_erofs_crypto_disable_all_engines(void)
{
	struct z_erofs_crypto_engine *e;
	int alg;

	down_write(&z_erofs_crypto_rwsem);
	for (alg = 0; alg < Z_EROFS_COMPRESSION_MAX; ++alg) {
		for (e = z_erofs_crypto[alg]; e->crypto_name; ++e) {
			if (!e->tfm)
				continue;
			crypto_free_acomp(e->tfm);
			e->tfm = NULL;
		}
	}
	up_write(&z_erofs_crypto_rwsem);
}

int z_erofs_crypto_show_engines(char *buf, int size, char sep)
{
	struct z_erofs_crypto_engine *e;
	int alg, len = 0;

	for (alg = 0; alg < Z_EROFS_COMPRESSION_MAX; ++alg) {
		for (e = z_erofs_crypto[alg]; e->crypto_name; ++e) {
			if (!e->tfm)
				continue;
			len += scnprintf(buf + len, size - len, "%s%c",
					 e->crypto_name, sep);
		}
	}
	return len;
}