// SPDX-License-Identifier: GPL-2.0-or-later #include #include #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; }