erofs: remove the fast path of per-CPU buffer decompression
[linux-2.6-block.git] / fs / erofs / decompressor.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2019 HUAWEI, Inc.
4  *             https://www.huawei.com/
5  */
6 #include "compress.h"
7 #include <linux/module.h>
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
14 #define LZ4_MAX_DISTANCE_PAGES  (DIV_ROUND_UP(LZ4_DISTANCE_MAX, PAGE_SIZE) + 1)
15 #ifndef LZ4_DECOMPRESS_INPLACE_MARGIN
16 #define LZ4_DECOMPRESS_INPLACE_MARGIN(srcsize)  (((srcsize) >> 8) + 32)
17 #endif
18
19 struct z_erofs_decompressor {
20         /*
21          * if destpages have sparsed pages, fill them with bounce pages.
22          * it also check whether destpages indicate continuous physical memory.
23          */
24         int (*prepare_destpages)(struct z_erofs_decompress_req *rq,
25                                  struct list_head *pagepool);
26         int (*decompress)(struct z_erofs_decompress_req *rq, u8 *out);
27         char *name;
28 };
29
30 int z_erofs_load_lz4_config(struct super_block *sb,
31                             struct erofs_super_block *dsb,
32                             struct z_erofs_lz4_cfgs *lz4, int size)
33 {
34         struct erofs_sb_info *sbi = EROFS_SB(sb);
35         u16 distance;
36
37         if (lz4) {
38                 if (size < sizeof(struct z_erofs_lz4_cfgs)) {
39                         erofs_err(sb, "invalid lz4 cfgs, size=%u", size);
40                         return -EINVAL;
41                 }
42                 distance = le16_to_cpu(lz4->max_distance);
43
44                 sbi->lz4.max_pclusterblks = le16_to_cpu(lz4->max_pclusterblks);
45                 if (!sbi->lz4.max_pclusterblks) {
46                         sbi->lz4.max_pclusterblks = 1;  /* reserved case */
47                 } else if (sbi->lz4.max_pclusterblks >
48                            Z_EROFS_PCLUSTER_MAX_SIZE / EROFS_BLKSIZ) {
49                         erofs_err(sb, "too large lz4 pclusterblks %u",
50                                   sbi->lz4.max_pclusterblks);
51                         return -EINVAL;
52                 } else if (sbi->lz4.max_pclusterblks >= 2) {
53                         erofs_info(sb, "EXPERIMENTAL big pcluster feature in use. Use at your own risk!");
54                 }
55         } else {
56                 distance = le16_to_cpu(dsb->u1.lz4_max_distance);
57                 sbi->lz4.max_pclusterblks = 1;
58         }
59
60         sbi->lz4.max_distance_pages = distance ?
61                                         DIV_ROUND_UP(distance, PAGE_SIZE) + 1 :
62                                         LZ4_MAX_DISTANCE_PAGES;
63         return erofs_pcpubuf_growsize(sbi->lz4.max_pclusterblks);
64 }
65
66 static int z_erofs_lz4_prepare_destpages(struct z_erofs_decompress_req *rq,
67                                          struct list_head *pagepool)
68 {
69         const unsigned int nr =
70                 PAGE_ALIGN(rq->pageofs_out + rq->outputsize) >> PAGE_SHIFT;
71         struct page *availables[LZ4_MAX_DISTANCE_PAGES] = { NULL };
72         unsigned long bounced[DIV_ROUND_UP(LZ4_MAX_DISTANCE_PAGES,
73                                            BITS_PER_LONG)] = { 0 };
74         unsigned int lz4_max_distance_pages =
75                                 EROFS_SB(rq->sb)->lz4.max_distance_pages;
76         void *kaddr = NULL;
77         unsigned int i, j, top;
78
79         top = 0;
80         for (i = j = 0; i < nr; ++i, ++j) {
81                 struct page *const page = rq->out[i];
82                 struct page *victim;
83
84                 if (j >= lz4_max_distance_pages)
85                         j = 0;
86
87                 /* 'valid' bounced can only be tested after a complete round */
88                 if (test_bit(j, bounced)) {
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];
92                 }
93
94                 if (page) {
95                         __clear_bit(j, bounced);
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;
107                 __set_bit(j, bounced);
108
109                 if (top) {
110                         victim = availables[--top];
111                         get_page(victim);
112                 } else {
113                         victim = erofs_allocpage(pagepool,
114                                                  GFP_KERNEL | __GFP_NOFAIL);
115                         set_page_private(victim, Z_EROFS_SHORTLIVED_PAGE);
116                 }
117                 rq->out[i] = victim;
118         }
119         return kaddr ? 1 : 0;
120 }
121
122 static void *z_erofs_handle_inplace_io(struct z_erofs_decompress_req *rq,
123                         void *inpage, unsigned int *inputmargin, int *maptype,
124                         bool support_0padding)
125 {
126         unsigned int nrpages_in, nrpages_out;
127         unsigned int ofull, oend, inputsize, total, i, j;
128         struct page **in;
129         void *src, *tmp;
130
131         inputsize = rq->inputsize;
132         nrpages_in = PAGE_ALIGN(inputsize) >> PAGE_SHIFT;
133         oend = rq->pageofs_out + rq->outputsize;
134         ofull = PAGE_ALIGN(oend);
135         nrpages_out = ofull >> PAGE_SHIFT;
136
137         if (rq->inplace_io) {
138                 if (rq->partial_decoding || !support_0padding ||
139                     ofull - oend < LZ4_DECOMPRESS_INPLACE_MARGIN(inputsize))
140                         goto docopy;
141
142                 for (i = 0; i < nrpages_in; ++i) {
143                         DBG_BUGON(rq->in[i] == NULL);
144                         for (j = 0; j < nrpages_out - nrpages_in + i; ++j)
145                                 if (rq->out[j] == rq->in[i])
146                                         goto docopy;
147                 }
148         }
149
150         if (nrpages_in <= 1) {
151                 *maptype = 0;
152                 return inpage;
153         }
154         kunmap_atomic(inpage);
155         might_sleep();
156         src = erofs_vm_map_ram(rq->in, nrpages_in);
157         if (!src)
158                 return ERR_PTR(-ENOMEM);
159         *maptype = 1;
160         return src;
161
162 docopy:
163         /* Or copy compressed data which can be overlapped to per-CPU buffer */
164         in = rq->in;
165         src = erofs_get_pcpubuf(nrpages_in);
166         if (!src) {
167                 DBG_BUGON(1);
168                 kunmap_atomic(inpage);
169                 return ERR_PTR(-EFAULT);
170         }
171
172         tmp = src;
173         total = rq->inputsize;
174         while (total) {
175                 unsigned int page_copycnt =
176                         min_t(unsigned int, total, PAGE_SIZE - *inputmargin);
177
178                 if (!inpage)
179                         inpage = kmap_atomic(*in);
180                 memcpy(tmp, inpage + *inputmargin, page_copycnt);
181                 kunmap_atomic(inpage);
182                 inpage = NULL;
183                 tmp += page_copycnt;
184                 total -= page_copycnt;
185                 ++in;
186                 *inputmargin = 0;
187         }
188         *maptype = 2;
189         return src;
190 }
191
192 static int z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq, u8 *out)
193 {
194         unsigned int inputmargin;
195         u8 *headpage, *src;
196         bool support_0padding;
197         int ret, maptype;
198
199         DBG_BUGON(*rq->in == NULL);
200         headpage = kmap_atomic(*rq->in);
201         inputmargin = 0;
202         support_0padding = false;
203
204         /* decompression inplace is only safe when 0padding is enabled */
205         if (erofs_sb_has_lz4_0padding(EROFS_SB(rq->sb))) {
206                 support_0padding = true;
207
208                 while (!headpage[inputmargin & ~PAGE_MASK])
209                         if (!(++inputmargin & ~PAGE_MASK))
210                                 break;
211
212                 if (inputmargin >= rq->inputsize) {
213                         kunmap_atomic(headpage);
214                         return -EIO;
215                 }
216         }
217
218         rq->inputsize -= inputmargin;
219         src = z_erofs_handle_inplace_io(rq, headpage, &inputmargin, &maptype,
220                                         support_0padding);
221         if (IS_ERR(src))
222                 return PTR_ERR(src);
223
224         /* legacy format could compress extra data in a pcluster. */
225         if (rq->partial_decoding || !support_0padding)
226                 ret = LZ4_decompress_safe_partial(src + inputmargin, out,
227                                 rq->inputsize, rq->outputsize, rq->outputsize);
228         else
229                 ret = LZ4_decompress_safe(src + inputmargin, out,
230                                           rq->inputsize, rq->outputsize);
231
232         if (ret != rq->outputsize) {
233                 erofs_err(rq->sb, "failed to decompress %d in[%u, %u] out[%u]",
234                           ret, rq->inputsize, inputmargin, rq->outputsize);
235
236                 WARN_ON(1);
237                 print_hex_dump(KERN_DEBUG, "[ in]: ", DUMP_PREFIX_OFFSET,
238                                16, 1, src + inputmargin, rq->inputsize, true);
239                 print_hex_dump(KERN_DEBUG, "[out]: ", DUMP_PREFIX_OFFSET,
240                                16, 1, out, rq->outputsize, true);
241
242                 if (ret >= 0)
243                         memset(out + ret, 0, rq->outputsize - ret);
244                 ret = -EIO;
245         } else {
246                 ret = 0;
247         }
248
249         if (maptype == 0) {
250                 kunmap_atomic(src);
251         } else if (maptype == 1) {
252                 vm_unmap_ram(src, PAGE_ALIGN(rq->inputsize) >> PAGE_SHIFT);
253         } else if (maptype == 2) {
254                 erofs_put_pcpubuf(src);
255         } else {
256                 DBG_BUGON(1);
257                 return -EFAULT;
258         }
259         return ret;
260 }
261
262 static struct z_erofs_decompressor decompressors[] = {
263         [Z_EROFS_COMPRESSION_SHIFTED] = {
264                 .name = "shifted"
265         },
266         [Z_EROFS_COMPRESSION_LZ4] = {
267                 .prepare_destpages = z_erofs_lz4_prepare_destpages,
268                 .decompress = z_erofs_lz4_decompress,
269                 .name = "lz4"
270         },
271 };
272
273 static int z_erofs_decompress_generic(struct z_erofs_decompress_req *rq,
274                                       struct list_head *pagepool)
275 {
276         const unsigned int nrpages_out =
277                 PAGE_ALIGN(rq->pageofs_out + rq->outputsize) >> PAGE_SHIFT;
278         const struct z_erofs_decompressor *alg = decompressors + rq->alg;
279         unsigned int dst_maptype;
280         void *dst;
281         int ret;
282
283         /* one optimized fast path only for non bigpcluster cases yet */
284         if (rq->inputsize <= PAGE_SIZE && nrpages_out == 1 && !rq->inplace_io) {
285                 DBG_BUGON(!*rq->out);
286                 dst = kmap_atomic(*rq->out);
287                 dst_maptype = 0;
288                 goto dstmap_out;
289         }
290
291         /* general decoding path which can be used for all cases */
292         ret = alg->prepare_destpages(rq, pagepool);
293         if (ret < 0)
294                 return ret;
295         if (ret) {
296                 dst = page_address(*rq->out);
297                 dst_maptype = 1;
298                 goto dstmap_out;
299         }
300
301         dst = erofs_vm_map_ram(rq->out, nrpages_out);
302         if (!dst)
303                 return -ENOMEM;
304         dst_maptype = 2;
305
306 dstmap_out:
307         ret = alg->decompress(rq, dst + rq->pageofs_out);
308
309         if (!dst_maptype)
310                 kunmap_atomic(dst);
311         else if (dst_maptype == 2)
312                 vm_unmap_ram(dst, nrpages_out);
313         return ret;
314 }
315
316 static int z_erofs_shifted_transform(const struct z_erofs_decompress_req *rq,
317                                      struct list_head *pagepool)
318 {
319         const unsigned int nrpages_out =
320                 PAGE_ALIGN(rq->pageofs_out + rq->outputsize) >> PAGE_SHIFT;
321         const unsigned int righthalf = PAGE_SIZE - rq->pageofs_out;
322         unsigned char *src, *dst;
323
324         if (nrpages_out > 2) {
325                 DBG_BUGON(1);
326                 return -EIO;
327         }
328
329         if (rq->out[0] == *rq->in) {
330                 DBG_BUGON(nrpages_out != 1);
331                 return 0;
332         }
333
334         src = kmap_atomic(*rq->in);
335         if (rq->out[0]) {
336                 dst = kmap_atomic(rq->out[0]);
337                 memcpy(dst + rq->pageofs_out, src, righthalf);
338                 kunmap_atomic(dst);
339         }
340
341         if (nrpages_out == 2) {
342                 DBG_BUGON(!rq->out[1]);
343                 if (rq->out[1] == *rq->in) {
344                         memmove(src, src + righthalf, rq->pageofs_out);
345                 } else {
346                         dst = kmap_atomic(rq->out[1]);
347                         memcpy(dst, src + righthalf, rq->pageofs_out);
348                         kunmap_atomic(dst);
349                 }
350         }
351         kunmap_atomic(src);
352         return 0;
353 }
354
355 int z_erofs_decompress(struct z_erofs_decompress_req *rq,
356                        struct list_head *pagepool)
357 {
358         if (rq->alg == Z_EROFS_COMPRESSION_SHIFTED)
359                 return z_erofs_shifted_transform(rq, pagepool);
360         return z_erofs_decompress_generic(rq, pagepool);
361 }