162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2008 Oracle. All rights reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Based on jffs2 zlib code: 662306a36Sopenharmony_ci * Copyright © 2001-2007 Red Hat, Inc. 762306a36Sopenharmony_ci * Created by David Woodhouse <dwmw2@infradead.org> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/zlib.h> 1362306a36Sopenharmony_ci#include <linux/zutil.h> 1462306a36Sopenharmony_ci#include <linux/mm.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/err.h> 1762306a36Sopenharmony_ci#include <linux/sched.h> 1862306a36Sopenharmony_ci#include <linux/pagemap.h> 1962306a36Sopenharmony_ci#include <linux/bio.h> 2062306a36Sopenharmony_ci#include <linux/refcount.h> 2162306a36Sopenharmony_ci#include "compression.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* workspace buffer size for s390 zlib hardware support */ 2462306a36Sopenharmony_ci#define ZLIB_DFLTCC_BUF_SIZE (4 * PAGE_SIZE) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct workspace { 2762306a36Sopenharmony_ci z_stream strm; 2862306a36Sopenharmony_ci char *buf; 2962306a36Sopenharmony_ci unsigned int buf_size; 3062306a36Sopenharmony_ci struct list_head list; 3162306a36Sopenharmony_ci int level; 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic struct workspace_manager wsm; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct list_head *zlib_get_workspace(unsigned int level) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct list_head *ws = btrfs_get_workspace(BTRFS_COMPRESS_ZLIB, level); 3962306a36Sopenharmony_ci struct workspace *workspace = list_entry(ws, struct workspace, list); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci workspace->level = level; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return ws; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_civoid zlib_free_workspace(struct list_head *ws) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct workspace *workspace = list_entry(ws, struct workspace, list); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci kvfree(workspace->strm.workspace); 5162306a36Sopenharmony_ci kfree(workspace->buf); 5262306a36Sopenharmony_ci kfree(workspace); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistruct list_head *zlib_alloc_workspace(unsigned int level) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct workspace *workspace; 5862306a36Sopenharmony_ci int workspacesize; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci workspace = kzalloc(sizeof(*workspace), GFP_KERNEL); 6162306a36Sopenharmony_ci if (!workspace) 6262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci workspacesize = max(zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL), 6562306a36Sopenharmony_ci zlib_inflate_workspacesize()); 6662306a36Sopenharmony_ci workspace->strm.workspace = kvzalloc(workspacesize, GFP_KERNEL | __GFP_NOWARN); 6762306a36Sopenharmony_ci workspace->level = level; 6862306a36Sopenharmony_ci workspace->buf = NULL; 6962306a36Sopenharmony_ci /* 7062306a36Sopenharmony_ci * In case of s390 zlib hardware support, allocate lager workspace 7162306a36Sopenharmony_ci * buffer. If allocator fails, fall back to a single page buffer. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci if (zlib_deflate_dfltcc_enabled()) { 7462306a36Sopenharmony_ci workspace->buf = kmalloc(ZLIB_DFLTCC_BUF_SIZE, 7562306a36Sopenharmony_ci __GFP_NOMEMALLOC | __GFP_NORETRY | 7662306a36Sopenharmony_ci __GFP_NOWARN | GFP_NOIO); 7762306a36Sopenharmony_ci workspace->buf_size = ZLIB_DFLTCC_BUF_SIZE; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci if (!workspace->buf) { 8062306a36Sopenharmony_ci workspace->buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 8162306a36Sopenharmony_ci workspace->buf_size = PAGE_SIZE; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci if (!workspace->strm.workspace || !workspace->buf) 8462306a36Sopenharmony_ci goto fail; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci INIT_LIST_HEAD(&workspace->list); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return &workspace->list; 8962306a36Sopenharmony_cifail: 9062306a36Sopenharmony_ci zlib_free_workspace(&workspace->list); 9162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ciint zlib_compress_pages(struct list_head *ws, struct address_space *mapping, 9562306a36Sopenharmony_ci u64 start, struct page **pages, unsigned long *out_pages, 9662306a36Sopenharmony_ci unsigned long *total_in, unsigned long *total_out) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct workspace *workspace = list_entry(ws, struct workspace, list); 9962306a36Sopenharmony_ci int ret; 10062306a36Sopenharmony_ci char *data_in = NULL; 10162306a36Sopenharmony_ci char *cpage_out; 10262306a36Sopenharmony_ci int nr_pages = 0; 10362306a36Sopenharmony_ci struct page *in_page = NULL; 10462306a36Sopenharmony_ci struct page *out_page = NULL; 10562306a36Sopenharmony_ci unsigned long bytes_left; 10662306a36Sopenharmony_ci unsigned int in_buf_pages; 10762306a36Sopenharmony_ci unsigned long len = *total_out; 10862306a36Sopenharmony_ci unsigned long nr_dest_pages = *out_pages; 10962306a36Sopenharmony_ci const unsigned long max_out = nr_dest_pages * PAGE_SIZE; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci *out_pages = 0; 11262306a36Sopenharmony_ci *total_out = 0; 11362306a36Sopenharmony_ci *total_in = 0; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (Z_OK != zlib_deflateInit(&workspace->strm, workspace->level)) { 11662306a36Sopenharmony_ci pr_warn("BTRFS: deflateInit failed\n"); 11762306a36Sopenharmony_ci ret = -EIO; 11862306a36Sopenharmony_ci goto out; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci workspace->strm.total_in = 0; 12262306a36Sopenharmony_ci workspace->strm.total_out = 0; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci out_page = alloc_page(GFP_NOFS); 12562306a36Sopenharmony_ci if (out_page == NULL) { 12662306a36Sopenharmony_ci ret = -ENOMEM; 12762306a36Sopenharmony_ci goto out; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci cpage_out = page_address(out_page); 13062306a36Sopenharmony_ci pages[0] = out_page; 13162306a36Sopenharmony_ci nr_pages = 1; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci workspace->strm.next_in = workspace->buf; 13462306a36Sopenharmony_ci workspace->strm.avail_in = 0; 13562306a36Sopenharmony_ci workspace->strm.next_out = cpage_out; 13662306a36Sopenharmony_ci workspace->strm.avail_out = PAGE_SIZE; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci while (workspace->strm.total_in < len) { 13962306a36Sopenharmony_ci /* 14062306a36Sopenharmony_ci * Get next input pages and copy the contents to 14162306a36Sopenharmony_ci * the workspace buffer if required. 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_ci if (workspace->strm.avail_in == 0) { 14462306a36Sopenharmony_ci bytes_left = len - workspace->strm.total_in; 14562306a36Sopenharmony_ci in_buf_pages = min(DIV_ROUND_UP(bytes_left, PAGE_SIZE), 14662306a36Sopenharmony_ci workspace->buf_size / PAGE_SIZE); 14762306a36Sopenharmony_ci if (in_buf_pages > 1) { 14862306a36Sopenharmony_ci int i; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci for (i = 0; i < in_buf_pages; i++) { 15162306a36Sopenharmony_ci if (data_in) { 15262306a36Sopenharmony_ci kunmap_local(data_in); 15362306a36Sopenharmony_ci put_page(in_page); 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci in_page = find_get_page(mapping, 15662306a36Sopenharmony_ci start >> PAGE_SHIFT); 15762306a36Sopenharmony_ci data_in = kmap_local_page(in_page); 15862306a36Sopenharmony_ci copy_page(workspace->buf + i * PAGE_SIZE, 15962306a36Sopenharmony_ci data_in); 16062306a36Sopenharmony_ci start += PAGE_SIZE; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci workspace->strm.next_in = workspace->buf; 16362306a36Sopenharmony_ci } else { 16462306a36Sopenharmony_ci if (data_in) { 16562306a36Sopenharmony_ci kunmap_local(data_in); 16662306a36Sopenharmony_ci put_page(in_page); 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci in_page = find_get_page(mapping, 16962306a36Sopenharmony_ci start >> PAGE_SHIFT); 17062306a36Sopenharmony_ci data_in = kmap_local_page(in_page); 17162306a36Sopenharmony_ci start += PAGE_SIZE; 17262306a36Sopenharmony_ci workspace->strm.next_in = data_in; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci workspace->strm.avail_in = min(bytes_left, 17562306a36Sopenharmony_ci (unsigned long) workspace->buf_size); 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ret = zlib_deflate(&workspace->strm, Z_SYNC_FLUSH); 17962306a36Sopenharmony_ci if (ret != Z_OK) { 18062306a36Sopenharmony_ci pr_debug("BTRFS: deflate in loop returned %d\n", 18162306a36Sopenharmony_ci ret); 18262306a36Sopenharmony_ci zlib_deflateEnd(&workspace->strm); 18362306a36Sopenharmony_ci ret = -EIO; 18462306a36Sopenharmony_ci goto out; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* we're making it bigger, give up */ 18862306a36Sopenharmony_ci if (workspace->strm.total_in > 8192 && 18962306a36Sopenharmony_ci workspace->strm.total_in < 19062306a36Sopenharmony_ci workspace->strm.total_out) { 19162306a36Sopenharmony_ci ret = -E2BIG; 19262306a36Sopenharmony_ci goto out; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci /* we need another page for writing out. Test this 19562306a36Sopenharmony_ci * before the total_in so we will pull in a new page for 19662306a36Sopenharmony_ci * the stream end if required 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci if (workspace->strm.avail_out == 0) { 19962306a36Sopenharmony_ci if (nr_pages == nr_dest_pages) { 20062306a36Sopenharmony_ci ret = -E2BIG; 20162306a36Sopenharmony_ci goto out; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci out_page = alloc_page(GFP_NOFS); 20462306a36Sopenharmony_ci if (out_page == NULL) { 20562306a36Sopenharmony_ci ret = -ENOMEM; 20662306a36Sopenharmony_ci goto out; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci cpage_out = page_address(out_page); 20962306a36Sopenharmony_ci pages[nr_pages] = out_page; 21062306a36Sopenharmony_ci nr_pages++; 21162306a36Sopenharmony_ci workspace->strm.avail_out = PAGE_SIZE; 21262306a36Sopenharmony_ci workspace->strm.next_out = cpage_out; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci /* we're all done */ 21562306a36Sopenharmony_ci if (workspace->strm.total_in >= len) 21662306a36Sopenharmony_ci break; 21762306a36Sopenharmony_ci if (workspace->strm.total_out > max_out) 21862306a36Sopenharmony_ci break; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci workspace->strm.avail_in = 0; 22162306a36Sopenharmony_ci /* 22262306a36Sopenharmony_ci * Call deflate with Z_FINISH flush parameter providing more output 22362306a36Sopenharmony_ci * space but no more input data, until it returns with Z_STREAM_END. 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_ci while (ret != Z_STREAM_END) { 22662306a36Sopenharmony_ci ret = zlib_deflate(&workspace->strm, Z_FINISH); 22762306a36Sopenharmony_ci if (ret == Z_STREAM_END) 22862306a36Sopenharmony_ci break; 22962306a36Sopenharmony_ci if (ret != Z_OK && ret != Z_BUF_ERROR) { 23062306a36Sopenharmony_ci zlib_deflateEnd(&workspace->strm); 23162306a36Sopenharmony_ci ret = -EIO; 23262306a36Sopenharmony_ci goto out; 23362306a36Sopenharmony_ci } else if (workspace->strm.avail_out == 0) { 23462306a36Sopenharmony_ci /* get another page for the stream end */ 23562306a36Sopenharmony_ci if (nr_pages == nr_dest_pages) { 23662306a36Sopenharmony_ci ret = -E2BIG; 23762306a36Sopenharmony_ci goto out; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci out_page = alloc_page(GFP_NOFS); 24062306a36Sopenharmony_ci if (out_page == NULL) { 24162306a36Sopenharmony_ci ret = -ENOMEM; 24262306a36Sopenharmony_ci goto out; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci cpage_out = page_address(out_page); 24562306a36Sopenharmony_ci pages[nr_pages] = out_page; 24662306a36Sopenharmony_ci nr_pages++; 24762306a36Sopenharmony_ci workspace->strm.avail_out = PAGE_SIZE; 24862306a36Sopenharmony_ci workspace->strm.next_out = cpage_out; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci zlib_deflateEnd(&workspace->strm); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (workspace->strm.total_out >= workspace->strm.total_in) { 25462306a36Sopenharmony_ci ret = -E2BIG; 25562306a36Sopenharmony_ci goto out; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci ret = 0; 25962306a36Sopenharmony_ci *total_out = workspace->strm.total_out; 26062306a36Sopenharmony_ci *total_in = workspace->strm.total_in; 26162306a36Sopenharmony_ciout: 26262306a36Sopenharmony_ci *out_pages = nr_pages; 26362306a36Sopenharmony_ci if (data_in) { 26462306a36Sopenharmony_ci kunmap_local(data_in); 26562306a36Sopenharmony_ci put_page(in_page); 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return ret; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ciint zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct workspace *workspace = list_entry(ws, struct workspace, list); 27462306a36Sopenharmony_ci int ret = 0, ret2; 27562306a36Sopenharmony_ci int wbits = MAX_WBITS; 27662306a36Sopenharmony_ci char *data_in; 27762306a36Sopenharmony_ci size_t total_out = 0; 27862306a36Sopenharmony_ci unsigned long page_in_index = 0; 27962306a36Sopenharmony_ci size_t srclen = cb->compressed_len; 28062306a36Sopenharmony_ci unsigned long total_pages_in = DIV_ROUND_UP(srclen, PAGE_SIZE); 28162306a36Sopenharmony_ci unsigned long buf_start; 28262306a36Sopenharmony_ci struct page **pages_in = cb->compressed_pages; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci data_in = kmap_local_page(pages_in[page_in_index]); 28562306a36Sopenharmony_ci workspace->strm.next_in = data_in; 28662306a36Sopenharmony_ci workspace->strm.avail_in = min_t(size_t, srclen, PAGE_SIZE); 28762306a36Sopenharmony_ci workspace->strm.total_in = 0; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci workspace->strm.total_out = 0; 29062306a36Sopenharmony_ci workspace->strm.next_out = workspace->buf; 29162306a36Sopenharmony_ci workspace->strm.avail_out = workspace->buf_size; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* If it's deflate, and it's got no preset dictionary, then 29462306a36Sopenharmony_ci we can tell zlib to skip the adler32 check. */ 29562306a36Sopenharmony_ci if (srclen > 2 && !(data_in[1] & PRESET_DICT) && 29662306a36Sopenharmony_ci ((data_in[0] & 0x0f) == Z_DEFLATED) && 29762306a36Sopenharmony_ci !(((data_in[0]<<8) + data_in[1]) % 31)) { 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci wbits = -((data_in[0] >> 4) + 8); 30062306a36Sopenharmony_ci workspace->strm.next_in += 2; 30162306a36Sopenharmony_ci workspace->strm.avail_in -= 2; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (Z_OK != zlib_inflateInit2(&workspace->strm, wbits)) { 30562306a36Sopenharmony_ci pr_warn("BTRFS: inflateInit failed\n"); 30662306a36Sopenharmony_ci kunmap_local(data_in); 30762306a36Sopenharmony_ci return -EIO; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci while (workspace->strm.total_in < srclen) { 31062306a36Sopenharmony_ci ret = zlib_inflate(&workspace->strm, Z_NO_FLUSH); 31162306a36Sopenharmony_ci if (ret != Z_OK && ret != Z_STREAM_END) 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci buf_start = total_out; 31562306a36Sopenharmony_ci total_out = workspace->strm.total_out; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* we didn't make progress in this inflate call, we're done */ 31862306a36Sopenharmony_ci if (buf_start == total_out) 31962306a36Sopenharmony_ci break; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci ret2 = btrfs_decompress_buf2page(workspace->buf, 32262306a36Sopenharmony_ci total_out - buf_start, cb, buf_start); 32362306a36Sopenharmony_ci if (ret2 == 0) { 32462306a36Sopenharmony_ci ret = 0; 32562306a36Sopenharmony_ci goto done; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci workspace->strm.next_out = workspace->buf; 32962306a36Sopenharmony_ci workspace->strm.avail_out = workspace->buf_size; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (workspace->strm.avail_in == 0) { 33262306a36Sopenharmony_ci unsigned long tmp; 33362306a36Sopenharmony_ci kunmap_local(data_in); 33462306a36Sopenharmony_ci page_in_index++; 33562306a36Sopenharmony_ci if (page_in_index >= total_pages_in) { 33662306a36Sopenharmony_ci data_in = NULL; 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci data_in = kmap_local_page(pages_in[page_in_index]); 34062306a36Sopenharmony_ci workspace->strm.next_in = data_in; 34162306a36Sopenharmony_ci tmp = srclen - workspace->strm.total_in; 34262306a36Sopenharmony_ci workspace->strm.avail_in = min(tmp, PAGE_SIZE); 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci if (ret != Z_STREAM_END) 34662306a36Sopenharmony_ci ret = -EIO; 34762306a36Sopenharmony_ci else 34862306a36Sopenharmony_ci ret = 0; 34962306a36Sopenharmony_cidone: 35062306a36Sopenharmony_ci zlib_inflateEnd(&workspace->strm); 35162306a36Sopenharmony_ci if (data_in) 35262306a36Sopenharmony_ci kunmap_local(data_in); 35362306a36Sopenharmony_ci return ret; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ciint zlib_decompress(struct list_head *ws, const u8 *data_in, 35762306a36Sopenharmony_ci struct page *dest_page, unsigned long start_byte, size_t srclen, 35862306a36Sopenharmony_ci size_t destlen) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct workspace *workspace = list_entry(ws, struct workspace, list); 36162306a36Sopenharmony_ci int ret = 0; 36262306a36Sopenharmony_ci int wbits = MAX_WBITS; 36362306a36Sopenharmony_ci unsigned long bytes_left; 36462306a36Sopenharmony_ci unsigned long total_out = 0; 36562306a36Sopenharmony_ci unsigned long pg_offset = 0; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci destlen = min_t(unsigned long, destlen, PAGE_SIZE); 36862306a36Sopenharmony_ci bytes_left = destlen; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci workspace->strm.next_in = data_in; 37162306a36Sopenharmony_ci workspace->strm.avail_in = srclen; 37262306a36Sopenharmony_ci workspace->strm.total_in = 0; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci workspace->strm.next_out = workspace->buf; 37562306a36Sopenharmony_ci workspace->strm.avail_out = workspace->buf_size; 37662306a36Sopenharmony_ci workspace->strm.total_out = 0; 37762306a36Sopenharmony_ci /* If it's deflate, and it's got no preset dictionary, then 37862306a36Sopenharmony_ci we can tell zlib to skip the adler32 check. */ 37962306a36Sopenharmony_ci if (srclen > 2 && !(data_in[1] & PRESET_DICT) && 38062306a36Sopenharmony_ci ((data_in[0] & 0x0f) == Z_DEFLATED) && 38162306a36Sopenharmony_ci !(((data_in[0]<<8) + data_in[1]) % 31)) { 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci wbits = -((data_in[0] >> 4) + 8); 38462306a36Sopenharmony_ci workspace->strm.next_in += 2; 38562306a36Sopenharmony_ci workspace->strm.avail_in -= 2; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (Z_OK != zlib_inflateInit2(&workspace->strm, wbits)) { 38962306a36Sopenharmony_ci pr_warn("BTRFS: inflateInit failed\n"); 39062306a36Sopenharmony_ci return -EIO; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci while (bytes_left > 0) { 39462306a36Sopenharmony_ci unsigned long buf_start; 39562306a36Sopenharmony_ci unsigned long buf_offset; 39662306a36Sopenharmony_ci unsigned long bytes; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci ret = zlib_inflate(&workspace->strm, Z_NO_FLUSH); 39962306a36Sopenharmony_ci if (ret != Z_OK && ret != Z_STREAM_END) 40062306a36Sopenharmony_ci break; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci buf_start = total_out; 40362306a36Sopenharmony_ci total_out = workspace->strm.total_out; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (total_out == buf_start) { 40662306a36Sopenharmony_ci ret = -EIO; 40762306a36Sopenharmony_ci break; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (total_out <= start_byte) 41162306a36Sopenharmony_ci goto next; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (total_out > start_byte && buf_start < start_byte) 41462306a36Sopenharmony_ci buf_offset = start_byte - buf_start; 41562306a36Sopenharmony_ci else 41662306a36Sopenharmony_ci buf_offset = 0; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci bytes = min(PAGE_SIZE - pg_offset, 41962306a36Sopenharmony_ci PAGE_SIZE - (buf_offset % PAGE_SIZE)); 42062306a36Sopenharmony_ci bytes = min(bytes, bytes_left); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci memcpy_to_page(dest_page, pg_offset, 42362306a36Sopenharmony_ci workspace->buf + buf_offset, bytes); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci pg_offset += bytes; 42662306a36Sopenharmony_ci bytes_left -= bytes; 42762306a36Sopenharmony_cinext: 42862306a36Sopenharmony_ci workspace->strm.next_out = workspace->buf; 42962306a36Sopenharmony_ci workspace->strm.avail_out = workspace->buf_size; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (ret != Z_STREAM_END && bytes_left != 0) 43362306a36Sopenharmony_ci ret = -EIO; 43462306a36Sopenharmony_ci else 43562306a36Sopenharmony_ci ret = 0; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci zlib_inflateEnd(&workspace->strm); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* 44062306a36Sopenharmony_ci * this should only happen if zlib returned fewer bytes than we 44162306a36Sopenharmony_ci * expected. btrfs_get_block is responsible for zeroing from the 44262306a36Sopenharmony_ci * end of the inline extent (destlen) to the end of the page 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_ci if (pg_offset < destlen) { 44562306a36Sopenharmony_ci memzero_page(dest_page, pg_offset, destlen - pg_offset); 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci return ret; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ciconst struct btrfs_compress_op btrfs_zlib_compress = { 45162306a36Sopenharmony_ci .workspace_manager = &wsm, 45262306a36Sopenharmony_ci .max_level = 9, 45362306a36Sopenharmony_ci .default_level = BTRFS_ZLIB_DEFAULT_LEVEL, 45462306a36Sopenharmony_ci}; 455