18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2008 Oracle. All rights reserved. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Based on jffs2 zlib code: 68c2ecf20Sopenharmony_ci * Copyright © 2001-2007 Red Hat, Inc. 78c2ecf20Sopenharmony_ci * Created by David Woodhouse <dwmw2@infradead.org> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/zlib.h> 138c2ecf20Sopenharmony_ci#include <linux/zutil.h> 148c2ecf20Sopenharmony_ci#include <linux/mm.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/sched.h> 188c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 198c2ecf20Sopenharmony_ci#include <linux/bio.h> 208c2ecf20Sopenharmony_ci#include <linux/refcount.h> 218c2ecf20Sopenharmony_ci#include "compression.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* workspace buffer size for s390 zlib hardware support */ 248c2ecf20Sopenharmony_ci#define ZLIB_DFLTCC_BUF_SIZE (4 * PAGE_SIZE) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct workspace { 278c2ecf20Sopenharmony_ci z_stream strm; 288c2ecf20Sopenharmony_ci char *buf; 298c2ecf20Sopenharmony_ci unsigned int buf_size; 308c2ecf20Sopenharmony_ci struct list_head list; 318c2ecf20Sopenharmony_ci int level; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic struct workspace_manager wsm; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct list_head *zlib_get_workspace(unsigned int level) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci struct list_head *ws = btrfs_get_workspace(BTRFS_COMPRESS_ZLIB, level); 398c2ecf20Sopenharmony_ci struct workspace *workspace = list_entry(ws, struct workspace, list); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci workspace->level = level; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci return ws; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_civoid zlib_free_workspace(struct list_head *ws) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct workspace *workspace = list_entry(ws, struct workspace, list); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci kvfree(workspace->strm.workspace); 518c2ecf20Sopenharmony_ci kfree(workspace->buf); 528c2ecf20Sopenharmony_ci kfree(workspace); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct list_head *zlib_alloc_workspace(unsigned int level) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct workspace *workspace; 588c2ecf20Sopenharmony_ci int workspacesize; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci workspace = kzalloc(sizeof(*workspace), GFP_KERNEL); 618c2ecf20Sopenharmony_ci if (!workspace) 628c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci workspacesize = max(zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL), 658c2ecf20Sopenharmony_ci zlib_inflate_workspacesize()); 668c2ecf20Sopenharmony_ci workspace->strm.workspace = kvzalloc(workspacesize, GFP_KERNEL); 678c2ecf20Sopenharmony_ci workspace->level = level; 688c2ecf20Sopenharmony_ci workspace->buf = NULL; 698c2ecf20Sopenharmony_ci /* 708c2ecf20Sopenharmony_ci * In case of s390 zlib hardware support, allocate lager workspace 718c2ecf20Sopenharmony_ci * buffer. If allocator fails, fall back to a single page buffer. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci if (zlib_deflate_dfltcc_enabled()) { 748c2ecf20Sopenharmony_ci workspace->buf = kmalloc(ZLIB_DFLTCC_BUF_SIZE, 758c2ecf20Sopenharmony_ci __GFP_NOMEMALLOC | __GFP_NORETRY | 768c2ecf20Sopenharmony_ci __GFP_NOWARN | GFP_NOIO); 778c2ecf20Sopenharmony_ci workspace->buf_size = ZLIB_DFLTCC_BUF_SIZE; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci if (!workspace->buf) { 808c2ecf20Sopenharmony_ci workspace->buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 818c2ecf20Sopenharmony_ci workspace->buf_size = PAGE_SIZE; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci if (!workspace->strm.workspace || !workspace->buf) 848c2ecf20Sopenharmony_ci goto fail; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&workspace->list); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return &workspace->list; 898c2ecf20Sopenharmony_cifail: 908c2ecf20Sopenharmony_ci zlib_free_workspace(&workspace->list); 918c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ciint zlib_compress_pages(struct list_head *ws, struct address_space *mapping, 958c2ecf20Sopenharmony_ci u64 start, struct page **pages, unsigned long *out_pages, 968c2ecf20Sopenharmony_ci unsigned long *total_in, unsigned long *total_out) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct workspace *workspace = list_entry(ws, struct workspace, list); 998c2ecf20Sopenharmony_ci int ret; 1008c2ecf20Sopenharmony_ci char *data_in; 1018c2ecf20Sopenharmony_ci char *cpage_out; 1028c2ecf20Sopenharmony_ci int nr_pages = 0; 1038c2ecf20Sopenharmony_ci struct page *in_page = NULL; 1048c2ecf20Sopenharmony_ci struct page *out_page = NULL; 1058c2ecf20Sopenharmony_ci unsigned long bytes_left; 1068c2ecf20Sopenharmony_ci unsigned int in_buf_pages; 1078c2ecf20Sopenharmony_ci unsigned long len = *total_out; 1088c2ecf20Sopenharmony_ci unsigned long nr_dest_pages = *out_pages; 1098c2ecf20Sopenharmony_ci const unsigned long max_out = nr_dest_pages * PAGE_SIZE; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci *out_pages = 0; 1128c2ecf20Sopenharmony_ci *total_out = 0; 1138c2ecf20Sopenharmony_ci *total_in = 0; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (Z_OK != zlib_deflateInit(&workspace->strm, workspace->level)) { 1168c2ecf20Sopenharmony_ci pr_warn("BTRFS: deflateInit failed\n"); 1178c2ecf20Sopenharmony_ci ret = -EIO; 1188c2ecf20Sopenharmony_ci goto out; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci workspace->strm.total_in = 0; 1228c2ecf20Sopenharmony_ci workspace->strm.total_out = 0; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); 1258c2ecf20Sopenharmony_ci if (out_page == NULL) { 1268c2ecf20Sopenharmony_ci ret = -ENOMEM; 1278c2ecf20Sopenharmony_ci goto out; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci cpage_out = kmap(out_page); 1308c2ecf20Sopenharmony_ci pages[0] = out_page; 1318c2ecf20Sopenharmony_ci nr_pages = 1; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci workspace->strm.next_in = workspace->buf; 1348c2ecf20Sopenharmony_ci workspace->strm.avail_in = 0; 1358c2ecf20Sopenharmony_ci workspace->strm.next_out = cpage_out; 1368c2ecf20Sopenharmony_ci workspace->strm.avail_out = PAGE_SIZE; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci while (workspace->strm.total_in < len) { 1398c2ecf20Sopenharmony_ci /* 1408c2ecf20Sopenharmony_ci * Get next input pages and copy the contents to 1418c2ecf20Sopenharmony_ci * the workspace buffer if required. 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_ci if (workspace->strm.avail_in == 0) { 1448c2ecf20Sopenharmony_ci bytes_left = len - workspace->strm.total_in; 1458c2ecf20Sopenharmony_ci in_buf_pages = min(DIV_ROUND_UP(bytes_left, PAGE_SIZE), 1468c2ecf20Sopenharmony_ci workspace->buf_size / PAGE_SIZE); 1478c2ecf20Sopenharmony_ci if (in_buf_pages > 1) { 1488c2ecf20Sopenharmony_ci int i; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci for (i = 0; i < in_buf_pages; i++) { 1518c2ecf20Sopenharmony_ci if (in_page) { 1528c2ecf20Sopenharmony_ci kunmap(in_page); 1538c2ecf20Sopenharmony_ci put_page(in_page); 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci in_page = find_get_page(mapping, 1568c2ecf20Sopenharmony_ci start >> PAGE_SHIFT); 1578c2ecf20Sopenharmony_ci data_in = kmap(in_page); 1588c2ecf20Sopenharmony_ci memcpy(workspace->buf + i * PAGE_SIZE, 1598c2ecf20Sopenharmony_ci data_in, PAGE_SIZE); 1608c2ecf20Sopenharmony_ci start += PAGE_SIZE; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci workspace->strm.next_in = workspace->buf; 1638c2ecf20Sopenharmony_ci } else { 1648c2ecf20Sopenharmony_ci if (in_page) { 1658c2ecf20Sopenharmony_ci kunmap(in_page); 1668c2ecf20Sopenharmony_ci put_page(in_page); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci in_page = find_get_page(mapping, 1698c2ecf20Sopenharmony_ci start >> PAGE_SHIFT); 1708c2ecf20Sopenharmony_ci data_in = kmap(in_page); 1718c2ecf20Sopenharmony_ci start += PAGE_SIZE; 1728c2ecf20Sopenharmony_ci workspace->strm.next_in = data_in; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci workspace->strm.avail_in = min(bytes_left, 1758c2ecf20Sopenharmony_ci (unsigned long) workspace->buf_size); 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci ret = zlib_deflate(&workspace->strm, Z_SYNC_FLUSH); 1798c2ecf20Sopenharmony_ci if (ret != Z_OK) { 1808c2ecf20Sopenharmony_ci pr_debug("BTRFS: deflate in loop returned %d\n", 1818c2ecf20Sopenharmony_ci ret); 1828c2ecf20Sopenharmony_ci zlib_deflateEnd(&workspace->strm); 1838c2ecf20Sopenharmony_ci ret = -EIO; 1848c2ecf20Sopenharmony_ci goto out; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* we're making it bigger, give up */ 1888c2ecf20Sopenharmony_ci if (workspace->strm.total_in > 8192 && 1898c2ecf20Sopenharmony_ci workspace->strm.total_in < 1908c2ecf20Sopenharmony_ci workspace->strm.total_out) { 1918c2ecf20Sopenharmony_ci ret = -E2BIG; 1928c2ecf20Sopenharmony_ci goto out; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci /* we need another page for writing out. Test this 1958c2ecf20Sopenharmony_ci * before the total_in so we will pull in a new page for 1968c2ecf20Sopenharmony_ci * the stream end if required 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_ci if (workspace->strm.avail_out == 0) { 1998c2ecf20Sopenharmony_ci kunmap(out_page); 2008c2ecf20Sopenharmony_ci if (nr_pages == nr_dest_pages) { 2018c2ecf20Sopenharmony_ci out_page = NULL; 2028c2ecf20Sopenharmony_ci ret = -E2BIG; 2038c2ecf20Sopenharmony_ci goto out; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); 2068c2ecf20Sopenharmony_ci if (out_page == NULL) { 2078c2ecf20Sopenharmony_ci ret = -ENOMEM; 2088c2ecf20Sopenharmony_ci goto out; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci cpage_out = kmap(out_page); 2118c2ecf20Sopenharmony_ci pages[nr_pages] = out_page; 2128c2ecf20Sopenharmony_ci nr_pages++; 2138c2ecf20Sopenharmony_ci workspace->strm.avail_out = PAGE_SIZE; 2148c2ecf20Sopenharmony_ci workspace->strm.next_out = cpage_out; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci /* we're all done */ 2178c2ecf20Sopenharmony_ci if (workspace->strm.total_in >= len) 2188c2ecf20Sopenharmony_ci break; 2198c2ecf20Sopenharmony_ci if (workspace->strm.total_out > max_out) 2208c2ecf20Sopenharmony_ci break; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci workspace->strm.avail_in = 0; 2238c2ecf20Sopenharmony_ci /* 2248c2ecf20Sopenharmony_ci * Call deflate with Z_FINISH flush parameter providing more output 2258c2ecf20Sopenharmony_ci * space but no more input data, until it returns with Z_STREAM_END. 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_ci while (ret != Z_STREAM_END) { 2288c2ecf20Sopenharmony_ci ret = zlib_deflate(&workspace->strm, Z_FINISH); 2298c2ecf20Sopenharmony_ci if (ret == Z_STREAM_END) 2308c2ecf20Sopenharmony_ci break; 2318c2ecf20Sopenharmony_ci if (ret != Z_OK && ret != Z_BUF_ERROR) { 2328c2ecf20Sopenharmony_ci zlib_deflateEnd(&workspace->strm); 2338c2ecf20Sopenharmony_ci ret = -EIO; 2348c2ecf20Sopenharmony_ci goto out; 2358c2ecf20Sopenharmony_ci } else if (workspace->strm.avail_out == 0) { 2368c2ecf20Sopenharmony_ci /* get another page for the stream end */ 2378c2ecf20Sopenharmony_ci kunmap(out_page); 2388c2ecf20Sopenharmony_ci if (nr_pages == nr_dest_pages) { 2398c2ecf20Sopenharmony_ci out_page = NULL; 2408c2ecf20Sopenharmony_ci ret = -E2BIG; 2418c2ecf20Sopenharmony_ci goto out; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); 2448c2ecf20Sopenharmony_ci if (out_page == NULL) { 2458c2ecf20Sopenharmony_ci ret = -ENOMEM; 2468c2ecf20Sopenharmony_ci goto out; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci cpage_out = kmap(out_page); 2498c2ecf20Sopenharmony_ci pages[nr_pages] = out_page; 2508c2ecf20Sopenharmony_ci nr_pages++; 2518c2ecf20Sopenharmony_ci workspace->strm.avail_out = PAGE_SIZE; 2528c2ecf20Sopenharmony_ci workspace->strm.next_out = cpage_out; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci zlib_deflateEnd(&workspace->strm); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (workspace->strm.total_out >= workspace->strm.total_in) { 2588c2ecf20Sopenharmony_ci ret = -E2BIG; 2598c2ecf20Sopenharmony_ci goto out; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci ret = 0; 2638c2ecf20Sopenharmony_ci *total_out = workspace->strm.total_out; 2648c2ecf20Sopenharmony_ci *total_in = workspace->strm.total_in; 2658c2ecf20Sopenharmony_ciout: 2668c2ecf20Sopenharmony_ci *out_pages = nr_pages; 2678c2ecf20Sopenharmony_ci if (out_page) 2688c2ecf20Sopenharmony_ci kunmap(out_page); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (in_page) { 2718c2ecf20Sopenharmony_ci kunmap(in_page); 2728c2ecf20Sopenharmony_ci put_page(in_page); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci return ret; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ciint zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct workspace *workspace = list_entry(ws, struct workspace, list); 2808c2ecf20Sopenharmony_ci int ret = 0, ret2; 2818c2ecf20Sopenharmony_ci int wbits = MAX_WBITS; 2828c2ecf20Sopenharmony_ci char *data_in; 2838c2ecf20Sopenharmony_ci size_t total_out = 0; 2848c2ecf20Sopenharmony_ci unsigned long page_in_index = 0; 2858c2ecf20Sopenharmony_ci size_t srclen = cb->compressed_len; 2868c2ecf20Sopenharmony_ci unsigned long total_pages_in = DIV_ROUND_UP(srclen, PAGE_SIZE); 2878c2ecf20Sopenharmony_ci unsigned long buf_start; 2888c2ecf20Sopenharmony_ci struct page **pages_in = cb->compressed_pages; 2898c2ecf20Sopenharmony_ci u64 disk_start = cb->start; 2908c2ecf20Sopenharmony_ci struct bio *orig_bio = cb->orig_bio; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci data_in = kmap(pages_in[page_in_index]); 2938c2ecf20Sopenharmony_ci workspace->strm.next_in = data_in; 2948c2ecf20Sopenharmony_ci workspace->strm.avail_in = min_t(size_t, srclen, PAGE_SIZE); 2958c2ecf20Sopenharmony_ci workspace->strm.total_in = 0; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci workspace->strm.total_out = 0; 2988c2ecf20Sopenharmony_ci workspace->strm.next_out = workspace->buf; 2998c2ecf20Sopenharmony_ci workspace->strm.avail_out = workspace->buf_size; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* If it's deflate, and it's got no preset dictionary, then 3028c2ecf20Sopenharmony_ci we can tell zlib to skip the adler32 check. */ 3038c2ecf20Sopenharmony_ci if (srclen > 2 && !(data_in[1] & PRESET_DICT) && 3048c2ecf20Sopenharmony_ci ((data_in[0] & 0x0f) == Z_DEFLATED) && 3058c2ecf20Sopenharmony_ci !(((data_in[0]<<8) + data_in[1]) % 31)) { 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci wbits = -((data_in[0] >> 4) + 8); 3088c2ecf20Sopenharmony_ci workspace->strm.next_in += 2; 3098c2ecf20Sopenharmony_ci workspace->strm.avail_in -= 2; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (Z_OK != zlib_inflateInit2(&workspace->strm, wbits)) { 3138c2ecf20Sopenharmony_ci pr_warn("BTRFS: inflateInit failed\n"); 3148c2ecf20Sopenharmony_ci kunmap(pages_in[page_in_index]); 3158c2ecf20Sopenharmony_ci return -EIO; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci while (workspace->strm.total_in < srclen) { 3188c2ecf20Sopenharmony_ci ret = zlib_inflate(&workspace->strm, Z_NO_FLUSH); 3198c2ecf20Sopenharmony_ci if (ret != Z_OK && ret != Z_STREAM_END) 3208c2ecf20Sopenharmony_ci break; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci buf_start = total_out; 3238c2ecf20Sopenharmony_ci total_out = workspace->strm.total_out; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* we didn't make progress in this inflate call, we're done */ 3268c2ecf20Sopenharmony_ci if (buf_start == total_out) 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci ret2 = btrfs_decompress_buf2page(workspace->buf, buf_start, 3308c2ecf20Sopenharmony_ci total_out, disk_start, 3318c2ecf20Sopenharmony_ci orig_bio); 3328c2ecf20Sopenharmony_ci if (ret2 == 0) { 3338c2ecf20Sopenharmony_ci ret = 0; 3348c2ecf20Sopenharmony_ci goto done; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci workspace->strm.next_out = workspace->buf; 3388c2ecf20Sopenharmony_ci workspace->strm.avail_out = workspace->buf_size; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (workspace->strm.avail_in == 0) { 3418c2ecf20Sopenharmony_ci unsigned long tmp; 3428c2ecf20Sopenharmony_ci kunmap(pages_in[page_in_index]); 3438c2ecf20Sopenharmony_ci page_in_index++; 3448c2ecf20Sopenharmony_ci if (page_in_index >= total_pages_in) { 3458c2ecf20Sopenharmony_ci data_in = NULL; 3468c2ecf20Sopenharmony_ci break; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci data_in = kmap(pages_in[page_in_index]); 3498c2ecf20Sopenharmony_ci workspace->strm.next_in = data_in; 3508c2ecf20Sopenharmony_ci tmp = srclen - workspace->strm.total_in; 3518c2ecf20Sopenharmony_ci workspace->strm.avail_in = min(tmp, 3528c2ecf20Sopenharmony_ci PAGE_SIZE); 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci if (ret != Z_STREAM_END) 3568c2ecf20Sopenharmony_ci ret = -EIO; 3578c2ecf20Sopenharmony_ci else 3588c2ecf20Sopenharmony_ci ret = 0; 3598c2ecf20Sopenharmony_cidone: 3608c2ecf20Sopenharmony_ci zlib_inflateEnd(&workspace->strm); 3618c2ecf20Sopenharmony_ci if (data_in) 3628c2ecf20Sopenharmony_ci kunmap(pages_in[page_in_index]); 3638c2ecf20Sopenharmony_ci if (!ret) 3648c2ecf20Sopenharmony_ci zero_fill_bio(orig_bio); 3658c2ecf20Sopenharmony_ci return ret; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ciint zlib_decompress(struct list_head *ws, unsigned char *data_in, 3698c2ecf20Sopenharmony_ci struct page *dest_page, unsigned long start_byte, size_t srclen, 3708c2ecf20Sopenharmony_ci size_t destlen) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct workspace *workspace = list_entry(ws, struct workspace, list); 3738c2ecf20Sopenharmony_ci int ret = 0; 3748c2ecf20Sopenharmony_ci int wbits = MAX_WBITS; 3758c2ecf20Sopenharmony_ci unsigned long bytes_left; 3768c2ecf20Sopenharmony_ci unsigned long total_out = 0; 3778c2ecf20Sopenharmony_ci unsigned long pg_offset = 0; 3788c2ecf20Sopenharmony_ci char *kaddr; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci destlen = min_t(unsigned long, destlen, PAGE_SIZE); 3818c2ecf20Sopenharmony_ci bytes_left = destlen; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci workspace->strm.next_in = data_in; 3848c2ecf20Sopenharmony_ci workspace->strm.avail_in = srclen; 3858c2ecf20Sopenharmony_ci workspace->strm.total_in = 0; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci workspace->strm.next_out = workspace->buf; 3888c2ecf20Sopenharmony_ci workspace->strm.avail_out = workspace->buf_size; 3898c2ecf20Sopenharmony_ci workspace->strm.total_out = 0; 3908c2ecf20Sopenharmony_ci /* If it's deflate, and it's got no preset dictionary, then 3918c2ecf20Sopenharmony_ci we can tell zlib to skip the adler32 check. */ 3928c2ecf20Sopenharmony_ci if (srclen > 2 && !(data_in[1] & PRESET_DICT) && 3938c2ecf20Sopenharmony_ci ((data_in[0] & 0x0f) == Z_DEFLATED) && 3948c2ecf20Sopenharmony_ci !(((data_in[0]<<8) + data_in[1]) % 31)) { 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci wbits = -((data_in[0] >> 4) + 8); 3978c2ecf20Sopenharmony_ci workspace->strm.next_in += 2; 3988c2ecf20Sopenharmony_ci workspace->strm.avail_in -= 2; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (Z_OK != zlib_inflateInit2(&workspace->strm, wbits)) { 4028c2ecf20Sopenharmony_ci pr_warn("BTRFS: inflateInit failed\n"); 4038c2ecf20Sopenharmony_ci return -EIO; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci while (bytes_left > 0) { 4078c2ecf20Sopenharmony_ci unsigned long buf_start; 4088c2ecf20Sopenharmony_ci unsigned long buf_offset; 4098c2ecf20Sopenharmony_ci unsigned long bytes; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci ret = zlib_inflate(&workspace->strm, Z_NO_FLUSH); 4128c2ecf20Sopenharmony_ci if (ret != Z_OK && ret != Z_STREAM_END) 4138c2ecf20Sopenharmony_ci break; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci buf_start = total_out; 4168c2ecf20Sopenharmony_ci total_out = workspace->strm.total_out; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (total_out == buf_start) { 4198c2ecf20Sopenharmony_ci ret = -EIO; 4208c2ecf20Sopenharmony_ci break; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (total_out <= start_byte) 4248c2ecf20Sopenharmony_ci goto next; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (total_out > start_byte && buf_start < start_byte) 4278c2ecf20Sopenharmony_ci buf_offset = start_byte - buf_start; 4288c2ecf20Sopenharmony_ci else 4298c2ecf20Sopenharmony_ci buf_offset = 0; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci bytes = min(PAGE_SIZE - pg_offset, 4328c2ecf20Sopenharmony_ci PAGE_SIZE - (buf_offset % PAGE_SIZE)); 4338c2ecf20Sopenharmony_ci bytes = min(bytes, bytes_left); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci kaddr = kmap_atomic(dest_page); 4368c2ecf20Sopenharmony_ci memcpy(kaddr + pg_offset, workspace->buf + buf_offset, bytes); 4378c2ecf20Sopenharmony_ci kunmap_atomic(kaddr); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci pg_offset += bytes; 4408c2ecf20Sopenharmony_ci bytes_left -= bytes; 4418c2ecf20Sopenharmony_cinext: 4428c2ecf20Sopenharmony_ci workspace->strm.next_out = workspace->buf; 4438c2ecf20Sopenharmony_ci workspace->strm.avail_out = workspace->buf_size; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (ret != Z_STREAM_END && bytes_left != 0) 4478c2ecf20Sopenharmony_ci ret = -EIO; 4488c2ecf20Sopenharmony_ci else 4498c2ecf20Sopenharmony_ci ret = 0; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci zlib_inflateEnd(&workspace->strm); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* 4548c2ecf20Sopenharmony_ci * this should only happen if zlib returned fewer bytes than we 4558c2ecf20Sopenharmony_ci * expected. btrfs_get_block is responsible for zeroing from the 4568c2ecf20Sopenharmony_ci * end of the inline extent (destlen) to the end of the page 4578c2ecf20Sopenharmony_ci */ 4588c2ecf20Sopenharmony_ci if (pg_offset < destlen) { 4598c2ecf20Sopenharmony_ci kaddr = kmap_atomic(dest_page); 4608c2ecf20Sopenharmony_ci memset(kaddr + pg_offset, 0, destlen - pg_offset); 4618c2ecf20Sopenharmony_ci kunmap_atomic(kaddr); 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci return ret; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ciconst struct btrfs_compress_op btrfs_zlib_compress = { 4678c2ecf20Sopenharmony_ci .workspace_manager = &wsm, 4688c2ecf20Sopenharmony_ci .max_level = 9, 4698c2ecf20Sopenharmony_ci .default_level = BTRFS_ZLIB_DEFAULT_LEVEL, 4708c2ecf20Sopenharmony_ci}; 471