~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/fs/btrfs/zlib.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 /*
  3  * Copyright (C) 2008 Oracle.  All rights reserved.
  4  *
  5  * Based on jffs2 zlib code:
  6  * Copyright © 2001-2007 Red Hat, Inc.
  7  * Created by David Woodhouse <dwmw2@infradead.org>
  8  */
  9 
 10 #include <linux/kernel.h>
 11 #include <linux/slab.h>
 12 #include <linux/zlib.h>
 13 #include <linux/zutil.h>
 14 #include <linux/mm.h>
 15 #include <linux/init.h>
 16 #include <linux/err.h>
 17 #include <linux/sched.h>
 18 #include <linux/pagemap.h>
 19 #include <linux/bio.h>
 20 #include <linux/refcount.h>
 21 #include "btrfs_inode.h"
 22 #include "compression.h"
 23 
 24 /* workspace buffer size for s390 zlib hardware support */
 25 #define ZLIB_DFLTCC_BUF_SIZE    (4 * PAGE_SIZE)
 26 
 27 struct workspace {
 28         z_stream strm;
 29         char *buf;
 30         unsigned int buf_size;
 31         struct list_head list;
 32         int level;
 33 };
 34 
 35 static struct workspace_manager wsm;
 36 
 37 struct list_head *zlib_get_workspace(unsigned int level)
 38 {
 39         struct list_head *ws = btrfs_get_workspace(BTRFS_COMPRESS_ZLIB, level);
 40         struct workspace *workspace = list_entry(ws, struct workspace, list);
 41 
 42         workspace->level = level;
 43 
 44         return ws;
 45 }
 46 
 47 void zlib_free_workspace(struct list_head *ws)
 48 {
 49         struct workspace *workspace = list_entry(ws, struct workspace, list);
 50 
 51         kvfree(workspace->strm.workspace);
 52         kfree(workspace->buf);
 53         kfree(workspace);
 54 }
 55 
 56 struct list_head *zlib_alloc_workspace(unsigned int level)
 57 {
 58         struct workspace *workspace;
 59         int workspacesize;
 60 
 61         workspace = kzalloc(sizeof(*workspace), GFP_KERNEL);
 62         if (!workspace)
 63                 return ERR_PTR(-ENOMEM);
 64 
 65         workspacesize = max(zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL),
 66                         zlib_inflate_workspacesize());
 67         workspace->strm.workspace = kvzalloc(workspacesize, GFP_KERNEL | __GFP_NOWARN);
 68         workspace->level = level;
 69         workspace->buf = NULL;
 70         /*
 71          * In case of s390 zlib hardware support, allocate lager workspace
 72          * buffer. If allocator fails, fall back to a single page buffer.
 73          */
 74         if (zlib_deflate_dfltcc_enabled()) {
 75                 workspace->buf = kmalloc(ZLIB_DFLTCC_BUF_SIZE,
 76                                          __GFP_NOMEMALLOC | __GFP_NORETRY |
 77                                          __GFP_NOWARN | GFP_NOIO);
 78                 workspace->buf_size = ZLIB_DFLTCC_BUF_SIZE;
 79         }
 80         if (!workspace->buf) {
 81                 workspace->buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
 82                 workspace->buf_size = PAGE_SIZE;
 83         }
 84         if (!workspace->strm.workspace || !workspace->buf)
 85                 goto fail;
 86 
 87         INIT_LIST_HEAD(&workspace->list);
 88 
 89         return &workspace->list;
 90 fail:
 91         zlib_free_workspace(&workspace->list);
 92         return ERR_PTR(-ENOMEM);
 93 }
 94 
 95 int zlib_compress_folios(struct list_head *ws, struct address_space *mapping,
 96                          u64 start, struct folio **folios, unsigned long *out_folios,
 97                          unsigned long *total_in, unsigned long *total_out)
 98 {
 99         struct workspace *workspace = list_entry(ws, struct workspace, list);
100         int ret;
101         char *data_in = NULL;
102         char *cfolio_out;
103         int nr_folios = 0;
104         struct folio *in_folio = NULL;
105         struct folio *out_folio = NULL;
106         unsigned long bytes_left;
107         unsigned int in_buf_folios;
108         unsigned long len = *total_out;
109         unsigned long nr_dest_folios = *out_folios;
110         const unsigned long max_out = nr_dest_folios * PAGE_SIZE;
111 
112         *out_folios = 0;
113         *total_out = 0;
114         *total_in = 0;
115 
116         ret = zlib_deflateInit(&workspace->strm, workspace->level);
117         if (unlikely(ret != Z_OK)) {
118                 struct btrfs_inode *inode = BTRFS_I(mapping->host);
119 
120                 btrfs_err(inode->root->fs_info,
121         "zlib compression init failed, error %d root %llu inode %llu offset %llu",
122                           ret, btrfs_root_id(inode->root), btrfs_ino(inode), start);
123                 ret = -EIO;
124                 goto out;
125         }
126 
127         workspace->strm.total_in = 0;
128         workspace->strm.total_out = 0;
129 
130         out_folio = btrfs_alloc_compr_folio();
131         if (out_folio == NULL) {
132                 ret = -ENOMEM;
133                 goto out;
134         }
135         cfolio_out = folio_address(out_folio);
136         folios[0] = out_folio;
137         nr_folios = 1;
138 
139         workspace->strm.next_in = workspace->buf;
140         workspace->strm.avail_in = 0;
141         workspace->strm.next_out = cfolio_out;
142         workspace->strm.avail_out = PAGE_SIZE;
143 
144         while (workspace->strm.total_in < len) {
145                 /*
146                  * Get next input pages and copy the contents to
147                  * the workspace buffer if required.
148                  */
149                 if (workspace->strm.avail_in == 0) {
150                         bytes_left = len - workspace->strm.total_in;
151                         in_buf_folios = min(DIV_ROUND_UP(bytes_left, PAGE_SIZE),
152                                             workspace->buf_size / PAGE_SIZE);
153                         if (in_buf_folios > 1) {
154                                 int i;
155 
156                                 for (i = 0; i < in_buf_folios; i++) {
157                                         if (data_in) {
158                                                 kunmap_local(data_in);
159                                                 folio_put(in_folio);
160                                                 data_in = NULL;
161                                         }
162                                         ret = btrfs_compress_filemap_get_folio(mapping,
163                                                         start, &in_folio);
164                                         if (ret < 0)
165                                                 goto out;
166                                         data_in = kmap_local_folio(in_folio, 0);
167                                         copy_page(workspace->buf + i * PAGE_SIZE,
168                                                   data_in);
169                                         start += PAGE_SIZE;
170                                 }
171                                 workspace->strm.next_in = workspace->buf;
172                         } else {
173                                 if (data_in) {
174                                         kunmap_local(data_in);
175                                         folio_put(in_folio);
176                                         data_in = NULL;
177                                 }
178                                 ret = btrfs_compress_filemap_get_folio(mapping,
179                                                 start, &in_folio);
180                                 if (ret < 0)
181                                         goto out;
182                                 data_in = kmap_local_folio(in_folio, 0);
183                                 start += PAGE_SIZE;
184                                 workspace->strm.next_in = data_in;
185                         }
186                         workspace->strm.avail_in = min(bytes_left,
187                                                        (unsigned long) workspace->buf_size);
188                 }
189 
190                 ret = zlib_deflate(&workspace->strm, Z_SYNC_FLUSH);
191                 if (unlikely(ret != Z_OK)) {
192                         struct btrfs_inode *inode = BTRFS_I(mapping->host);
193 
194                         btrfs_warn(inode->root->fs_info,
195                 "zlib compression failed, error %d root %llu inode %llu offset %llu",
196                                    ret, btrfs_root_id(inode->root), btrfs_ino(inode),
197                                    start);
198                         zlib_deflateEnd(&workspace->strm);
199                         ret = -EIO;
200                         goto out;
201                 }
202 
203                 /* we're making it bigger, give up */
204                 if (workspace->strm.total_in > 8192 &&
205                     workspace->strm.total_in <
206                     workspace->strm.total_out) {
207                         ret = -E2BIG;
208                         goto out;
209                 }
210                 /* we need another page for writing out.  Test this
211                  * before the total_in so we will pull in a new page for
212                  * the stream end if required
213                  */
214                 if (workspace->strm.avail_out == 0) {
215                         if (nr_folios == nr_dest_folios) {
216                                 ret = -E2BIG;
217                                 goto out;
218                         }
219                         out_folio = btrfs_alloc_compr_folio();
220                         if (out_folio == NULL) {
221                                 ret = -ENOMEM;
222                                 goto out;
223                         }
224                         cfolio_out = folio_address(out_folio);
225                         folios[nr_folios] = out_folio;
226                         nr_folios++;
227                         workspace->strm.avail_out = PAGE_SIZE;
228                         workspace->strm.next_out = cfolio_out;
229                 }
230                 /* we're all done */
231                 if (workspace->strm.total_in >= len)
232                         break;
233                 if (workspace->strm.total_out > max_out)
234                         break;
235         }
236         workspace->strm.avail_in = 0;
237         /*
238          * Call deflate with Z_FINISH flush parameter providing more output
239          * space but no more input data, until it returns with Z_STREAM_END.
240          */
241         while (ret != Z_STREAM_END) {
242                 ret = zlib_deflate(&workspace->strm, Z_FINISH);
243                 if (ret == Z_STREAM_END)
244                         break;
245                 if (ret != Z_OK && ret != Z_BUF_ERROR) {
246                         zlib_deflateEnd(&workspace->strm);
247                         ret = -EIO;
248                         goto out;
249                 } else if (workspace->strm.avail_out == 0) {
250                         /* Get another folio for the stream end. */
251                         if (nr_folios == nr_dest_folios) {
252                                 ret = -E2BIG;
253                                 goto out;
254                         }
255                         out_folio = btrfs_alloc_compr_folio();
256                         if (out_folio == NULL) {
257                                 ret = -ENOMEM;
258                                 goto out;
259                         }
260                         cfolio_out = folio_address(out_folio);
261                         folios[nr_folios] = out_folio;
262                         nr_folios++;
263                         workspace->strm.avail_out = PAGE_SIZE;
264                         workspace->strm.next_out = cfolio_out;
265                 }
266         }
267         zlib_deflateEnd(&workspace->strm);
268 
269         if (workspace->strm.total_out >= workspace->strm.total_in) {
270                 ret = -E2BIG;
271                 goto out;
272         }
273 
274         ret = 0;
275         *total_out = workspace->strm.total_out;
276         *total_in = workspace->strm.total_in;
277 out:
278         *out_folios = nr_folios;
279         if (data_in) {
280                 kunmap_local(data_in);
281                 folio_put(in_folio);
282         }
283 
284         return ret;
285 }
286 
287 int zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
288 {
289         struct workspace *workspace = list_entry(ws, struct workspace, list);
290         int ret = 0, ret2;
291         int wbits = MAX_WBITS;
292         char *data_in;
293         size_t total_out = 0;
294         unsigned long folio_in_index = 0;
295         size_t srclen = cb->compressed_len;
296         unsigned long total_folios_in = DIV_ROUND_UP(srclen, PAGE_SIZE);
297         unsigned long buf_start;
298         struct folio **folios_in = cb->compressed_folios;
299 
300         data_in = kmap_local_folio(folios_in[folio_in_index], 0);
301         workspace->strm.next_in = data_in;
302         workspace->strm.avail_in = min_t(size_t, srclen, PAGE_SIZE);
303         workspace->strm.total_in = 0;
304 
305         workspace->strm.total_out = 0;
306         workspace->strm.next_out = workspace->buf;
307         workspace->strm.avail_out = workspace->buf_size;
308 
309         /* If it's deflate, and it's got no preset dictionary, then
310            we can tell zlib to skip the adler32 check. */
311         if (srclen > 2 && !(data_in[1] & PRESET_DICT) &&
312             ((data_in[0] & 0x0f) == Z_DEFLATED) &&
313             !(((data_in[0]<<8) + data_in[1]) % 31)) {
314 
315                 wbits = -((data_in[0] >> 4) + 8);
316                 workspace->strm.next_in += 2;
317                 workspace->strm.avail_in -= 2;
318         }
319 
320         ret = zlib_inflateInit2(&workspace->strm, wbits);
321         if (unlikely(ret != Z_OK)) {
322                 struct btrfs_inode *inode = cb->bbio.inode;
323 
324                 kunmap_local(data_in);
325                 btrfs_err(inode->root->fs_info,
326         "zlib decompression init failed, error %d root %llu inode %llu offset %llu",
327                           ret, btrfs_root_id(inode->root), btrfs_ino(inode), cb->start);
328                 return -EIO;
329         }
330         while (workspace->strm.total_in < srclen) {
331                 ret = zlib_inflate(&workspace->strm, Z_NO_FLUSH);
332                 if (ret != Z_OK && ret != Z_STREAM_END)
333                         break;
334 
335                 buf_start = total_out;
336                 total_out = workspace->strm.total_out;
337 
338                 /* we didn't make progress in this inflate call, we're done */
339                 if (buf_start == total_out)
340                         break;
341 
342                 ret2 = btrfs_decompress_buf2page(workspace->buf,
343                                 total_out - buf_start, cb, buf_start);
344                 if (ret2 == 0) {
345                         ret = 0;
346                         goto done;
347                 }
348 
349                 workspace->strm.next_out = workspace->buf;
350                 workspace->strm.avail_out = workspace->buf_size;
351 
352                 if (workspace->strm.avail_in == 0) {
353                         unsigned long tmp;
354                         kunmap_local(data_in);
355                         folio_in_index++;
356                         if (folio_in_index >= total_folios_in) {
357                                 data_in = NULL;
358                                 break;
359                         }
360                         data_in = kmap_local_folio(folios_in[folio_in_index], 0);
361                         workspace->strm.next_in = data_in;
362                         tmp = srclen - workspace->strm.total_in;
363                         workspace->strm.avail_in = min(tmp, PAGE_SIZE);
364                 }
365         }
366         if (unlikely(ret != Z_STREAM_END)) {
367                 btrfs_err(cb->bbio.inode->root->fs_info,
368                 "zlib decompression failed, error %d root %llu inode %llu offset %llu",
369                           ret, btrfs_root_id(cb->bbio.inode->root),
370                           btrfs_ino(cb->bbio.inode), cb->start);
371                 ret = -EIO;
372         } else {
373                 ret = 0;
374         }
375 done:
376         zlib_inflateEnd(&workspace->strm);
377         if (data_in)
378                 kunmap_local(data_in);
379         return ret;
380 }
381 
382 int zlib_decompress(struct list_head *ws, const u8 *data_in,
383                 struct page *dest_page, unsigned long dest_pgoff, size_t srclen,
384                 size_t destlen)
385 {
386         struct workspace *workspace = list_entry(ws, struct workspace, list);
387         int ret = 0;
388         int wbits = MAX_WBITS;
389         unsigned long to_copy;
390 
391         workspace->strm.next_in = data_in;
392         workspace->strm.avail_in = srclen;
393         workspace->strm.total_in = 0;
394 
395         workspace->strm.next_out = workspace->buf;
396         workspace->strm.avail_out = workspace->buf_size;
397         workspace->strm.total_out = 0;
398         /* If it's deflate, and it's got no preset dictionary, then
399            we can tell zlib to skip the adler32 check. */
400         if (srclen > 2 && !(data_in[1] & PRESET_DICT) &&
401             ((data_in[0] & 0x0f) == Z_DEFLATED) &&
402             !(((data_in[0]<<8) + data_in[1]) % 31)) {
403 
404                 wbits = -((data_in[0] >> 4) + 8);
405                 workspace->strm.next_in += 2;
406                 workspace->strm.avail_in -= 2;
407         }
408 
409         ret = zlib_inflateInit2(&workspace->strm, wbits);
410         if (unlikely(ret != Z_OK)) {
411                 struct btrfs_inode *inode = BTRFS_I(dest_page->mapping->host);
412 
413                 btrfs_err(inode->root->fs_info,
414                 "zlib decompression init failed, error %d root %llu inode %llu offset %llu",
415                           ret, btrfs_root_id(inode->root), btrfs_ino(inode),
416                           page_offset(dest_page));
417                 return -EIO;
418         }
419 
420         /*
421          * Everything (in/out buf) should be at most one sector, there should
422          * be no need to switch any input/output buffer.
423          */
424         ret = zlib_inflate(&workspace->strm, Z_FINISH);
425         to_copy = min(workspace->strm.total_out, destlen);
426         if (ret != Z_STREAM_END)
427                 goto out;
428 
429         memcpy_to_page(dest_page, dest_pgoff, workspace->buf, to_copy);
430 
431 out:
432         if (unlikely(to_copy != destlen)) {
433                 struct btrfs_inode *inode = BTRFS_I(dest_page->mapping->host);
434 
435                 btrfs_err(inode->root->fs_info,
436 "zlib decompression failed, error %d root %llu inode %llu offset %llu decompressed %lu expected %zu",
437                           ret, btrfs_root_id(inode->root), btrfs_ino(inode),
438                           page_offset(dest_page), to_copy, destlen);
439                 ret = -EIO;
440         } else {
441                 ret = 0;
442         }
443 
444         zlib_inflateEnd(&workspace->strm);
445 
446         if (unlikely(to_copy < destlen))
447                 memzero_page(dest_page, dest_pgoff + to_copy, destlen - to_copy);
448         return ret;
449 }
450 
451 const struct btrfs_compress_op btrfs_zlib_compress = {
452         .workspace_manager      = &wsm,
453         .max_level              = 9,
454         .default_level          = BTRFS_ZLIB_DEFAULT_LEVEL,
455 };
456 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php