xref: /openbmc/linux/fs/btrfs/zlib.c (revision 8ab546bb)
1c1d7c514SDavid Sterba // SPDX-License-Identifier: GPL-2.0
2c8b97818SChris Mason /*
3c8b97818SChris Mason  * Copyright (C) 2008 Oracle.  All rights reserved.
4c8b97818SChris Mason  *
5c8b97818SChris Mason  * Based on jffs2 zlib code:
6c8b97818SChris Mason  * Copyright © 2001-2007 Red Hat, Inc.
7c8b97818SChris Mason  * Created by David Woodhouse <dwmw2@infradead.org>
8c8b97818SChris Mason  */
9c8b97818SChris Mason 
10c8b97818SChris Mason #include <linux/kernel.h>
11c8b97818SChris Mason #include <linux/slab.h>
12c8b97818SChris Mason #include <linux/zlib.h>
13c8b97818SChris Mason #include <linux/zutil.h>
146acafd1eSDavid Sterba #include <linux/mm.h>
15c8b97818SChris Mason #include <linux/init.h>
16c8b97818SChris Mason #include <linux/err.h>
17c8b97818SChris Mason #include <linux/sched.h>
18c8b97818SChris Mason #include <linux/pagemap.h>
19c8b97818SChris Mason #include <linux/bio.h>
20e1ddce71SAnand Jain #include <linux/refcount.h>
21b2950863SChristoph Hellwig #include "compression.h"
22c8b97818SChris Mason 
233fd396afSMikhail Zaslonko /* workspace buffer size for s390 zlib hardware support */
243fd396afSMikhail Zaslonko #define ZLIB_DFLTCC_BUF_SIZE    (4 * PAGE_SIZE)
253fd396afSMikhail Zaslonko 
26c8b97818SChris Mason struct workspace {
2778809913SSergey Senozhatsky 	z_stream strm;
28c8b97818SChris Mason 	char *buf;
293fd396afSMikhail Zaslonko 	unsigned int buf_size;
30c8b97818SChris Mason 	struct list_head list;
31f51d2b59SDavid Sterba 	int level;
32c8b97818SChris Mason };
33c8b97818SChris Mason 
3492ee5530SDennis Zhou static struct workspace_manager wsm;
3592ee5530SDennis Zhou 
zlib_get_workspace(unsigned int level)36d20f395fSDavid Sterba struct list_head *zlib_get_workspace(unsigned int level)
3792ee5530SDennis Zhou {
385907a9bbSDavid Sterba 	struct list_head *ws = btrfs_get_workspace(BTRFS_COMPRESS_ZLIB, level);
39d0ab62ceSDennis Zhou 	struct workspace *workspace = list_entry(ws, struct workspace, list);
40d0ab62ceSDennis Zhou 
41d0ab62ceSDennis Zhou 	workspace->level = level;
42d0ab62ceSDennis Zhou 
43d0ab62ceSDennis Zhou 	return ws;
4492ee5530SDennis Zhou }
4592ee5530SDennis Zhou 
zlib_free_workspace(struct list_head * ws)46d20f395fSDavid Sterba void zlib_free_workspace(struct list_head *ws)
47261507a0SLi Zefan {
48261507a0SLi Zefan 	struct workspace *workspace = list_entry(ws, struct workspace, list);
49c8b97818SChris Mason 
506acafd1eSDavid Sterba 	kvfree(workspace->strm.workspace);
51261507a0SLi Zefan 	kfree(workspace->buf);
52261507a0SLi Zefan 	kfree(workspace);
53261507a0SLi Zefan }
54261507a0SLi Zefan 
zlib_alloc_workspace(unsigned int level)55d20f395fSDavid Sterba struct list_head *zlib_alloc_workspace(unsigned int level)
56c8b97818SChris Mason {
57c8b97818SChris Mason 	struct workspace *workspace;
5878809913SSergey Senozhatsky 	int workspacesize;
598844355dSLi Zefan 
60389a6cfcSDavid Sterba 	workspace = kzalloc(sizeof(*workspace), GFP_KERNEL);
61261507a0SLi Zefan 	if (!workspace)
62261507a0SLi Zefan 		return ERR_PTR(-ENOMEM);
63c8b97818SChris Mason 
6478809913SSergey Senozhatsky 	workspacesize = max(zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL),
6578809913SSergey Senozhatsky 			zlib_inflate_workspacesize());
66*8ab546bbSDavid Sterba 	workspace->strm.workspace = kvzalloc(workspacesize, GFP_KERNEL | __GFP_NOWARN);
677bf49943SDennis Zhou 	workspace->level = level;
683fd396afSMikhail Zaslonko 	workspace->buf = NULL;
693fd396afSMikhail Zaslonko 	/*
703fd396afSMikhail Zaslonko 	 * In case of s390 zlib hardware support, allocate lager workspace
713fd396afSMikhail Zaslonko 	 * buffer. If allocator fails, fall back to a single page buffer.
723fd396afSMikhail Zaslonko 	 */
733fd396afSMikhail Zaslonko 	if (zlib_deflate_dfltcc_enabled()) {
743fd396afSMikhail Zaslonko 		workspace->buf = kmalloc(ZLIB_DFLTCC_BUF_SIZE,
753fd396afSMikhail Zaslonko 					 __GFP_NOMEMALLOC | __GFP_NORETRY |
763fd396afSMikhail Zaslonko 					 __GFP_NOWARN | GFP_NOIO);
773fd396afSMikhail Zaslonko 		workspace->buf_size = ZLIB_DFLTCC_BUF_SIZE;
783fd396afSMikhail Zaslonko 	}
793fd396afSMikhail Zaslonko 	if (!workspace->buf) {
80389a6cfcSDavid Sterba 		workspace->buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
813fd396afSMikhail Zaslonko 		workspace->buf_size = PAGE_SIZE;
823fd396afSMikhail Zaslonko 	}
8378809913SSergey Senozhatsky 	if (!workspace->strm.workspace || !workspace->buf)
84261507a0SLi Zefan 		goto fail;
85c8b97818SChris Mason 
86261507a0SLi Zefan 	INIT_LIST_HEAD(&workspace->list);
87261507a0SLi Zefan 
88261507a0SLi Zefan 	return &workspace->list;
89c8b97818SChris Mason fail:
90261507a0SLi Zefan 	zlib_free_workspace(&workspace->list);
91261507a0SLi Zefan 	return ERR_PTR(-ENOMEM);
92c8b97818SChris Mason }
93c8b97818SChris Mason 
zlib_compress_pages(struct list_head * ws,struct address_space * mapping,u64 start,struct page ** pages,unsigned long * out_pages,unsigned long * total_in,unsigned long * total_out)94c4bf665aSDavid Sterba int zlib_compress_pages(struct list_head *ws, struct address_space *mapping,
95c4bf665aSDavid Sterba 		u64 start, struct page **pages, unsigned long *out_pages,
96c4bf665aSDavid Sterba 		unsigned long *total_in, unsigned long *total_out)
97c8b97818SChris Mason {
98261507a0SLi Zefan 	struct workspace *workspace = list_entry(ws, struct workspace, list);
99c8b97818SChris Mason 	int ret;
100718e5855SFabio M. De Francesco 	char *data_in = NULL;
101c8b97818SChris Mason 	char *cpage_out;
102c8b97818SChris Mason 	int nr_pages = 0;
103c8b97818SChris Mason 	struct page *in_page = NULL;
104c8b97818SChris Mason 	struct page *out_page = NULL;
105c8b97818SChris Mason 	unsigned long bytes_left;
1063fd396afSMikhail Zaslonko 	unsigned int in_buf_pages;
10738c31464SDavid Sterba 	unsigned long len = *total_out;
1084d3a800eSDavid Sterba 	unsigned long nr_dest_pages = *out_pages;
109e5d74902SDavid Sterba 	const unsigned long max_out = nr_dest_pages * PAGE_SIZE;
110c8b97818SChris Mason 
111c8b97818SChris Mason 	*out_pages = 0;
112c8b97818SChris Mason 	*total_out = 0;
113c8b97818SChris Mason 	*total_in = 0;
114c8b97818SChris Mason 
115f51d2b59SDavid Sterba 	if (Z_OK != zlib_deflateInit(&workspace->strm, workspace->level)) {
11662e85577SJeff Mahoney 		pr_warn("BTRFS: deflateInit failed\n");
11760e1975aSZach Brown 		ret = -EIO;
118c8b97818SChris Mason 		goto out;
119c8b97818SChris Mason 	}
120c8b97818SChris Mason 
12178809913SSergey Senozhatsky 	workspace->strm.total_in = 0;
12278809913SSergey Senozhatsky 	workspace->strm.total_out = 0;
123c8b97818SChris Mason 
124b0ee5e1eSDavid Sterba 	out_page = alloc_page(GFP_NOFS);
1254b72029dSLi Zefan 	if (out_page == NULL) {
12660e1975aSZach Brown 		ret = -ENOMEM;
1274b72029dSLi Zefan 		goto out;
1284b72029dSLi Zefan 	}
129718e5855SFabio M. De Francesco 	cpage_out = page_address(out_page);
130c8b97818SChris Mason 	pages[0] = out_page;
131c8b97818SChris Mason 	nr_pages = 1;
132c8b97818SChris Mason 
1333fd396afSMikhail Zaslonko 	workspace->strm.next_in = workspace->buf;
1343fd396afSMikhail Zaslonko 	workspace->strm.avail_in = 0;
13578809913SSergey Senozhatsky 	workspace->strm.next_out = cpage_out;
13609cbfeafSKirill A. Shutemov 	workspace->strm.avail_out = PAGE_SIZE;
137c8b97818SChris Mason 
13878809913SSergey Senozhatsky 	while (workspace->strm.total_in < len) {
1393fd396afSMikhail Zaslonko 		/*
1403fd396afSMikhail Zaslonko 		 * Get next input pages and copy the contents to
1413fd396afSMikhail Zaslonko 		 * the workspace buffer if required.
1423fd396afSMikhail Zaslonko 		 */
1433fd396afSMikhail Zaslonko 		if (workspace->strm.avail_in == 0) {
1443fd396afSMikhail Zaslonko 			bytes_left = len - workspace->strm.total_in;
1453fd396afSMikhail Zaslonko 			in_buf_pages = min(DIV_ROUND_UP(bytes_left, PAGE_SIZE),
1463fd396afSMikhail Zaslonko 					   workspace->buf_size / PAGE_SIZE);
1473fd396afSMikhail Zaslonko 			if (in_buf_pages > 1) {
1483fd396afSMikhail Zaslonko 				int i;
1493fd396afSMikhail Zaslonko 
1503fd396afSMikhail Zaslonko 				for (i = 0; i < in_buf_pages; i++) {
151718e5855SFabio M. De Francesco 					if (data_in) {
152718e5855SFabio M. De Francesco 						kunmap_local(data_in);
1533fd396afSMikhail Zaslonko 						put_page(in_page);
15455276e14SDavid Sterba 					}
1553fd396afSMikhail Zaslonko 					in_page = find_get_page(mapping,
1563fd396afSMikhail Zaslonko 								start >> PAGE_SHIFT);
157718e5855SFabio M. De Francesco 					data_in = kmap_local_page(in_page);
1589e5e6d4eSDavid Sterba 					copy_page(workspace->buf + i * PAGE_SIZE,
1599e5e6d4eSDavid Sterba 						  data_in);
1603fd396afSMikhail Zaslonko 					start += PAGE_SIZE;
1613fd396afSMikhail Zaslonko 				}
1623fd396afSMikhail Zaslonko 				workspace->strm.next_in = workspace->buf;
1633fd396afSMikhail Zaslonko 			} else {
164718e5855SFabio M. De Francesco 				if (data_in) {
165718e5855SFabio M. De Francesco 					kunmap_local(data_in);
1663fd396afSMikhail Zaslonko 					put_page(in_page);
16755276e14SDavid Sterba 				}
1683fd396afSMikhail Zaslonko 				in_page = find_get_page(mapping,
1693fd396afSMikhail Zaslonko 							start >> PAGE_SHIFT);
170718e5855SFabio M. De Francesco 				data_in = kmap_local_page(in_page);
1713fd396afSMikhail Zaslonko 				start += PAGE_SIZE;
1723fd396afSMikhail Zaslonko 				workspace->strm.next_in = data_in;
1733fd396afSMikhail Zaslonko 			}
1743fd396afSMikhail Zaslonko 			workspace->strm.avail_in = min(bytes_left,
1753fd396afSMikhail Zaslonko 						       (unsigned long) workspace->buf_size);
1763fd396afSMikhail Zaslonko 		}
1773fd396afSMikhail Zaslonko 
17878809913SSergey Senozhatsky 		ret = zlib_deflate(&workspace->strm, Z_SYNC_FLUSH);
179c8b97818SChris Mason 		if (ret != Z_OK) {
18062e85577SJeff Mahoney 			pr_debug("BTRFS: deflate in loop returned %d\n",
181c8b97818SChris Mason 			       ret);
18278809913SSergey Senozhatsky 			zlib_deflateEnd(&workspace->strm);
18360e1975aSZach Brown 			ret = -EIO;
184c8b97818SChris Mason 			goto out;
185c8b97818SChris Mason 		}
186c8b97818SChris Mason 
187c8b97818SChris Mason 		/* we're making it bigger, give up */
18878809913SSergey Senozhatsky 		if (workspace->strm.total_in > 8192 &&
18978809913SSergey Senozhatsky 		    workspace->strm.total_in <
19078809913SSergey Senozhatsky 		    workspace->strm.total_out) {
191130d5b41SDavid Sterba 			ret = -E2BIG;
192c8b97818SChris Mason 			goto out;
193c8b97818SChris Mason 		}
194c8b97818SChris Mason 		/* we need another page for writing out.  Test this
195c8b97818SChris Mason 		 * before the total_in so we will pull in a new page for
196c8b97818SChris Mason 		 * the stream end if required
197c8b97818SChris Mason 		 */
19878809913SSergey Senozhatsky 		if (workspace->strm.avail_out == 0) {
199c8b97818SChris Mason 			if (nr_pages == nr_dest_pages) {
20060e1975aSZach Brown 				ret = -E2BIG;
201c8b97818SChris Mason 				goto out;
202c8b97818SChris Mason 			}
203b0ee5e1eSDavid Sterba 			out_page = alloc_page(GFP_NOFS);
2044b72029dSLi Zefan 			if (out_page == NULL) {
20560e1975aSZach Brown 				ret = -ENOMEM;
2064b72029dSLi Zefan 				goto out;
2074b72029dSLi Zefan 			}
208718e5855SFabio M. De Francesco 			cpage_out = page_address(out_page);
209c8b97818SChris Mason 			pages[nr_pages] = out_page;
210c8b97818SChris Mason 			nr_pages++;
21109cbfeafSKirill A. Shutemov 			workspace->strm.avail_out = PAGE_SIZE;
21278809913SSergey Senozhatsky 			workspace->strm.next_out = cpage_out;
213c8b97818SChris Mason 		}
214c8b97818SChris Mason 		/* we're all done */
21578809913SSergey Senozhatsky 		if (workspace->strm.total_in >= len)
216c8b97818SChris Mason 			break;
21778809913SSergey Senozhatsky 		if (workspace->strm.total_out > max_out)
218c8b97818SChris Mason 			break;
219c8b97818SChris Mason 	}
22078809913SSergey Senozhatsky 	workspace->strm.avail_in = 0;
2213fd396afSMikhail Zaslonko 	/*
2223fd396afSMikhail Zaslonko 	 * Call deflate with Z_FINISH flush parameter providing more output
2233fd396afSMikhail Zaslonko 	 * space but no more input data, until it returns with Z_STREAM_END.
2243fd396afSMikhail Zaslonko 	 */
2253fd396afSMikhail Zaslonko 	while (ret != Z_STREAM_END) {
22678809913SSergey Senozhatsky 		ret = zlib_deflate(&workspace->strm, Z_FINISH);
2273fd396afSMikhail Zaslonko 		if (ret == Z_STREAM_END)
2283fd396afSMikhail Zaslonko 			break;
2293fd396afSMikhail Zaslonko 		if (ret != Z_OK && ret != Z_BUF_ERROR) {
23078809913SSergey Senozhatsky 			zlib_deflateEnd(&workspace->strm);
23160e1975aSZach Brown 			ret = -EIO;
232c8b97818SChris Mason 			goto out;
2333fd396afSMikhail Zaslonko 		} else if (workspace->strm.avail_out == 0) {
2343fd396afSMikhail Zaslonko 			/* get another page for the stream end */
2353fd396afSMikhail Zaslonko 			if (nr_pages == nr_dest_pages) {
2363fd396afSMikhail Zaslonko 				ret = -E2BIG;
2373fd396afSMikhail Zaslonko 				goto out;
238c8b97818SChris Mason 			}
239b0ee5e1eSDavid Sterba 			out_page = alloc_page(GFP_NOFS);
2403fd396afSMikhail Zaslonko 			if (out_page == NULL) {
2413fd396afSMikhail Zaslonko 				ret = -ENOMEM;
2423fd396afSMikhail Zaslonko 				goto out;
2433fd396afSMikhail Zaslonko 			}
244718e5855SFabio M. De Francesco 			cpage_out = page_address(out_page);
2453fd396afSMikhail Zaslonko 			pages[nr_pages] = out_page;
2463fd396afSMikhail Zaslonko 			nr_pages++;
2473fd396afSMikhail Zaslonko 			workspace->strm.avail_out = PAGE_SIZE;
2483fd396afSMikhail Zaslonko 			workspace->strm.next_out = cpage_out;
2493fd396afSMikhail Zaslonko 		}
2503fd396afSMikhail Zaslonko 	}
2513fd396afSMikhail Zaslonko 	zlib_deflateEnd(&workspace->strm);
252c8b97818SChris Mason 
25378809913SSergey Senozhatsky 	if (workspace->strm.total_out >= workspace->strm.total_in) {
25460e1975aSZach Brown 		ret = -E2BIG;
255c8b97818SChris Mason 		goto out;
256c8b97818SChris Mason 	}
257c8b97818SChris Mason 
258c8b97818SChris Mason 	ret = 0;
25978809913SSergey Senozhatsky 	*total_out = workspace->strm.total_out;
26078809913SSergey Senozhatsky 	*total_in = workspace->strm.total_in;
261c8b97818SChris Mason out:
262c8b97818SChris Mason 	*out_pages = nr_pages;
263718e5855SFabio M. De Francesco 	if (data_in) {
264718e5855SFabio M. De Francesco 		kunmap_local(data_in);
26509cbfeafSKirill A. Shutemov 		put_page(in_page);
26655276e14SDavid Sterba 	}
267718e5855SFabio M. De Francesco 
268c8b97818SChris Mason 	return ret;
269c8b97818SChris Mason }
270c8b97818SChris Mason 
zlib_decompress_bio(struct list_head * ws,struct compressed_bio * cb)271c4bf665aSDavid Sterba int zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb)
272c8b97818SChris Mason {
273261507a0SLi Zefan 	struct workspace *workspace = list_entry(ws, struct workspace, list);
2743a39c18dSLi Zefan 	int ret = 0, ret2;
275c8b97818SChris Mason 	int wbits = MAX_WBITS;
276c8b97818SChris Mason 	char *data_in;
277c8b97818SChris Mason 	size_t total_out = 0;
278c8b97818SChris Mason 	unsigned long page_in_index = 0;
279e1ddce71SAnand Jain 	size_t srclen = cb->compressed_len;
28009cbfeafSKirill A. Shutemov 	unsigned long total_pages_in = DIV_ROUND_UP(srclen, PAGE_SIZE);
281c8b97818SChris Mason 	unsigned long buf_start;
282e1ddce71SAnand Jain 	struct page **pages_in = cb->compressed_pages;
283c8b97818SChris Mason 
2845a6e6e7cSFabio M. De Francesco 	data_in = kmap_local_page(pages_in[page_in_index]);
28578809913SSergey Senozhatsky 	workspace->strm.next_in = data_in;
28609cbfeafSKirill A. Shutemov 	workspace->strm.avail_in = min_t(size_t, srclen, PAGE_SIZE);
28778809913SSergey Senozhatsky 	workspace->strm.total_in = 0;
288c8b97818SChris Mason 
28978809913SSergey Senozhatsky 	workspace->strm.total_out = 0;
29078809913SSergey Senozhatsky 	workspace->strm.next_out = workspace->buf;
2913fd396afSMikhail Zaslonko 	workspace->strm.avail_out = workspace->buf_size;
292c8b97818SChris Mason 
293c8b97818SChris Mason 	/* If it's deflate, and it's got no preset dictionary, then
294c8b97818SChris Mason 	   we can tell zlib to skip the adler32 check. */
295c8b97818SChris Mason 	if (srclen > 2 && !(data_in[1] & PRESET_DICT) &&
296c8b97818SChris Mason 	    ((data_in[0] & 0x0f) == Z_DEFLATED) &&
297c8b97818SChris Mason 	    !(((data_in[0]<<8) + data_in[1]) % 31)) {
298c8b97818SChris Mason 
299c8b97818SChris Mason 		wbits = -((data_in[0] >> 4) + 8);
30078809913SSergey Senozhatsky 		workspace->strm.next_in += 2;
30178809913SSergey Senozhatsky 		workspace->strm.avail_in -= 2;
302c8b97818SChris Mason 	}
303c8b97818SChris Mason 
30478809913SSergey Senozhatsky 	if (Z_OK != zlib_inflateInit2(&workspace->strm, wbits)) {
30562e85577SJeff Mahoney 		pr_warn("BTRFS: inflateInit failed\n");
3065a6e6e7cSFabio M. De Francesco 		kunmap_local(data_in);
30760e1975aSZach Brown 		return -EIO;
308c8b97818SChris Mason 	}
30978809913SSergey Senozhatsky 	while (workspace->strm.total_in < srclen) {
31078809913SSergey Senozhatsky 		ret = zlib_inflate(&workspace->strm, Z_NO_FLUSH);
311d397712bSChris Mason 		if (ret != Z_OK && ret != Z_STREAM_END)
312c8b97818SChris Mason 			break;
313c8b97818SChris Mason 
3143a39c18dSLi Zefan 		buf_start = total_out;
31578809913SSergey Senozhatsky 		total_out = workspace->strm.total_out;
316c8b97818SChris Mason 
3173a39c18dSLi Zefan 		/* we didn't make progress in this inflate call, we're done */
3183a39c18dSLi Zefan 		if (buf_start == total_out)
319c8b97818SChris Mason 			break;
320c8b97818SChris Mason 
3211c3dc173SQu Wenruo 		ret2 = btrfs_decompress_buf2page(workspace->buf,
3221c3dc173SQu Wenruo 				total_out - buf_start, cb, buf_start);
3233a39c18dSLi Zefan 		if (ret2 == 0) {
324c8b97818SChris Mason 			ret = 0;
325c8b97818SChris Mason 			goto done;
326c8b97818SChris Mason 		}
327d397712bSChris Mason 
32878809913SSergey Senozhatsky 		workspace->strm.next_out = workspace->buf;
3293fd396afSMikhail Zaslonko 		workspace->strm.avail_out = workspace->buf_size;
330c8b97818SChris Mason 
33178809913SSergey Senozhatsky 		if (workspace->strm.avail_in == 0) {
332c8b97818SChris Mason 			unsigned long tmp;
3335a6e6e7cSFabio M. De Francesco 			kunmap_local(data_in);
334c8b97818SChris Mason 			page_in_index++;
335c8b97818SChris Mason 			if (page_in_index >= total_pages_in) {
336c8b97818SChris Mason 				data_in = NULL;
337c8b97818SChris Mason 				break;
338c8b97818SChris Mason 			}
3395a6e6e7cSFabio M. De Francesco 			data_in = kmap_local_page(pages_in[page_in_index]);
34078809913SSergey Senozhatsky 			workspace->strm.next_in = data_in;
34178809913SSergey Senozhatsky 			tmp = srclen - workspace->strm.total_in;
3421c3dc173SQu Wenruo 			workspace->strm.avail_in = min(tmp, PAGE_SIZE);
343c8b97818SChris Mason 		}
344c8b97818SChris Mason 	}
345d397712bSChris Mason 	if (ret != Z_STREAM_END)
34660e1975aSZach Brown 		ret = -EIO;
347d397712bSChris Mason 	else
348c8b97818SChris Mason 		ret = 0;
349c8b97818SChris Mason done:
35078809913SSergey Senozhatsky 	zlib_inflateEnd(&workspace->strm);
35155276e14SDavid Sterba 	if (data_in)
3525a6e6e7cSFabio M. De Francesco 		kunmap_local(data_in);
353c8b97818SChris Mason 	return ret;
354c8b97818SChris Mason }
355c8b97818SChris Mason 
zlib_decompress(struct list_head * ws,const u8 * data_in,struct page * dest_page,unsigned long start_byte,size_t srclen,size_t destlen)3563e09b5b2SDavid Sterba int zlib_decompress(struct list_head *ws, const u8 *data_in,
357c4bf665aSDavid Sterba 		struct page *dest_page, unsigned long start_byte, size_t srclen,
358c4bf665aSDavid Sterba 		size_t destlen)
359c8b97818SChris Mason {
360261507a0SLi Zefan 	struct workspace *workspace = list_entry(ws, struct workspace, list);
361c8b97818SChris Mason 	int ret = 0;
362c8b97818SChris Mason 	int wbits = MAX_WBITS;
3632f19cad9SChris Mason 	unsigned long bytes_left;
364c8b97818SChris Mason 	unsigned long total_out = 0;
3652f19cad9SChris Mason 	unsigned long pg_offset = 0;
366c8b97818SChris Mason 
3672f19cad9SChris Mason 	destlen = min_t(unsigned long, destlen, PAGE_SIZE);
3682f19cad9SChris Mason 	bytes_left = destlen;
3692f19cad9SChris Mason 
37078809913SSergey Senozhatsky 	workspace->strm.next_in = data_in;
37178809913SSergey Senozhatsky 	workspace->strm.avail_in = srclen;
37278809913SSergey Senozhatsky 	workspace->strm.total_in = 0;
373c8b97818SChris Mason 
37478809913SSergey Senozhatsky 	workspace->strm.next_out = workspace->buf;
3753fd396afSMikhail Zaslonko 	workspace->strm.avail_out = workspace->buf_size;
37678809913SSergey Senozhatsky 	workspace->strm.total_out = 0;
377c8b97818SChris Mason 	/* If it's deflate, and it's got no preset dictionary, then
378c8b97818SChris Mason 	   we can tell zlib to skip the adler32 check. */
379c8b97818SChris Mason 	if (srclen > 2 && !(data_in[1] & PRESET_DICT) &&
380c8b97818SChris Mason 	    ((data_in[0] & 0x0f) == Z_DEFLATED) &&
381c8b97818SChris Mason 	    !(((data_in[0]<<8) + data_in[1]) % 31)) {
382c8b97818SChris Mason 
383c8b97818SChris Mason 		wbits = -((data_in[0] >> 4) + 8);
38478809913SSergey Senozhatsky 		workspace->strm.next_in += 2;
38578809913SSergey Senozhatsky 		workspace->strm.avail_in -= 2;
386c8b97818SChris Mason 	}
387c8b97818SChris Mason 
38878809913SSergey Senozhatsky 	if (Z_OK != zlib_inflateInit2(&workspace->strm, wbits)) {
38962e85577SJeff Mahoney 		pr_warn("BTRFS: inflateInit failed\n");
39060e1975aSZach Brown 		return -EIO;
391c8b97818SChris Mason 	}
392c8b97818SChris Mason 
393c8b97818SChris Mason 	while (bytes_left > 0) {
394c8b97818SChris Mason 		unsigned long buf_start;
395c8b97818SChris Mason 		unsigned long buf_offset;
396c8b97818SChris Mason 		unsigned long bytes;
397c8b97818SChris Mason 
39878809913SSergey Senozhatsky 		ret = zlib_inflate(&workspace->strm, Z_NO_FLUSH);
399d397712bSChris Mason 		if (ret != Z_OK && ret != Z_STREAM_END)
400c8b97818SChris Mason 			break;
401c8b97818SChris Mason 
402c8b97818SChris Mason 		buf_start = total_out;
40378809913SSergey Senozhatsky 		total_out = workspace->strm.total_out;
404c8b97818SChris Mason 
405c8b97818SChris Mason 		if (total_out == buf_start) {
40660e1975aSZach Brown 			ret = -EIO;
407c8b97818SChris Mason 			break;
408c8b97818SChris Mason 		}
409c8b97818SChris Mason 
410d397712bSChris Mason 		if (total_out <= start_byte)
411c8b97818SChris Mason 			goto next;
412c8b97818SChris Mason 
413d397712bSChris Mason 		if (total_out > start_byte && buf_start < start_byte)
414c8b97818SChris Mason 			buf_offset = start_byte - buf_start;
415d397712bSChris Mason 		else
416c8b97818SChris Mason 			buf_offset = 0;
417c8b97818SChris Mason 
41809cbfeafSKirill A. Shutemov 		bytes = min(PAGE_SIZE - pg_offset,
4193fd396afSMikhail Zaslonko 			    PAGE_SIZE - (buf_offset % PAGE_SIZE));
420c8b97818SChris Mason 		bytes = min(bytes, bytes_left);
421c8b97818SChris Mason 
4223590ec58SIra Weiny 		memcpy_to_page(dest_page, pg_offset,
4233590ec58SIra Weiny 			       workspace->buf + buf_offset, bytes);
424c8b97818SChris Mason 
425c8b97818SChris Mason 		pg_offset += bytes;
426c8b97818SChris Mason 		bytes_left -= bytes;
427c8b97818SChris Mason next:
42878809913SSergey Senozhatsky 		workspace->strm.next_out = workspace->buf;
4293fd396afSMikhail Zaslonko 		workspace->strm.avail_out = workspace->buf_size;
430c8b97818SChris Mason 	}
431d397712bSChris Mason 
432d397712bSChris Mason 	if (ret != Z_STREAM_END && bytes_left != 0)
43360e1975aSZach Brown 		ret = -EIO;
434d397712bSChris Mason 	else
435c8b97818SChris Mason 		ret = 0;
436d397712bSChris Mason 
43778809913SSergey Senozhatsky 	zlib_inflateEnd(&workspace->strm);
4382f19cad9SChris Mason 
4392f19cad9SChris Mason 	/*
4402f19cad9SChris Mason 	 * this should only happen if zlib returned fewer bytes than we
4412f19cad9SChris Mason 	 * expected.  btrfs_get_block is responsible for zeroing from the
4422f19cad9SChris Mason 	 * end of the inline extent (destlen) to the end of the page
4432f19cad9SChris Mason 	 */
4442f19cad9SChris Mason 	if (pg_offset < destlen) {
445d048b9c2SIra Weiny 		memzero_page(dest_page, pg_offset, destlen - pg_offset);
4462f19cad9SChris Mason 	}
447c8b97818SChris Mason 	return ret;
448c8b97818SChris Mason }
449c8b97818SChris Mason 
450e8c9f186SDavid Sterba const struct btrfs_compress_op btrfs_zlib_compress = {
451be951045SDavid Sterba 	.workspace_manager	= &wsm,
452e18333a7SDavid Sterba 	.max_level		= 9,
453e18333a7SDavid Sterba 	.default_level		= BTRFS_ZLIB_DEFAULT_LEVEL,
454261507a0SLi Zefan };
455