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