]>
Commit | Line | Data |
---|---|---|
c1d7c514 | 1 | // SPDX-License-Identifier: GPL-2.0 |
c8b97818 CM |
2 | /* |
3 | * Copyright (C) 2008 Oracle. All rights reserved. | |
4 | * | |
c8b97818 CM |
5 | * Based on jffs2 zlib code: |
6 | * Copyright © 2001-2007 Red Hat, Inc. | |
7 | * Created by David Woodhouse <[email protected]> | |
8 | */ | |
9 | ||
10 | #include <linux/kernel.h> | |
11 | #include <linux/slab.h> | |
12 | #include <linux/zlib.h> | |
13 | #include <linux/zutil.h> | |
6acafd1e | 14 | #include <linux/mm.h> |
c8b97818 CM |
15 | #include <linux/init.h> |
16 | #include <linux/err.h> | |
17 | #include <linux/sched.h> | |
18 | #include <linux/pagemap.h> | |
19 | #include <linux/bio.h> | |
e1ddce71 | 20 | #include <linux/refcount.h> |
b2950863 | 21 | #include "compression.h" |
c8b97818 | 22 | |
c8b97818 | 23 | struct workspace { |
78809913 | 24 | z_stream strm; |
c8b97818 CM |
25 | char *buf; |
26 | struct list_head list; | |
f51d2b59 | 27 | int level; |
c8b97818 CM |
28 | }; |
29 | ||
261507a0 LZ |
30 | static void zlib_free_workspace(struct list_head *ws) |
31 | { | |
32 | struct workspace *workspace = list_entry(ws, struct workspace, list); | |
c8b97818 | 33 | |
6acafd1e | 34 | kvfree(workspace->strm.workspace); |
261507a0 LZ |
35 | kfree(workspace->buf); |
36 | kfree(workspace); | |
37 | } | |
38 | ||
39 | static struct list_head *zlib_alloc_workspace(void) | |
c8b97818 CM |
40 | { |
41 | struct workspace *workspace; | |
78809913 | 42 | int workspacesize; |
8844355d | 43 | |
389a6cfc | 44 | workspace = kzalloc(sizeof(*workspace), GFP_KERNEL); |
261507a0 LZ |
45 | if (!workspace) |
46 | return ERR_PTR(-ENOMEM); | |
c8b97818 | 47 | |
78809913 SS |
48 | workspacesize = max(zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL), |
49 | zlib_inflate_workspacesize()); | |
6acafd1e | 50 | workspace->strm.workspace = kvmalloc(workspacesize, GFP_KERNEL); |
389a6cfc | 51 | workspace->buf = kmalloc(PAGE_SIZE, GFP_KERNEL); |
78809913 | 52 | if (!workspace->strm.workspace || !workspace->buf) |
261507a0 | 53 | goto fail; |
c8b97818 | 54 | |
261507a0 | 55 | INIT_LIST_HEAD(&workspace->list); |
c8b97818 | 56 | |
261507a0 LZ |
57 | return &workspace->list; |
58 | fail: | |
59 | zlib_free_workspace(&workspace->list); | |
60 | return ERR_PTR(-ENOMEM); | |
c8b97818 CM |
61 | } |
62 | ||
261507a0 LZ |
63 | static int zlib_compress_pages(struct list_head *ws, |
64 | struct address_space *mapping, | |
38c31464 | 65 | u64 start, |
261507a0 | 66 | struct page **pages, |
261507a0 LZ |
67 | unsigned long *out_pages, |
68 | unsigned long *total_in, | |
e5d74902 | 69 | unsigned long *total_out) |
c8b97818 | 70 | { |
261507a0 | 71 | struct workspace *workspace = list_entry(ws, struct workspace, list); |
c8b97818 | 72 | int ret; |
c8b97818 CM |
73 | char *data_in; |
74 | char *cpage_out; | |
75 | int nr_pages = 0; | |
76 | struct page *in_page = NULL; | |
77 | struct page *out_page = NULL; | |
c8b97818 | 78 | unsigned long bytes_left; |
38c31464 | 79 | unsigned long len = *total_out; |
4d3a800e | 80 | unsigned long nr_dest_pages = *out_pages; |
e5d74902 | 81 | const unsigned long max_out = nr_dest_pages * PAGE_SIZE; |
c8b97818 CM |
82 | |
83 | *out_pages = 0; | |
84 | *total_out = 0; | |
85 | *total_in = 0; | |
86 | ||
f51d2b59 | 87 | if (Z_OK != zlib_deflateInit(&workspace->strm, workspace->level)) { |
62e85577 | 88 | pr_warn("BTRFS: deflateInit failed\n"); |
60e1975a | 89 | ret = -EIO; |
c8b97818 CM |
90 | goto out; |
91 | } | |
92 | ||
78809913 SS |
93 | workspace->strm.total_in = 0; |
94 | workspace->strm.total_out = 0; | |
c8b97818 | 95 | |
09cbfeaf | 96 | in_page = find_get_page(mapping, start >> PAGE_SHIFT); |
c8b97818 CM |
97 | data_in = kmap(in_page); |
98 | ||
99 | out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); | |
4b72029d | 100 | if (out_page == NULL) { |
60e1975a | 101 | ret = -ENOMEM; |
4b72029d LZ |
102 | goto out; |
103 | } | |
c8b97818 CM |
104 | cpage_out = kmap(out_page); |
105 | pages[0] = out_page; | |
106 | nr_pages = 1; | |
107 | ||
78809913 SS |
108 | workspace->strm.next_in = data_in; |
109 | workspace->strm.next_out = cpage_out; | |
09cbfeaf KS |
110 | workspace->strm.avail_out = PAGE_SIZE; |
111 | workspace->strm.avail_in = min(len, PAGE_SIZE); | |
c8b97818 | 112 | |
78809913 SS |
113 | while (workspace->strm.total_in < len) { |
114 | ret = zlib_deflate(&workspace->strm, Z_SYNC_FLUSH); | |
c8b97818 | 115 | if (ret != Z_OK) { |
62e85577 | 116 | pr_debug("BTRFS: deflate in loop returned %d\n", |
c8b97818 | 117 | ret); |
78809913 | 118 | zlib_deflateEnd(&workspace->strm); |
60e1975a | 119 | ret = -EIO; |
c8b97818 CM |
120 | goto out; |
121 | } | |
122 | ||
123 | /* we're making it bigger, give up */ | |
78809913 SS |
124 | if (workspace->strm.total_in > 8192 && |
125 | workspace->strm.total_in < | |
126 | workspace->strm.total_out) { | |
130d5b41 | 127 | ret = -E2BIG; |
c8b97818 CM |
128 | goto out; |
129 | } | |
130 | /* we need another page for writing out. Test this | |
131 | * before the total_in so we will pull in a new page for | |
132 | * the stream end if required | |
133 | */ | |
78809913 | 134 | if (workspace->strm.avail_out == 0) { |
c8b97818 CM |
135 | kunmap(out_page); |
136 | if (nr_pages == nr_dest_pages) { | |
137 | out_page = NULL; | |
60e1975a | 138 | ret = -E2BIG; |
c8b97818 CM |
139 | goto out; |
140 | } | |
141 | out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); | |
4b72029d | 142 | if (out_page == NULL) { |
60e1975a | 143 | ret = -ENOMEM; |
4b72029d LZ |
144 | goto out; |
145 | } | |
c8b97818 CM |
146 | cpage_out = kmap(out_page); |
147 | pages[nr_pages] = out_page; | |
148 | nr_pages++; | |
09cbfeaf | 149 | workspace->strm.avail_out = PAGE_SIZE; |
78809913 | 150 | workspace->strm.next_out = cpage_out; |
c8b97818 CM |
151 | } |
152 | /* we're all done */ | |
78809913 | 153 | if (workspace->strm.total_in >= len) |
c8b97818 CM |
154 | break; |
155 | ||
156 | /* we've read in a full page, get a new one */ | |
78809913 SS |
157 | if (workspace->strm.avail_in == 0) { |
158 | if (workspace->strm.total_out > max_out) | |
c8b97818 CM |
159 | break; |
160 | ||
78809913 | 161 | bytes_left = len - workspace->strm.total_in; |
c8b97818 | 162 | kunmap(in_page); |
09cbfeaf | 163 | put_page(in_page); |
c8b97818 | 164 | |
09cbfeaf | 165 | start += PAGE_SIZE; |
c8b97818 | 166 | in_page = find_get_page(mapping, |
09cbfeaf | 167 | start >> PAGE_SHIFT); |
c8b97818 | 168 | data_in = kmap(in_page); |
78809913 | 169 | workspace->strm.avail_in = min(bytes_left, |
09cbfeaf | 170 | PAGE_SIZE); |
78809913 | 171 | workspace->strm.next_in = data_in; |
c8b97818 CM |
172 | } |
173 | } | |
78809913 SS |
174 | workspace->strm.avail_in = 0; |
175 | ret = zlib_deflate(&workspace->strm, Z_FINISH); | |
176 | zlib_deflateEnd(&workspace->strm); | |
c8b97818 CM |
177 | |
178 | if (ret != Z_STREAM_END) { | |
60e1975a | 179 | ret = -EIO; |
c8b97818 CM |
180 | goto out; |
181 | } | |
182 | ||
78809913 | 183 | if (workspace->strm.total_out >= workspace->strm.total_in) { |
60e1975a | 184 | ret = -E2BIG; |
c8b97818 CM |
185 | goto out; |
186 | } | |
187 | ||
188 | ret = 0; | |
78809913 SS |
189 | *total_out = workspace->strm.total_out; |
190 | *total_in = workspace->strm.total_in; | |
c8b97818 CM |
191 | out: |
192 | *out_pages = nr_pages; | |
193 | if (out_page) | |
194 | kunmap(out_page); | |
195 | ||
196 | if (in_page) { | |
197 | kunmap(in_page); | |
09cbfeaf | 198 | put_page(in_page); |
c8b97818 | 199 | } |
c8b97818 CM |
200 | return ret; |
201 | } | |
202 | ||
e1ddce71 | 203 | static int zlib_decompress_bio(struct list_head *ws, struct compressed_bio *cb) |
c8b97818 | 204 | { |
261507a0 | 205 | struct workspace *workspace = list_entry(ws, struct workspace, list); |
3a39c18d | 206 | int ret = 0, ret2; |
c8b97818 | 207 | int wbits = MAX_WBITS; |
c8b97818 CM |
208 | char *data_in; |
209 | size_t total_out = 0; | |
c8b97818 | 210 | unsigned long page_in_index = 0; |
e1ddce71 | 211 | size_t srclen = cb->compressed_len; |
09cbfeaf | 212 | unsigned long total_pages_in = DIV_ROUND_UP(srclen, PAGE_SIZE); |
c8b97818 | 213 | unsigned long buf_start; |
e1ddce71 AJ |
214 | struct page **pages_in = cb->compressed_pages; |
215 | u64 disk_start = cb->start; | |
216 | struct bio *orig_bio = cb->orig_bio; | |
c8b97818 | 217 | |
c8b97818 | 218 | data_in = kmap(pages_in[page_in_index]); |
78809913 | 219 | workspace->strm.next_in = data_in; |
09cbfeaf | 220 | workspace->strm.avail_in = min_t(size_t, srclen, PAGE_SIZE); |
78809913 | 221 | workspace->strm.total_in = 0; |
c8b97818 | 222 | |
78809913 SS |
223 | workspace->strm.total_out = 0; |
224 | workspace->strm.next_out = workspace->buf; | |
09cbfeaf | 225 | workspace->strm.avail_out = PAGE_SIZE; |
c8b97818 CM |
226 | |
227 | /* If it's deflate, and it's got no preset dictionary, then | |
228 | we can tell zlib to skip the adler32 check. */ | |
229 | if (srclen > 2 && !(data_in[1] & PRESET_DICT) && | |
230 | ((data_in[0] & 0x0f) == Z_DEFLATED) && | |
231 | !(((data_in[0]<<8) + data_in[1]) % 31)) { | |
232 | ||
233 | wbits = -((data_in[0] >> 4) + 8); | |
78809913 SS |
234 | workspace->strm.next_in += 2; |
235 | workspace->strm.avail_in -= 2; | |
c8b97818 CM |
236 | } |
237 | ||
78809913 | 238 | if (Z_OK != zlib_inflateInit2(&workspace->strm, wbits)) { |
62e85577 | 239 | pr_warn("BTRFS: inflateInit failed\n"); |
d1111a75 | 240 | kunmap(pages_in[page_in_index]); |
60e1975a | 241 | return -EIO; |
c8b97818 | 242 | } |
78809913 SS |
243 | while (workspace->strm.total_in < srclen) { |
244 | ret = zlib_inflate(&workspace->strm, Z_NO_FLUSH); | |
d397712b | 245 | if (ret != Z_OK && ret != Z_STREAM_END) |
c8b97818 | 246 | break; |
c8b97818 | 247 | |
3a39c18d | 248 | buf_start = total_out; |
78809913 | 249 | total_out = workspace->strm.total_out; |
c8b97818 | 250 | |
3a39c18d LZ |
251 | /* we didn't make progress in this inflate call, we're done */ |
252 | if (buf_start == total_out) | |
c8b97818 | 253 | break; |
c8b97818 | 254 | |
3a39c18d LZ |
255 | ret2 = btrfs_decompress_buf2page(workspace->buf, buf_start, |
256 | total_out, disk_start, | |
974b1adc | 257 | orig_bio); |
3a39c18d LZ |
258 | if (ret2 == 0) { |
259 | ret = 0; | |
260 | goto done; | |
c8b97818 | 261 | } |
3a39c18d | 262 | |
78809913 | 263 | workspace->strm.next_out = workspace->buf; |
09cbfeaf | 264 | workspace->strm.avail_out = PAGE_SIZE; |
c8b97818 | 265 | |
78809913 | 266 | if (workspace->strm.avail_in == 0) { |
c8b97818 CM |
267 | unsigned long tmp; |
268 | kunmap(pages_in[page_in_index]); | |
269 | page_in_index++; | |
270 | if (page_in_index >= total_pages_in) { | |
271 | data_in = NULL; | |
272 | break; | |
273 | } | |
274 | data_in = kmap(pages_in[page_in_index]); | |
78809913 SS |
275 | workspace->strm.next_in = data_in; |
276 | tmp = srclen - workspace->strm.total_in; | |
277 | workspace->strm.avail_in = min(tmp, | |
09cbfeaf | 278 | PAGE_SIZE); |
c8b97818 CM |
279 | } |
280 | } | |
d397712b | 281 | if (ret != Z_STREAM_END) |
60e1975a | 282 | ret = -EIO; |
d397712b | 283 | else |
c8b97818 | 284 | ret = 0; |
c8b97818 | 285 | done: |
78809913 | 286 | zlib_inflateEnd(&workspace->strm); |
c8b97818 CM |
287 | if (data_in) |
288 | kunmap(pages_in[page_in_index]); | |
2f19cad9 | 289 | if (!ret) |
974b1adc | 290 | zero_fill_bio(orig_bio); |
c8b97818 CM |
291 | return ret; |
292 | } | |
293 | ||
261507a0 LZ |
294 | static int zlib_decompress(struct list_head *ws, unsigned char *data_in, |
295 | struct page *dest_page, | |
296 | unsigned long start_byte, | |
297 | size_t srclen, size_t destlen) | |
c8b97818 | 298 | { |
261507a0 | 299 | struct workspace *workspace = list_entry(ws, struct workspace, list); |
c8b97818 CM |
300 | int ret = 0; |
301 | int wbits = MAX_WBITS; | |
2f19cad9 | 302 | unsigned long bytes_left; |
c8b97818 | 303 | unsigned long total_out = 0; |
2f19cad9 | 304 | unsigned long pg_offset = 0; |
c8b97818 CM |
305 | char *kaddr; |
306 | ||
2f19cad9 CM |
307 | destlen = min_t(unsigned long, destlen, PAGE_SIZE); |
308 | bytes_left = destlen; | |
309 | ||
78809913 SS |
310 | workspace->strm.next_in = data_in; |
311 | workspace->strm.avail_in = srclen; | |
312 | workspace->strm.total_in = 0; | |
c8b97818 | 313 | |
78809913 | 314 | workspace->strm.next_out = workspace->buf; |
09cbfeaf | 315 | workspace->strm.avail_out = PAGE_SIZE; |
78809913 | 316 | workspace->strm.total_out = 0; |
c8b97818 CM |
317 | /* If it's deflate, and it's got no preset dictionary, then |
318 | we can tell zlib to skip the adler32 check. */ | |
319 | if (srclen > 2 && !(data_in[1] & PRESET_DICT) && | |
320 | ((data_in[0] & 0x0f) == Z_DEFLATED) && | |
321 | !(((data_in[0]<<8) + data_in[1]) % 31)) { | |
322 | ||
323 | wbits = -((data_in[0] >> 4) + 8); | |
78809913 SS |
324 | workspace->strm.next_in += 2; |
325 | workspace->strm.avail_in -= 2; | |
c8b97818 CM |
326 | } |
327 | ||
78809913 | 328 | if (Z_OK != zlib_inflateInit2(&workspace->strm, wbits)) { |
62e85577 | 329 | pr_warn("BTRFS: inflateInit failed\n"); |
60e1975a | 330 | return -EIO; |
c8b97818 CM |
331 | } |
332 | ||
d397712b | 333 | while (bytes_left > 0) { |
c8b97818 CM |
334 | unsigned long buf_start; |
335 | unsigned long buf_offset; | |
336 | unsigned long bytes; | |
c8b97818 | 337 | |
78809913 | 338 | ret = zlib_inflate(&workspace->strm, Z_NO_FLUSH); |
d397712b | 339 | if (ret != Z_OK && ret != Z_STREAM_END) |
c8b97818 | 340 | break; |
c8b97818 CM |
341 | |
342 | buf_start = total_out; | |
78809913 | 343 | total_out = workspace->strm.total_out; |
c8b97818 CM |
344 | |
345 | if (total_out == buf_start) { | |
60e1975a | 346 | ret = -EIO; |
c8b97818 CM |
347 | break; |
348 | } | |
349 | ||
d397712b | 350 | if (total_out <= start_byte) |
c8b97818 | 351 | goto next; |
c8b97818 | 352 | |
d397712b | 353 | if (total_out > start_byte && buf_start < start_byte) |
c8b97818 | 354 | buf_offset = start_byte - buf_start; |
d397712b | 355 | else |
c8b97818 | 356 | buf_offset = 0; |
c8b97818 | 357 | |
09cbfeaf KS |
358 | bytes = min(PAGE_SIZE - pg_offset, |
359 | PAGE_SIZE - buf_offset); | |
c8b97818 CM |
360 | bytes = min(bytes, bytes_left); |
361 | ||
7ac687d9 | 362 | kaddr = kmap_atomic(dest_page); |
c8b97818 | 363 | memcpy(kaddr + pg_offset, workspace->buf + buf_offset, bytes); |
7ac687d9 | 364 | kunmap_atomic(kaddr); |
c8b97818 CM |
365 | |
366 | pg_offset += bytes; | |
367 | bytes_left -= bytes; | |
368 | next: | |
78809913 | 369 | workspace->strm.next_out = workspace->buf; |
09cbfeaf | 370 | workspace->strm.avail_out = PAGE_SIZE; |
c8b97818 | 371 | } |
d397712b CM |
372 | |
373 | if (ret != Z_STREAM_END && bytes_left != 0) | |
60e1975a | 374 | ret = -EIO; |
d397712b | 375 | else |
c8b97818 | 376 | ret = 0; |
d397712b | 377 | |
78809913 | 378 | zlib_inflateEnd(&workspace->strm); |
2f19cad9 CM |
379 | |
380 | /* | |
381 | * this should only happen if zlib returned fewer bytes than we | |
382 | * expected. btrfs_get_block is responsible for zeroing from the | |
383 | * end of the inline extent (destlen) to the end of the page | |
384 | */ | |
385 | if (pg_offset < destlen) { | |
386 | kaddr = kmap_atomic(dest_page); | |
387 | memset(kaddr + pg_offset, 0, destlen - pg_offset); | |
388 | kunmap_atomic(kaddr); | |
389 | } | |
c8b97818 CM |
390 | return ret; |
391 | } | |
392 | ||
f51d2b59 DS |
393 | static void zlib_set_level(struct list_head *ws, unsigned int type) |
394 | { | |
395 | struct workspace *workspace = list_entry(ws, struct workspace, list); | |
396 | unsigned level = (type & 0xF0) >> 4; | |
397 | ||
398 | if (level > 9) | |
399 | level = 9; | |
400 | ||
401 | workspace->level = level > 0 ? level : 3; | |
402 | } | |
403 | ||
e8c9f186 | 404 | const struct btrfs_compress_op btrfs_zlib_compress = { |
261507a0 LZ |
405 | .alloc_workspace = zlib_alloc_workspace, |
406 | .free_workspace = zlib_free_workspace, | |
407 | .compress_pages = zlib_compress_pages, | |
974b1adc | 408 | .decompress_bio = zlib_decompress_bio, |
261507a0 | 409 | .decompress = zlib_decompress, |
f51d2b59 | 410 | .set_level = zlib_set_level, |
261507a0 | 411 | }; |