| // 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; | |
| const char *reason; | |
| u8 *headpage; | |
| int ret; | |
| headpage = kmap_local_page(*rq->in); | |
| reason = 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 (reason) | |
| return IS_ERR(reason) ? PTR_ERR(reason) : -EFSCORRUPTED; | |
| 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; | |
| }; | |
| static 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; | |
| } |