Gao Xiang | 29b24f6 | 2019-07-31 23:57:31 +0800 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-only |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 2 | /* |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 3 | * Copyright (C) 2019 HUAWEI, Inc. |
Alexander A. Klimov | 592e7cd | 2020-07-13 15:09:44 +0200 | [diff] [blame] | 4 | * https://www.huawei.com/ |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 5 | */ |
| 6 | #include "compress.h" |
Gao Xiang | 46c2d14 | 2019-07-31 23:57:44 +0800 | [diff] [blame] | 7 | #include <linux/module.h> |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 8 | #include <linux/lz4.h> |
| 9 | |
| 10 | #ifndef LZ4_DISTANCE_MAX /* history window size */ |
| 11 | #define LZ4_DISTANCE_MAX 65535 /* set to maximum value by default */ |
| 12 | #endif |
| 13 | |
Gao Xiang | af89bce | 2019-07-03 14:52:09 +0800 | [diff] [blame] | 14 | #define LZ4_MAX_DISTANCE_PAGES (DIV_ROUND_UP(LZ4_DISTANCE_MAX, PAGE_SIZE) + 1) |
Gao Xiang | 0ffd71b | 2019-06-24 15:22:56 +0800 | [diff] [blame] | 15 | #ifndef LZ4_DECOMPRESS_INPLACE_MARGIN |
| 16 | #define LZ4_DECOMPRESS_INPLACE_MARGIN(srcsize) (((srcsize) >> 8) + 32) |
| 17 | #endif |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 18 | |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 19 | struct z_erofs_lz4_decompress_ctx { |
| 20 | struct z_erofs_decompress_req *rq; |
| 21 | /* # of encoded, decoded pages */ |
| 22 | unsigned int inpages, outpages; |
| 23 | /* decoded block total length (used for in-place decompression) */ |
| 24 | unsigned int oend; |
| 25 | }; |
| 26 | |
Huang Jianan | 5d50538 | 2021-03-29 09:23:06 +0800 | [diff] [blame] | 27 | int z_erofs_load_lz4_config(struct super_block *sb, |
Gao Xiang | 46249cd | 2021-03-29 09:23:07 +0800 | [diff] [blame] | 28 | struct erofs_super_block *dsb, |
| 29 | struct z_erofs_lz4_cfgs *lz4, int size) |
Huang Jianan | 5d50538 | 2021-03-29 09:23:06 +0800 | [diff] [blame] | 30 | { |
Gao Xiang | 4fea63f | 2021-04-07 12:39:23 +0800 | [diff] [blame] | 31 | struct erofs_sb_info *sbi = EROFS_SB(sb); |
Gao Xiang | 46249cd | 2021-03-29 09:23:07 +0800 | [diff] [blame] | 32 | u16 distance; |
| 33 | |
| 34 | if (lz4) { |
| 35 | if (size < sizeof(struct z_erofs_lz4_cfgs)) { |
| 36 | erofs_err(sb, "invalid lz4 cfgs, size=%u", size); |
| 37 | return -EINVAL; |
| 38 | } |
| 39 | distance = le16_to_cpu(lz4->max_distance); |
Gao Xiang | 4fea63f | 2021-04-07 12:39:23 +0800 | [diff] [blame] | 40 | |
| 41 | sbi->lz4.max_pclusterblks = le16_to_cpu(lz4->max_pclusterblks); |
| 42 | if (!sbi->lz4.max_pclusterblks) { |
| 43 | sbi->lz4.max_pclusterblks = 1; /* reserved case */ |
| 44 | } else if (sbi->lz4.max_pclusterblks > |
| 45 | Z_EROFS_PCLUSTER_MAX_SIZE / EROFS_BLKSIZ) { |
| 46 | erofs_err(sb, "too large lz4 pclusterblks %u", |
| 47 | sbi->lz4.max_pclusterblks); |
| 48 | return -EINVAL; |
| 49 | } else if (sbi->lz4.max_pclusterblks >= 2) { |
| 50 | erofs_info(sb, "EXPERIMENTAL big pcluster feature in use. Use at your own risk!"); |
| 51 | } |
Gao Xiang | 46249cd | 2021-03-29 09:23:07 +0800 | [diff] [blame] | 52 | } else { |
Gao Xiang | 1437371 | 2021-03-29 18:00:12 +0800 | [diff] [blame] | 53 | distance = le16_to_cpu(dsb->u1.lz4_max_distance); |
Gao Xiang | 4fea63f | 2021-04-07 12:39:23 +0800 | [diff] [blame] | 54 | sbi->lz4.max_pclusterblks = 1; |
Gao Xiang | 46249cd | 2021-03-29 09:23:07 +0800 | [diff] [blame] | 55 | } |
Huang Jianan | 5d50538 | 2021-03-29 09:23:06 +0800 | [diff] [blame] | 56 | |
Gao Xiang | 4fea63f | 2021-04-07 12:39:23 +0800 | [diff] [blame] | 57 | sbi->lz4.max_distance_pages = distance ? |
Huang Jianan | 5d50538 | 2021-03-29 09:23:06 +0800 | [diff] [blame] | 58 | DIV_ROUND_UP(distance, PAGE_SIZE) + 1 : |
| 59 | LZ4_MAX_DISTANCE_PAGES; |
Gao Xiang | 4fea63f | 2021-04-07 12:39:23 +0800 | [diff] [blame] | 60 | return erofs_pcpubuf_growsize(sbi->lz4.max_pclusterblks); |
Huang Jianan | 5d50538 | 2021-03-29 09:23:06 +0800 | [diff] [blame] | 61 | } |
| 62 | |
Gao Xiang | 966edfb | 2021-10-11 05:31:44 +0800 | [diff] [blame] | 63 | /* |
| 64 | * Fill all gaps with bounce pages if it's a sparse page list. Also check if |
| 65 | * all physical pages are consecutive, which can be seen for moderate CR. |
| 66 | */ |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 67 | static int z_erofs_lz4_prepare_dstpages(struct z_erofs_lz4_decompress_ctx *ctx, |
Gao Xiang | eaa9172 | 2021-10-22 17:01:20 +0800 | [diff] [blame] | 68 | struct page **pagepool) |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 69 | { |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 70 | struct z_erofs_decompress_req *rq = ctx->rq; |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 71 | struct page *availables[LZ4_MAX_DISTANCE_PAGES] = { NULL }; |
Gao Xiang | af89bce | 2019-07-03 14:52:09 +0800 | [diff] [blame] | 72 | unsigned long bounced[DIV_ROUND_UP(LZ4_MAX_DISTANCE_PAGES, |
| 73 | BITS_PER_LONG)] = { 0 }; |
Huang Jianan | 5d50538 | 2021-03-29 09:23:06 +0800 | [diff] [blame] | 74 | unsigned int lz4_max_distance_pages = |
| 75 | EROFS_SB(rq->sb)->lz4.max_distance_pages; |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 76 | void *kaddr = NULL; |
Gao Xiang | af89bce | 2019-07-03 14:52:09 +0800 | [diff] [blame] | 77 | unsigned int i, j, top; |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 78 | |
Gao Xiang | af89bce | 2019-07-03 14:52:09 +0800 | [diff] [blame] | 79 | top = 0; |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 80 | for (i = j = 0; i < ctx->outpages; ++i, ++j) { |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 81 | struct page *const page = rq->out[i]; |
Gao Xiang | af89bce | 2019-07-03 14:52:09 +0800 | [diff] [blame] | 82 | struct page *victim; |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 83 | |
Huang Jianan | 5d50538 | 2021-03-29 09:23:06 +0800 | [diff] [blame] | 84 | if (j >= lz4_max_distance_pages) |
Gao Xiang | af89bce | 2019-07-03 14:52:09 +0800 | [diff] [blame] | 85 | j = 0; |
| 86 | |
| 87 | /* 'valid' bounced can only be tested after a complete round */ |
| 88 | if (test_bit(j, bounced)) { |
Huang Jianan | 5d50538 | 2021-03-29 09:23:06 +0800 | [diff] [blame] | 89 | DBG_BUGON(i < lz4_max_distance_pages); |
| 90 | DBG_BUGON(top >= lz4_max_distance_pages); |
| 91 | availables[top++] = rq->out[i - lz4_max_distance_pages]; |
Gao Xiang | af89bce | 2019-07-03 14:52:09 +0800 | [diff] [blame] | 92 | } |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 93 | |
| 94 | if (page) { |
Gao Xiang | af89bce | 2019-07-03 14:52:09 +0800 | [diff] [blame] | 95 | __clear_bit(j, bounced); |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 96 | if (kaddr) { |
| 97 | if (kaddr + PAGE_SIZE == page_address(page)) |
| 98 | kaddr += PAGE_SIZE; |
| 99 | else |
| 100 | kaddr = NULL; |
| 101 | } else if (!i) { |
| 102 | kaddr = page_address(page); |
| 103 | } |
| 104 | continue; |
| 105 | } |
| 106 | kaddr = NULL; |
Gao Xiang | af89bce | 2019-07-03 14:52:09 +0800 | [diff] [blame] | 107 | __set_bit(j, bounced); |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 108 | |
Gao Xiang | af89bce | 2019-07-03 14:52:09 +0800 | [diff] [blame] | 109 | if (top) { |
| 110 | victim = availables[--top]; |
| 111 | get_page(victim); |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 112 | } else { |
Huang Jianan | b4892fa | 2021-03-16 11:15:14 +0800 | [diff] [blame] | 113 | victim = erofs_allocpage(pagepool, |
| 114 | GFP_KERNEL | __GFP_NOFAIL); |
Gao Xiang | 6aaa7b0 | 2020-12-08 17:58:32 +0800 | [diff] [blame] | 115 | set_page_private(victim, Z_EROFS_SHORTLIVED_PAGE); |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 116 | } |
Gao Xiang | af89bce | 2019-07-03 14:52:09 +0800 | [diff] [blame] | 117 | rq->out[i] = victim; |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 118 | } |
| 119 | return kaddr ? 1 : 0; |
| 120 | } |
| 121 | |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 122 | static void *z_erofs_lz4_handle_overlap(struct z_erofs_lz4_decompress_ctx *ctx, |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 123 | void *inpage, unsigned int *inputmargin, int *maptype, |
Gao Xiang | ab749ba | 2021-12-28 13:46:02 +0800 | [diff] [blame] | 124 | bool may_inplace) |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 125 | { |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 126 | struct z_erofs_decompress_req *rq = ctx->rq; |
| 127 | unsigned int omargin, total, i, j; |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 128 | struct page **in; |
| 129 | void *src, *tmp; |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 130 | |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 131 | if (rq->inplace_io) { |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 132 | omargin = PAGE_ALIGN(ctx->oend) - ctx->oend; |
Gao Xiang | ab749ba | 2021-12-28 13:46:02 +0800 | [diff] [blame] | 133 | if (rq->partial_decoding || !may_inplace || |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 134 | omargin < LZ4_DECOMPRESS_INPLACE_MARGIN(rq->inputsize)) |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 135 | goto docopy; |
| 136 | |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 137 | for (i = 0; i < ctx->inpages; ++i) { |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 138 | DBG_BUGON(rq->in[i] == NULL); |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 139 | for (j = 0; j < ctx->outpages - ctx->inpages + i; ++j) |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 140 | if (rq->out[j] == rq->in[i]) |
| 141 | goto docopy; |
| 142 | } |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 143 | } |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 144 | |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 145 | if (ctx->inpages <= 1) { |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 146 | *maptype = 0; |
| 147 | return inpage; |
| 148 | } |
| 149 | kunmap_atomic(inpage); |
| 150 | might_sleep(); |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 151 | src = erofs_vm_map_ram(rq->in, ctx->inpages); |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 152 | if (!src) |
| 153 | return ERR_PTR(-ENOMEM); |
| 154 | *maptype = 1; |
| 155 | return src; |
| 156 | |
| 157 | docopy: |
| 158 | /* Or copy compressed data which can be overlapped to per-CPU buffer */ |
| 159 | in = rq->in; |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 160 | src = erofs_get_pcpubuf(ctx->inpages); |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 161 | if (!src) { |
| 162 | DBG_BUGON(1); |
| 163 | kunmap_atomic(inpage); |
| 164 | return ERR_PTR(-EFAULT); |
| 165 | } |
| 166 | |
| 167 | tmp = src; |
| 168 | total = rq->inputsize; |
| 169 | while (total) { |
| 170 | unsigned int page_copycnt = |
| 171 | min_t(unsigned int, total, PAGE_SIZE - *inputmargin); |
| 172 | |
| 173 | if (!inpage) |
| 174 | inpage = kmap_atomic(*in); |
| 175 | memcpy(tmp, inpage + *inputmargin, page_copycnt); |
| 176 | kunmap_atomic(inpage); |
| 177 | inpage = NULL; |
| 178 | tmp += page_copycnt; |
| 179 | total -= page_copycnt; |
| 180 | ++in; |
| 181 | *inputmargin = 0; |
| 182 | } |
| 183 | *maptype = 2; |
| 184 | return src; |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 185 | } |
| 186 | |
Gao Xiang | 10e5f6e | 2021-12-28 13:46:01 +0800 | [diff] [blame] | 187 | /* |
| 188 | * Get the exact inputsize with zero_padding feature. |
| 189 | * - For LZ4, it should work if zero_padding feature is on (5.3+); |
| 190 | * - For MicroLZMA, it'd be enabled all the time. |
| 191 | */ |
| 192 | int z_erofs_fixup_insize(struct z_erofs_decompress_req *rq, const char *padbuf, |
| 193 | unsigned int padbufsize) |
| 194 | { |
| 195 | const char *padend; |
| 196 | |
| 197 | padend = memchr_inv(padbuf, 0, padbufsize); |
| 198 | if (!padend) |
| 199 | return -EFSCORRUPTED; |
| 200 | rq->inputsize -= padend - padbuf; |
| 201 | rq->pageofs_in += padend - padbuf; |
| 202 | return 0; |
| 203 | } |
| 204 | |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 205 | static int z_erofs_lz4_decompress_mem(struct z_erofs_lz4_decompress_ctx *ctx, |
Gao Xiang | 966edfb | 2021-10-11 05:31:44 +0800 | [diff] [blame] | 206 | u8 *out) |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 207 | { |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 208 | struct z_erofs_decompress_req *rq = ctx->rq; |
Gao Xiang | ab749ba | 2021-12-28 13:46:02 +0800 | [diff] [blame] | 209 | bool support_0padding = false, may_inplace = false; |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 210 | unsigned int inputmargin; |
| 211 | u8 *headpage, *src; |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 212 | int ret, maptype; |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 213 | |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 214 | DBG_BUGON(*rq->in == NULL); |
| 215 | headpage = kmap_atomic(*rq->in); |
Gao Xiang | 0ffd71b | 2019-06-24 15:22:56 +0800 | [diff] [blame] | 216 | |
Gao Xiang | 10e5f6e | 2021-12-28 13:46:01 +0800 | [diff] [blame] | 217 | /* LZ4 decompression inplace is only safe if zero_padding is enabled */ |
Huang Jianan | 7e508f2 | 2021-11-13 00:09:33 +0800 | [diff] [blame] | 218 | if (erofs_sb_has_zero_padding(EROFS_SB(rq->sb))) { |
Gao Xiang | 0ffd71b | 2019-06-24 15:22:56 +0800 | [diff] [blame] | 219 | support_0padding = true; |
Gao Xiang | 10e5f6e | 2021-12-28 13:46:01 +0800 | [diff] [blame] | 220 | ret = z_erofs_fixup_insize(rq, headpage + rq->pageofs_in, |
| 221 | min_t(unsigned int, rq->inputsize, |
| 222 | EROFS_BLKSIZ - rq->pageofs_in)); |
| 223 | if (ret) { |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 224 | kunmap_atomic(headpage); |
Gao Xiang | 10e5f6e | 2021-12-28 13:46:01 +0800 | [diff] [blame] | 225 | return ret; |
Gao Xiang | 0ffd71b | 2019-06-24 15:22:56 +0800 | [diff] [blame] | 226 | } |
Gao Xiang | ab749ba | 2021-12-28 13:46:02 +0800 | [diff] [blame] | 227 | may_inplace = !((rq->pageofs_in + rq->inputsize) & |
| 228 | (EROFS_BLKSIZ - 1)); |
Gao Xiang | 0ffd71b | 2019-06-24 15:22:56 +0800 | [diff] [blame] | 229 | } |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 230 | |
Gao Xiang | 10e5f6e | 2021-12-28 13:46:01 +0800 | [diff] [blame] | 231 | inputmargin = rq->pageofs_in; |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 232 | src = z_erofs_lz4_handle_overlap(ctx, headpage, &inputmargin, |
Gao Xiang | ab749ba | 2021-12-28 13:46:02 +0800 | [diff] [blame] | 233 | &maptype, may_inplace); |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 234 | if (IS_ERR(src)) |
| 235 | return PTR_ERR(src); |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 236 | |
Gao Xiang | af1038a | 2020-02-26 16:10:07 +0800 | [diff] [blame] | 237 | /* legacy format could compress extra data in a pcluster. */ |
| 238 | if (rq->partial_decoding || !support_0padding) |
| 239 | ret = LZ4_decompress_safe_partial(src + inputmargin, out, |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 240 | rq->inputsize, rq->outputsize, rq->outputsize); |
Gao Xiang | af1038a | 2020-02-26 16:10:07 +0800 | [diff] [blame] | 241 | else |
| 242 | ret = LZ4_decompress_safe(src + inputmargin, out, |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 243 | rq->inputsize, rq->outputsize); |
Gao Xiang | af1038a | 2020-02-26 16:10:07 +0800 | [diff] [blame] | 244 | |
Gao Xiang | aa99a76 | 2020-02-26 16:10:08 +0800 | [diff] [blame] | 245 | if (ret != rq->outputsize) { |
| 246 | erofs_err(rq->sb, "failed to decompress %d in[%u, %u] out[%u]", |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 247 | ret, rq->inputsize, inputmargin, rq->outputsize); |
Gao Xiang | aa99a76 | 2020-02-26 16:10:08 +0800 | [diff] [blame] | 248 | |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 249 | print_hex_dump(KERN_DEBUG, "[ in]: ", DUMP_PREFIX_OFFSET, |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 250 | 16, 1, src + inputmargin, rq->inputsize, true); |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 251 | print_hex_dump(KERN_DEBUG, "[out]: ", DUMP_PREFIX_OFFSET, |
| 252 | 16, 1, out, rq->outputsize, true); |
Gao Xiang | aa99a76 | 2020-02-26 16:10:08 +0800 | [diff] [blame] | 253 | |
| 254 | if (ret >= 0) |
| 255 | memset(out + ret, 0, rq->outputsize - ret); |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 256 | ret = -EIO; |
Yue Hu | 5b6e7e1 | 2021-10-14 14:57:44 +0800 | [diff] [blame] | 257 | } else { |
| 258 | ret = 0; |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 259 | } |
| 260 | |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 261 | if (maptype == 0) { |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 262 | kunmap_atomic(headpage); |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 263 | } else if (maptype == 1) { |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 264 | vm_unmap_ram(src, ctx->inpages); |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 265 | } else if (maptype == 2) { |
| 266 | erofs_put_pcpubuf(src); |
| 267 | } else { |
| 268 | DBG_BUGON(1); |
| 269 | return -EFAULT; |
| 270 | } |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 271 | return ret; |
| 272 | } |
| 273 | |
Gao Xiang | 966edfb | 2021-10-11 05:31:44 +0800 | [diff] [blame] | 274 | static int z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq, |
Gao Xiang | eaa9172 | 2021-10-22 17:01:20 +0800 | [diff] [blame] | 275 | struct page **pagepool) |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 276 | { |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 277 | struct z_erofs_lz4_decompress_ctx ctx; |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 278 | unsigned int dst_maptype; |
| 279 | void *dst; |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 280 | int ret; |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 281 | |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 282 | ctx.rq = rq; |
| 283 | ctx.oend = rq->pageofs_out + rq->outputsize; |
| 284 | ctx.outpages = PAGE_ALIGN(ctx.oend) >> PAGE_SHIFT; |
| 285 | ctx.inpages = PAGE_ALIGN(rq->inputsize) >> PAGE_SHIFT; |
| 286 | |
Yue Hu | 5b6e7e1 | 2021-10-14 14:57:44 +0800 | [diff] [blame] | 287 | /* one optimized fast path only for non bigpcluster cases yet */ |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 288 | if (ctx.inpages == 1 && ctx.outpages == 1 && !rq->inplace_io) { |
Yue Hu | 5b6e7e1 | 2021-10-14 14:57:44 +0800 | [diff] [blame] | 289 | DBG_BUGON(!*rq->out); |
| 290 | dst = kmap_atomic(*rq->out); |
| 291 | dst_maptype = 0; |
| 292 | goto dstmap_out; |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 293 | } |
| 294 | |
Gao Xiang | 598162d | 2021-04-07 12:39:26 +0800 | [diff] [blame] | 295 | /* general decoding path which can be used for all cases */ |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 296 | ret = z_erofs_lz4_prepare_dstpages(&ctx, pagepool); |
| 297 | if (ret < 0) { |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 298 | return ret; |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 299 | } else if (ret > 0) { |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 300 | dst = page_address(*rq->out); |
| 301 | dst_maptype = 1; |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 302 | } else { |
| 303 | dst = erofs_vm_map_ram(rq->out, ctx.outpages); |
| 304 | if (!dst) |
| 305 | return -ENOMEM; |
| 306 | dst_maptype = 2; |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 307 | } |
| 308 | |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 309 | dstmap_out: |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 310 | ret = z_erofs_lz4_decompress_mem(&ctx, dst + rq->pageofs_out); |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 311 | if (!dst_maptype) |
| 312 | kunmap_atomic(dst); |
| 313 | else if (dst_maptype == 2) |
Gao Xiang | d67aee7 | 2021-12-28 13:46:00 +0800 | [diff] [blame] | 314 | vm_unmap_ram(dst, ctx.outpages); |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 315 | return ret; |
| 316 | } |
| 317 | |
Gao Xiang | 966edfb | 2021-10-11 05:31:44 +0800 | [diff] [blame] | 318 | static int z_erofs_shifted_transform(struct z_erofs_decompress_req *rq, |
Gao Xiang | eaa9172 | 2021-10-22 17:01:20 +0800 | [diff] [blame] | 319 | struct page **pagepool) |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 320 | { |
| 321 | const unsigned int nrpages_out = |
| 322 | PAGE_ALIGN(rq->pageofs_out + rq->outputsize) >> PAGE_SHIFT; |
Gao Xiang | ab749ba | 2021-12-28 13:46:02 +0800 | [diff] [blame] | 323 | const unsigned int righthalf = min_t(unsigned int, rq->outputsize, |
| 324 | PAGE_SIZE - rq->pageofs_out); |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 325 | unsigned char *src, *dst; |
| 326 | |
| 327 | if (nrpages_out > 2) { |
| 328 | DBG_BUGON(1); |
| 329 | return -EIO; |
| 330 | } |
| 331 | |
| 332 | if (rq->out[0] == *rq->in) { |
| 333 | DBG_BUGON(nrpages_out != 1); |
| 334 | return 0; |
| 335 | } |
| 336 | |
Gao Xiang | ab749ba | 2021-12-28 13:46:02 +0800 | [diff] [blame] | 337 | src = kmap_atomic(*rq->in) + rq->pageofs_in; |
Gao Xiang | 4d20243 | 2020-01-07 10:25:46 +0800 | [diff] [blame] | 338 | if (rq->out[0]) { |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 339 | dst = kmap_atomic(rq->out[0]); |
| 340 | memcpy(dst + rq->pageofs_out, src, righthalf); |
Gao Xiang | 4d20243 | 2020-01-07 10:25:46 +0800 | [diff] [blame] | 341 | kunmap_atomic(dst); |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 342 | } |
| 343 | |
Gao Xiang | 4d20243 | 2020-01-07 10:25:46 +0800 | [diff] [blame] | 344 | if (nrpages_out == 2) { |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 345 | DBG_BUGON(!rq->out[1]); |
Gao Xiang | 4d20243 | 2020-01-07 10:25:46 +0800 | [diff] [blame] | 346 | if (rq->out[1] == *rq->in) { |
| 347 | memmove(src, src + righthalf, rq->pageofs_out); |
| 348 | } else { |
| 349 | dst = kmap_atomic(rq->out[1]); |
| 350 | memcpy(dst, src + righthalf, rq->pageofs_out); |
| 351 | kunmap_atomic(dst); |
| 352 | } |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 353 | } |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 354 | kunmap_atomic(src); |
| 355 | return 0; |
| 356 | } |
| 357 | |
Gao Xiang | 966edfb | 2021-10-11 05:31:44 +0800 | [diff] [blame] | 358 | static struct z_erofs_decompressor decompressors[] = { |
| 359 | [Z_EROFS_COMPRESSION_SHIFTED] = { |
| 360 | .decompress = z_erofs_shifted_transform, |
| 361 | .name = "shifted" |
| 362 | }, |
| 363 | [Z_EROFS_COMPRESSION_LZ4] = { |
| 364 | .decompress = z_erofs_lz4_decompress, |
| 365 | .name = "lz4" |
| 366 | }, |
Gao Xiang | 622cead | 2021-10-11 05:31:45 +0800 | [diff] [blame] | 367 | #ifdef CONFIG_EROFS_FS_ZIP_LZMA |
| 368 | [Z_EROFS_COMPRESSION_LZMA] = { |
| 369 | .decompress = z_erofs_lzma_decompress, |
| 370 | .name = "lzma" |
| 371 | }, |
| 372 | #endif |
Gao Xiang | 966edfb | 2021-10-11 05:31:44 +0800 | [diff] [blame] | 373 | }; |
| 374 | |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 375 | int z_erofs_decompress(struct z_erofs_decompress_req *rq, |
Gao Xiang | eaa9172 | 2021-10-22 17:01:20 +0800 | [diff] [blame] | 376 | struct page **pagepool) |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 377 | { |
Gao Xiang | 966edfb | 2021-10-11 05:31:44 +0800 | [diff] [blame] | 378 | return decompressors[rq->alg].decompress(rq, pagepool); |
Gao Xiang | 7fc45db | 2019-06-24 15:22:55 +0800 | [diff] [blame] | 379 | } |