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