LCOV - code coverage report
Current view: top level - fs/btrfs - zlib.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc3-achx @ Mon Jul 31 20:08:12 PDT 2023 Lines: 192 255 75.3 %
Date: 2023-07-31 20:08:12 Functions: 5 6 83.3 %

          Line data    Source code
       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 "compression.h"
      22             : 
      23             : /* workspace buffer size for s390 zlib hardware support */
      24             : #define ZLIB_DFLTCC_BUF_SIZE    (4 * PAGE_SIZE)
      25             : 
      26             : struct workspace {
      27             :         z_stream strm;
      28             :         char *buf;
      29             :         unsigned int buf_size;
      30             :         struct list_head list;
      31             :         int level;
      32             : };
      33             : 
      34             : static struct workspace_manager wsm;
      35             : 
      36      142620 : struct list_head *zlib_get_workspace(unsigned int level)
      37             : {
      38      142620 :         struct list_head *ws = btrfs_get_workspace(BTRFS_COMPRESS_ZLIB, level);
      39      142652 :         struct workspace *workspace = list_entry(ws, struct workspace, list);
      40             : 
      41      142652 :         workspace->level = level;
      42             : 
      43      142652 :         return ws;
      44             : }
      45             : 
      46           0 : void zlib_free_workspace(struct list_head *ws)
      47             : {
      48           0 :         struct workspace *workspace = list_entry(ws, struct workspace, list);
      49             : 
      50           0 :         kvfree(workspace->strm.workspace);
      51           0 :         kfree(workspace->buf);
      52           0 :         kfree(workspace);
      53           0 : }
      54             : 
      55          15 : struct list_head *zlib_alloc_workspace(unsigned int level)
      56             : {
      57          15 :         struct workspace *workspace;
      58          15 :         int workspacesize;
      59             : 
      60          15 :         workspace = kzalloc(sizeof(*workspace), GFP_KERNEL);
      61          15 :         if (!workspace)
      62             :                 return ERR_PTR(-ENOMEM);
      63             : 
      64          15 :         workspacesize = max(zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL),
      65             :                         zlib_inflate_workspacesize());
      66          15 :         workspace->strm.workspace = kvzalloc(workspacesize, GFP_KERNEL | __GFP_NOWARN);
      67          15 :         workspace->level = level;
      68          15 :         workspace->buf = NULL;
      69             :         /*
      70             :          * In case of s390 zlib hardware support, allocate lager workspace
      71             :          * buffer. If allocator fails, fall back to a single page buffer.
      72             :          */
      73          15 :         if (zlib_deflate_dfltcc_enabled()) {
      74           0 :                 workspace->buf = kmalloc(ZLIB_DFLTCC_BUF_SIZE,
      75             :                                          __GFP_NOMEMALLOC | __GFP_NORETRY |
      76             :                                          __GFP_NOWARN | GFP_NOIO);
      77           0 :                 workspace->buf_size = ZLIB_DFLTCC_BUF_SIZE;
      78             :         }
      79          15 :         if (!workspace->buf) {
      80          15 :                 workspace->buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
      81          15 :                 workspace->buf_size = PAGE_SIZE;
      82             :         }
      83          15 :         if (!workspace->strm.workspace || !workspace->buf)
      84           0 :                 goto fail;
      85             : 
      86          15 :         INIT_LIST_HEAD(&workspace->list);
      87             : 
      88          15 :         return &workspace->list;
      89             : fail:
      90           0 :         zlib_free_workspace(&workspace->list);
      91           0 :         return ERR_PTR(-ENOMEM);
      92             : }
      93             : 
      94      141523 : int zlib_compress_pages(struct list_head *ws, struct address_space *mapping,
      95             :                 u64 start, struct page **pages, unsigned long *out_pages,
      96             :                 unsigned long *total_in, unsigned long *total_out)
      97             : {
      98      141523 :         struct workspace *workspace = list_entry(ws, struct workspace, list);
      99      141523 :         int ret;
     100      141523 :         char *data_in = NULL;
     101      141523 :         char *cpage_out;
     102      141523 :         int nr_pages = 0;
     103      141523 :         struct page *in_page = NULL;
     104      141523 :         struct page *out_page = NULL;
     105      141523 :         unsigned long bytes_left;
     106      141523 :         unsigned int in_buf_pages;
     107      141523 :         unsigned long len = *total_out;
     108      141523 :         unsigned long nr_dest_pages = *out_pages;
     109      141523 :         const unsigned long max_out = nr_dest_pages * PAGE_SIZE;
     110             : 
     111      141523 :         *out_pages = 0;
     112      141523 :         *total_out = 0;
     113      141523 :         *total_in = 0;
     114             : 
     115      141523 :         if (Z_OK != zlib_deflateInit(&workspace->strm, workspace->level)) {
     116           0 :                 pr_warn("BTRFS: deflateInit failed\n");
     117           0 :                 ret = -EIO;
     118           0 :                 goto out;
     119             :         }
     120             : 
     121      141512 :         workspace->strm.total_in = 0;
     122      141512 :         workspace->strm.total_out = 0;
     123             : 
     124      141512 :         out_page = alloc_page(GFP_NOFS);
     125      141498 :         if (out_page == NULL) {
     126           0 :                 ret = -ENOMEM;
     127           0 :                 goto out;
     128             :         }
     129      141498 :         cpage_out = page_address(out_page);
     130      141498 :         pages[0] = out_page;
     131      141498 :         nr_pages = 1;
     132             : 
     133      141498 :         workspace->strm.next_in = workspace->buf;
     134      141498 :         workspace->strm.avail_in = 0;
     135      141498 :         workspace->strm.next_out = cpage_out;
     136      141498 :         workspace->strm.avail_out = PAGE_SIZE;
     137             : 
     138     4487652 :         while (workspace->strm.total_in < len) {
     139             :                 /*
     140             :                  * Get next input pages and copy the contents to
     141             :                  * the workspace buffer if required.
     142             :                  */
     143     4487652 :                 if (workspace->strm.avail_in == 0) {
     144     4477351 :                         bytes_left = len - workspace->strm.total_in;
     145     4477351 :                         in_buf_pages = min(DIV_ROUND_UP(bytes_left, PAGE_SIZE),
     146             :                                            workspace->buf_size / PAGE_SIZE);
     147     4477351 :                         if (in_buf_pages > 1) {
     148             :                                 int i;
     149             : 
     150           0 :                                 for (i = 0; i < in_buf_pages; i++) {
     151           0 :                                         if (data_in) {
     152           0 :                                                 kunmap_local(data_in);
     153           0 :                                                 put_page(in_page);
     154             :                                         }
     155           0 :                                         in_page = find_get_page(mapping,
     156           0 :                                                                 start >> PAGE_SHIFT);
     157           0 :                                         data_in = kmap_local_page(in_page);
     158           0 :                                         copy_page(workspace->buf + i * PAGE_SIZE,
     159             :                                                   data_in);
     160           0 :                                         start += PAGE_SIZE;
     161             :                                 }
     162           0 :                                 workspace->strm.next_in = workspace->buf;
     163             :                         } else {
     164     4477351 :                                 if (data_in) {
     165     4335863 :                                         kunmap_local(data_in);
     166     4335863 :                                         put_page(in_page);
     167             :                                 }
     168     4491990 :                                 in_page = find_get_page(mapping,
     169     4491990 :                                                         start >> PAGE_SHIFT);
     170     4515870 :                                 data_in = kmap_local_page(in_page);
     171     4515870 :                                 start += PAGE_SIZE;
     172     4515870 :                                 workspace->strm.next_in = data_in;
     173             :                         }
     174     4515870 :                         workspace->strm.avail_in = min(bytes_left,
     175             :                                                        (unsigned long) workspace->buf_size);
     176             :                 }
     177             : 
     178     4526171 :                 ret = zlib_deflate(&workspace->strm, Z_SYNC_FLUSH);
     179     4486924 :                 if (ret != Z_OK) {
     180           0 :                         pr_debug("BTRFS: deflate in loop returned %d\n",
     181             :                                ret);
     182           0 :                         zlib_deflateEnd(&workspace->strm);
     183           0 :                         ret = -EIO;
     184           0 :                         goto out;
     185             :                 }
     186             : 
     187             :                 /* we're making it bigger, give up */
     188     4486924 :                 if (workspace->strm.total_in > 8192 &&
     189             :                     workspace->strm.total_in <
     190     4218759 :                     workspace->strm.total_out) {
     191           0 :                         ret = -E2BIG;
     192           0 :                         goto out;
     193             :                 }
     194             :                 /* we need another page for writing out.  Test this
     195             :                  * before the total_in so we will pull in a new page for
     196             :                  * the stream end if required
     197             :                  */
     198     4486924 :                 if (workspace->strm.avail_out == 0) {
     199      106486 :                         if (nr_pages == nr_dest_pages) {
     200           1 :                                 ret = -E2BIG;
     201           1 :                                 goto out;
     202             :                         }
     203      106485 :                         out_page = alloc_page(GFP_NOFS);
     204      106489 :                         if (out_page == NULL) {
     205           0 :                                 ret = -ENOMEM;
     206           0 :                                 goto out;
     207             :                         }
     208      106489 :                         cpage_out = page_address(out_page);
     209      106489 :                         pages[nr_pages] = out_page;
     210      106489 :                         nr_pages++;
     211      106489 :                         workspace->strm.avail_out = PAGE_SIZE;
     212      106489 :                         workspace->strm.next_out = cpage_out;
     213             :                 }
     214             :                 /* we're all done */
     215     4486927 :                 if (workspace->strm.total_in >= len)
     216             :                         break;
     217     4346154 :                 if (workspace->strm.total_out > max_out)
     218             :                         break;
     219             :         }
     220      140773 :         workspace->strm.avail_in = 0;
     221             :         /*
     222             :          * Call deflate with Z_FINISH flush parameter providing more output
     223             :          * space but no more input data, until it returns with Z_STREAM_END.
     224             :          */
     225      140773 :         while (ret != Z_STREAM_END) {
     226      140773 :                 ret = zlib_deflate(&workspace->strm, Z_FINISH);
     227      140944 :                 if (ret == Z_STREAM_END)
     228             :                         break;
     229           0 :                 if (ret != Z_OK && ret != Z_BUF_ERROR) {
     230           0 :                         zlib_deflateEnd(&workspace->strm);
     231           0 :                         ret = -EIO;
     232           0 :                         goto out;
     233           0 :                 } else if (workspace->strm.avail_out == 0) {
     234             :                         /* get another page for the stream end */
     235           0 :                         if (nr_pages == nr_dest_pages) {
     236           0 :                                 ret = -E2BIG;
     237           0 :                                 goto out;
     238             :                         }
     239           0 :                         out_page = alloc_page(GFP_NOFS);
     240           0 :                         if (out_page == NULL) {
     241           0 :                                 ret = -ENOMEM;
     242           0 :                                 goto out;
     243             :                         }
     244           0 :                         cpage_out = page_address(out_page);
     245           0 :                         pages[nr_pages] = out_page;
     246           0 :                         nr_pages++;
     247           0 :                         workspace->strm.avail_out = PAGE_SIZE;
     248           0 :                         workspace->strm.next_out = cpage_out;
     249             :                 }
     250             :         }
     251      140944 :         zlib_deflateEnd(&workspace->strm);
     252             : 
     253      140738 :         if (workspace->strm.total_out >= workspace->strm.total_in) {
     254           0 :                 ret = -E2BIG;
     255           0 :                 goto out;
     256             :         }
     257             : 
     258      140738 :         ret = 0;
     259      140738 :         *total_out = workspace->strm.total_out;
     260      140738 :         *total_in = workspace->strm.total_in;
     261      140739 : out:
     262      140739 :         *out_pages = nr_pages;
     263      140739 :         if (data_in) {
     264      140634 :                 kunmap_local(data_in);
     265      140634 :                 put_page(in_page);
     266             :         }
     267             : 
     268      141099 :         return ret;
     269             : }
     270             : 
     271        1094 : int zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
     272             : {
     273        1094 :         struct workspace *workspace = list_entry(ws, struct workspace, list);
     274        1094 :         int ret = 0, ret2;
     275        1094 :         int wbits = MAX_WBITS;
     276        1094 :         char *data_in;
     277        1094 :         size_t total_out = 0;
     278        1094 :         unsigned long page_in_index = 0;
     279        1094 :         size_t srclen = cb->compressed_len;
     280        1094 :         unsigned long total_pages_in = DIV_ROUND_UP(srclen, PAGE_SIZE);
     281        1094 :         unsigned long buf_start;
     282        1094 :         struct page **pages_in = cb->compressed_pages;
     283             : 
     284        1094 :         data_in = kmap_local_page(pages_in[page_in_index]);
     285        1094 :         workspace->strm.next_in = data_in;
     286        1094 :         workspace->strm.avail_in = min_t(size_t, srclen, PAGE_SIZE);
     287        1094 :         workspace->strm.total_in = 0;
     288             : 
     289        1094 :         workspace->strm.total_out = 0;
     290        1094 :         workspace->strm.next_out = workspace->buf;
     291        1094 :         workspace->strm.avail_out = workspace->buf_size;
     292             : 
     293             :         /* If it's deflate, and it's got no preset dictionary, then
     294             :            we can tell zlib to skip the adler32 check. */
     295        1094 :         if (srclen > 2 && !(data_in[1] & PRESET_DICT) &&
     296        1094 :             ((data_in[0] & 0x0f) == Z_DEFLATED) &&
     297        1094 :             !(((data_in[0]<<8) + data_in[1]) % 31)) {
     298             : 
     299        1094 :                 wbits = -((data_in[0] >> 4) + 8);
     300        1094 :                 workspace->strm.next_in += 2;
     301        1094 :                 workspace->strm.avail_in -= 2;
     302             :         }
     303             : 
     304        1094 :         if (Z_OK != zlib_inflateInit2(&workspace->strm, wbits)) {
     305           0 :                 pr_warn("BTRFS: inflateInit failed\n");
     306           0 :                 kunmap_local(data_in);
     307           0 :                 return -EIO;
     308             :         }
     309       34343 :         while (workspace->strm.total_in < srclen) {
     310       34343 :                 ret = zlib_inflate(&workspace->strm, Z_NO_FLUSH);
     311       34401 :                 if (ret != Z_OK && ret != Z_STREAM_END)
     312             :                         break;
     313             : 
     314       34401 :                 buf_start = total_out;
     315       34401 :                 total_out = workspace->strm.total_out;
     316             : 
     317             :                 /* we didn't make progress in this inflate call, we're done */
     318       34401 :                 if (buf_start == total_out)
     319             :                         break;
     320             : 
     321       34401 :                 ret2 = btrfs_decompress_buf2page(workspace->buf,
     322             :                                 total_out - buf_start, cb, buf_start);
     323       34343 :                 if (ret2 == 0) {
     324        1094 :                         ret = 0;
     325        1094 :                         goto done;
     326             :                 }
     327             : 
     328       33249 :                 workspace->strm.next_out = workspace->buf;
     329       33249 :                 workspace->strm.avail_out = workspace->buf_size;
     330             : 
     331       33249 :                 if (workspace->strm.avail_in == 0) {
     332          52 :                         unsigned long tmp;
     333          52 :                         kunmap_local(data_in);
     334          52 :                         page_in_index++;
     335          52 :                         if (page_in_index >= total_pages_in) {
     336             :                                 data_in = NULL;
     337             :                                 break;
     338             :                         }
     339          52 :                         data_in = kmap_local_page(pages_in[page_in_index]);
     340          52 :                         workspace->strm.next_in = data_in;
     341          52 :                         tmp = srclen - workspace->strm.total_in;
     342          52 :                         workspace->strm.avail_in = min(tmp, PAGE_SIZE);
     343             :                 }
     344             :         }
     345           0 :         if (ret != Z_STREAM_END)
     346             :                 ret = -EIO;
     347             :         else
     348           0 :                 ret = 0;
     349        1094 : done:
     350        1094 :         zlib_inflateEnd(&workspace->strm);
     351        1094 :         if (data_in)
     352        1094 :                 kunmap_local(data_in);
     353        1094 :         return ret;
     354             : }
     355             : 
     356          25 : int zlib_decompress(struct list_head *ws, const u8 *data_in,
     357             :                 struct page *dest_page, unsigned long start_byte, size_t srclen,
     358             :                 size_t destlen)
     359             : {
     360          25 :         struct workspace *workspace = list_entry(ws, struct workspace, list);
     361          25 :         int ret = 0;
     362          25 :         int wbits = MAX_WBITS;
     363          25 :         unsigned long bytes_left;
     364          25 :         unsigned long total_out = 0;
     365          25 :         unsigned long pg_offset = 0;
     366             : 
     367          25 :         destlen = min_t(unsigned long, destlen, PAGE_SIZE);
     368          25 :         bytes_left = destlen;
     369             : 
     370          25 :         workspace->strm.next_in = data_in;
     371          25 :         workspace->strm.avail_in = srclen;
     372          25 :         workspace->strm.total_in = 0;
     373             : 
     374          25 :         workspace->strm.next_out = workspace->buf;
     375          25 :         workspace->strm.avail_out = workspace->buf_size;
     376          25 :         workspace->strm.total_out = 0;
     377             :         /* If it's deflate, and it's got no preset dictionary, then
     378             :            we can tell zlib to skip the adler32 check. */
     379          25 :         if (srclen > 2 && !(data_in[1] & PRESET_DICT) &&
     380          25 :             ((data_in[0] & 0x0f) == Z_DEFLATED) &&
     381          25 :             !(((data_in[0]<<8) + data_in[1]) % 31)) {
     382             : 
     383          25 :                 wbits = -((data_in[0] >> 4) + 8);
     384          25 :                 workspace->strm.next_in += 2;
     385          25 :                 workspace->strm.avail_in -= 2;
     386             :         }
     387             : 
     388          25 :         if (Z_OK != zlib_inflateInit2(&workspace->strm, wbits)) {
     389           0 :                 pr_warn("BTRFS: inflateInit failed\n");
     390           0 :                 return -EIO;
     391             :         }
     392             : 
     393          50 :         while (bytes_left > 0) {
     394          25 :                 unsigned long buf_start;
     395          25 :                 unsigned long buf_offset;
     396          25 :                 unsigned long bytes;
     397             : 
     398          25 :                 ret = zlib_inflate(&workspace->strm, Z_NO_FLUSH);
     399          25 :                 if (ret != Z_OK && ret != Z_STREAM_END)
     400             :                         break;
     401             : 
     402          25 :                 buf_start = total_out;
     403          25 :                 total_out = workspace->strm.total_out;
     404             : 
     405          25 :                 if (total_out == buf_start) {
     406             :                         ret = -EIO;
     407             :                         break;
     408             :                 }
     409             : 
     410          25 :                 if (total_out <= start_byte)
     411           0 :                         goto next;
     412             : 
     413          25 :                 if (total_out > start_byte && buf_start < start_byte)
     414           0 :                         buf_offset = start_byte - buf_start;
     415             :                 else
     416             :                         buf_offset = 0;
     417             : 
     418          25 :                 bytes = min(PAGE_SIZE - pg_offset,
     419             :                             PAGE_SIZE - (buf_offset % PAGE_SIZE));
     420          25 :                 bytes = min(bytes, bytes_left);
     421             : 
     422          25 :                 memcpy_to_page(dest_page, pg_offset,
     423          25 :                                workspace->buf + buf_offset, bytes);
     424             : 
     425          25 :                 pg_offset += bytes;
     426          25 :                 bytes_left -= bytes;
     427          25 : next:
     428          25 :                 workspace->strm.next_out = workspace->buf;
     429          25 :                 workspace->strm.avail_out = workspace->buf_size;
     430             :         }
     431             : 
     432          25 :         if (ret != Z_STREAM_END && bytes_left != 0)
     433             :                 ret = -EIO;
     434             :         else
     435          25 :                 ret = 0;
     436             : 
     437          25 :         zlib_inflateEnd(&workspace->strm);
     438             : 
     439             :         /*
     440             :          * this should only happen if zlib returned fewer bytes than we
     441             :          * expected.  btrfs_get_block is responsible for zeroing from the
     442             :          * end of the inline extent (destlen) to the end of the page
     443             :          */
     444          25 :         if (pg_offset < destlen) {
     445           0 :                 memzero_page(dest_page, pg_offset, destlen - pg_offset);
     446             :         }
     447             :         return ret;
     448             : }
     449             : 
     450             : const struct btrfs_compress_op btrfs_zlib_compress = {
     451             :         .workspace_manager      = &wsm,
     452             :         .max_level              = 9,
     453             :         .default_level          = BTRFS_ZLIB_DEFAULT_LEVEL,
     454             : };

Generated by: LCOV version 1.14