Blame view
fs/btrfs/zlib.c
10.4 KB
c8b978188 Btrfs: Add zlib c... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
/* * Copyright (C) 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License v2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 021110-1307, USA. * * Based on jffs2 zlib code: * Copyright © 2001-2007 Red Hat, Inc. * Created by David Woodhouse <dwmw2@infradead.org> */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/zlib.h> #include <linux/zutil.h> #include <linux/vmalloc.h> #include <linux/init.h> #include <linux/err.h> #include <linux/sched.h> #include <linux/pagemap.h> #include <linux/bio.h> |
b2950863c Btrfs: make thing... |
33 |
#include "compression.h" |
c8b978188 Btrfs: Add zlib c... |
34 |
|
c8b978188 Btrfs: Add zlib c... |
35 36 37 38 39 40 |
struct workspace { z_stream inf_strm; z_stream def_strm; char *buf; struct list_head list; }; |
261507a02 btrfs: Allow to a... |
41 42 43 |
static void zlib_free_workspace(struct list_head *ws) { struct workspace *workspace = list_entry(ws, struct workspace, list); |
c8b978188 Btrfs: Add zlib c... |
44 |
|
261507a02 btrfs: Allow to a... |
45 46 47 48 49 50 51 |
vfree(workspace->def_strm.workspace); vfree(workspace->inf_strm.workspace); kfree(workspace->buf); kfree(workspace); } static struct list_head *zlib_alloc_workspace(void) |
c8b978188 Btrfs: Add zlib c... |
52 53 |
{ struct workspace *workspace; |
8844355df btrfs: Fix bugs i... |
54 |
|
c8b978188 Btrfs: Add zlib c... |
55 |
workspace = kzalloc(sizeof(*workspace), GFP_NOFS); |
261507a02 btrfs: Allow to a... |
56 57 |
if (!workspace) return ERR_PTR(-ENOMEM); |
c8b978188 Btrfs: Add zlib c... |
58 |
|
565d76cb7 zlib: slim down z... |
59 60 |
workspace->def_strm.workspace = vmalloc(zlib_deflate_workspacesize( MAX_WBITS, MAX_MEM_LEVEL)); |
c8b978188 Btrfs: Add zlib c... |
61 |
workspace->inf_strm.workspace = vmalloc(zlib_inflate_workspacesize()); |
c8b978188 Btrfs: Add zlib c... |
62 |
workspace->buf = kmalloc(PAGE_CACHE_SIZE, GFP_NOFS); |
261507a02 btrfs: Allow to a... |
63 64 65 |
if (!workspace->def_strm.workspace || !workspace->inf_strm.workspace || !workspace->buf) goto fail; |
c8b978188 Btrfs: Add zlib c... |
66 |
|
261507a02 btrfs: Allow to a... |
67 |
INIT_LIST_HEAD(&workspace->list); |
c8b978188 Btrfs: Add zlib c... |
68 |
|
261507a02 btrfs: Allow to a... |
69 70 71 72 |
return &workspace->list; fail: zlib_free_workspace(&workspace->list); return ERR_PTR(-ENOMEM); |
c8b978188 Btrfs: Add zlib c... |
73 |
} |
261507a02 btrfs: Allow to a... |
74 75 76 77 78 79 80 81 82 |
static int zlib_compress_pages(struct list_head *ws, struct address_space *mapping, u64 start, unsigned long len, struct page **pages, unsigned long nr_dest_pages, unsigned long *out_pages, unsigned long *total_in, unsigned long *total_out, unsigned long max_out) |
c8b978188 Btrfs: Add zlib c... |
83 |
{ |
261507a02 btrfs: Allow to a... |
84 |
struct workspace *workspace = list_entry(ws, struct workspace, list); |
c8b978188 Btrfs: Add zlib c... |
85 |
int ret; |
c8b978188 Btrfs: Add zlib c... |
86 87 88 89 90 |
char *data_in; char *cpage_out; int nr_pages = 0; struct page *in_page = NULL; struct page *out_page = NULL; |
c8b978188 Btrfs: Add zlib c... |
91 92 93 94 95 |
unsigned long bytes_left; *out_pages = 0; *total_out = 0; *total_in = 0; |
c8b978188 Btrfs: Add zlib c... |
96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
if (Z_OK != zlib_deflateInit(&workspace->def_strm, 3)) { printk(KERN_WARNING "deflateInit failed "); ret = -1; goto out; } workspace->def_strm.total_in = 0; workspace->def_strm.total_out = 0; in_page = find_get_page(mapping, start >> PAGE_CACHE_SHIFT); data_in = kmap(in_page); out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); |
4b72029dc btrfs: Fix error ... |
110 111 112 113 |
if (out_page == NULL) { ret = -1; goto out; } |
c8b978188 Btrfs: Add zlib c... |
114 115 116 117 118 119 120 121 |
cpage_out = kmap(out_page); pages[0] = out_page; nr_pages = 1; workspace->def_strm.next_in = data_in; workspace->def_strm.next_out = cpage_out; workspace->def_strm.avail_out = PAGE_CACHE_SIZE; workspace->def_strm.avail_in = min(len, PAGE_CACHE_SIZE); |
c8b978188 Btrfs: Add zlib c... |
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
while (workspace->def_strm.total_in < len) { ret = zlib_deflate(&workspace->def_strm, Z_SYNC_FLUSH); if (ret != Z_OK) { printk(KERN_DEBUG "btrfs deflate in loop returned %d ", ret); zlib_deflateEnd(&workspace->def_strm); ret = -1; goto out; } /* we're making it bigger, give up */ if (workspace->def_strm.total_in > 8192 && workspace->def_strm.total_in < workspace->def_strm.total_out) { ret = -1; goto out; } /* we need another page for writing out. Test this * before the total_in so we will pull in a new page for * the stream end if required */ if (workspace->def_strm.avail_out == 0) { kunmap(out_page); if (nr_pages == nr_dest_pages) { out_page = NULL; ret = -1; goto out; } out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM); |
4b72029dc btrfs: Fix error ... |
152 153 154 155 |
if (out_page == NULL) { ret = -1; goto out; } |
c8b978188 Btrfs: Add zlib c... |
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
cpage_out = kmap(out_page); pages[nr_pages] = out_page; nr_pages++; workspace->def_strm.avail_out = PAGE_CACHE_SIZE; workspace->def_strm.next_out = cpage_out; } /* we're all done */ if (workspace->def_strm.total_in >= len) break; /* we've read in a full page, get a new one */ if (workspace->def_strm.avail_in == 0) { if (workspace->def_strm.total_out > max_out) break; bytes_left = len - workspace->def_strm.total_in; kunmap(in_page); page_cache_release(in_page); start += PAGE_CACHE_SIZE; in_page = find_get_page(mapping, start >> PAGE_CACHE_SHIFT); data_in = kmap(in_page); workspace->def_strm.avail_in = min(bytes_left, PAGE_CACHE_SIZE); workspace->def_strm.next_in = data_in; } } workspace->def_strm.avail_in = 0; ret = zlib_deflate(&workspace->def_strm, Z_FINISH); zlib_deflateEnd(&workspace->def_strm); if (ret != Z_STREAM_END) { ret = -1; goto out; } if (workspace->def_strm.total_out >= workspace->def_strm.total_in) { ret = -1; goto out; } ret = 0; *total_out = workspace->def_strm.total_out; *total_in = workspace->def_strm.total_in; out: *out_pages = nr_pages; if (out_page) kunmap(out_page); if (in_page) { kunmap(in_page); page_cache_release(in_page); } |
c8b978188 Btrfs: Add zlib c... |
210 211 |
return ret; } |
261507a02 btrfs: Allow to a... |
212 213 214 215 216 |
static int zlib_decompress_biovec(struct list_head *ws, struct page **pages_in, u64 disk_start, struct bio_vec *bvec, int vcnt, size_t srclen) |
c8b978188 Btrfs: Add zlib c... |
217 |
{ |
261507a02 btrfs: Allow to a... |
218 |
struct workspace *workspace = list_entry(ws, struct workspace, list); |
3a39c18d6 btrfs: Extract du... |
219 |
int ret = 0, ret2; |
c8b978188 Btrfs: Add zlib c... |
220 |
int wbits = MAX_WBITS; |
c8b978188 Btrfs: Add zlib c... |
221 222 |
char *data_in; size_t total_out = 0; |
c8b978188 Btrfs: Add zlib c... |
223 224 |
unsigned long page_in_index = 0; unsigned long page_out_index = 0; |
c8b978188 Btrfs: Add zlib c... |
225 226 227 |
unsigned long total_pages_in = (srclen + PAGE_CACHE_SIZE - 1) / PAGE_CACHE_SIZE; unsigned long buf_start; |
c8b978188 Btrfs: Add zlib c... |
228 |
unsigned long pg_offset; |
c8b978188 Btrfs: Add zlib c... |
229 |
|
c8b978188 Btrfs: Add zlib c... |
230 231 |
data_in = kmap(pages_in[page_in_index]); workspace->inf_strm.next_in = data_in; |
5b050f04c Btrfs: Fix compil... |
232 |
workspace->inf_strm.avail_in = min_t(size_t, srclen, PAGE_CACHE_SIZE); |
c8b978188 Btrfs: Add zlib c... |
233 234 235 236 237 |
workspace->inf_strm.total_in = 0; workspace->inf_strm.total_out = 0; workspace->inf_strm.next_out = workspace->buf; workspace->inf_strm.avail_out = PAGE_CACHE_SIZE; |
c8b978188 Btrfs: Add zlib c... |
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
pg_offset = 0; /* If it's deflate, and it's got no preset dictionary, then we can tell zlib to skip the adler32 check. */ if (srclen > 2 && !(data_in[1] & PRESET_DICT) && ((data_in[0] & 0x0f) == Z_DEFLATED) && !(((data_in[0]<<8) + data_in[1]) % 31)) { wbits = -((data_in[0] >> 4) + 8); workspace->inf_strm.next_in += 2; workspace->inf_strm.avail_in -= 2; } if (Z_OK != zlib_inflateInit2(&workspace->inf_strm, wbits)) { printk(KERN_WARNING "inflateInit failed "); |
261507a02 btrfs: Allow to a... |
254 |
return -1; |
c8b978188 Btrfs: Add zlib c... |
255 |
} |
d397712bc Btrfs: Fix checkp... |
256 |
while (workspace->inf_strm.total_in < srclen) { |
c8b978188 Btrfs: Add zlib c... |
257 |
ret = zlib_inflate(&workspace->inf_strm, Z_NO_FLUSH); |
d397712bc Btrfs: Fix checkp... |
258 |
if (ret != Z_OK && ret != Z_STREAM_END) |
c8b978188 Btrfs: Add zlib c... |
259 |
break; |
c8b978188 Btrfs: Add zlib c... |
260 |
|
3a39c18d6 btrfs: Extract du... |
261 |
buf_start = total_out; |
c8b978188 Btrfs: Add zlib c... |
262 |
total_out = workspace->inf_strm.total_out; |
3a39c18d6 btrfs: Extract du... |
263 264 |
/* we didn't make progress in this inflate call, we're done */ if (buf_start == total_out) |
c8b978188 Btrfs: Add zlib c... |
265 |
break; |
c8b978188 Btrfs: Add zlib c... |
266 |
|
3a39c18d6 btrfs: Extract du... |
267 268 269 270 271 272 273 |
ret2 = btrfs_decompress_buf2page(workspace->buf, buf_start, total_out, disk_start, bvec, vcnt, &page_out_index, &pg_offset); if (ret2 == 0) { ret = 0; goto done; |
c8b978188 Btrfs: Add zlib c... |
274 |
} |
3a39c18d6 btrfs: Extract du... |
275 |
|
c8b978188 Btrfs: Add zlib c... |
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
workspace->inf_strm.next_out = workspace->buf; workspace->inf_strm.avail_out = PAGE_CACHE_SIZE; if (workspace->inf_strm.avail_in == 0) { unsigned long tmp; kunmap(pages_in[page_in_index]); page_in_index++; if (page_in_index >= total_pages_in) { data_in = NULL; break; } data_in = kmap(pages_in[page_in_index]); workspace->inf_strm.next_in = data_in; tmp = srclen - workspace->inf_strm.total_in; workspace->inf_strm.avail_in = min(tmp, PAGE_CACHE_SIZE); } } |
d397712bc Btrfs: Fix checkp... |
294 |
if (ret != Z_STREAM_END) |
c8b978188 Btrfs: Add zlib c... |
295 |
ret = -1; |
d397712bc Btrfs: Fix checkp... |
296 |
else |
c8b978188 Btrfs: Add zlib c... |
297 |
ret = 0; |
c8b978188 Btrfs: Add zlib c... |
298 299 300 301 |
done: zlib_inflateEnd(&workspace->inf_strm); if (data_in) kunmap(pages_in[page_in_index]); |
c8b978188 Btrfs: Add zlib c... |
302 303 |
return ret; } |
261507a02 btrfs: Allow to a... |
304 305 306 307 |
static int zlib_decompress(struct list_head *ws, unsigned char *data_in, struct page *dest_page, unsigned long start_byte, size_t srclen, size_t destlen) |
c8b978188 Btrfs: Add zlib c... |
308 |
{ |
261507a02 btrfs: Allow to a... |
309 |
struct workspace *workspace = list_entry(ws, struct workspace, list); |
c8b978188 Btrfs: Add zlib c... |
310 311 |
int ret = 0; int wbits = MAX_WBITS; |
c8b978188 Btrfs: Add zlib c... |
312 313 314 |
unsigned long bytes_left = destlen; unsigned long total_out = 0; char *kaddr; |
c8b978188 Btrfs: Add zlib c... |
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
workspace->inf_strm.next_in = data_in; workspace->inf_strm.avail_in = srclen; workspace->inf_strm.total_in = 0; workspace->inf_strm.next_out = workspace->buf; workspace->inf_strm.avail_out = PAGE_CACHE_SIZE; workspace->inf_strm.total_out = 0; /* If it's deflate, and it's got no preset dictionary, then we can tell zlib to skip the adler32 check. */ if (srclen > 2 && !(data_in[1] & PRESET_DICT) && ((data_in[0] & 0x0f) == Z_DEFLATED) && !(((data_in[0]<<8) + data_in[1]) % 31)) { wbits = -((data_in[0] >> 4) + 8); workspace->inf_strm.next_in += 2; workspace->inf_strm.avail_in -= 2; } if (Z_OK != zlib_inflateInit2(&workspace->inf_strm, wbits)) { printk(KERN_WARNING "inflateInit failed "); |
261507a02 btrfs: Allow to a... |
336 |
return -1; |
c8b978188 Btrfs: Add zlib c... |
337 |
} |
d397712bc Btrfs: Fix checkp... |
338 |
while (bytes_left > 0) { |
c8b978188 Btrfs: Add zlib c... |
339 340 341 342 343 344 |
unsigned long buf_start; unsigned long buf_offset; unsigned long bytes; unsigned long pg_offset = 0; ret = zlib_inflate(&workspace->inf_strm, Z_NO_FLUSH); |
d397712bc Btrfs: Fix checkp... |
345 |
if (ret != Z_OK && ret != Z_STREAM_END) |
c8b978188 Btrfs: Add zlib c... |
346 |
break; |
c8b978188 Btrfs: Add zlib c... |
347 348 349 350 351 352 353 354 |
buf_start = total_out; total_out = workspace->inf_strm.total_out; if (total_out == buf_start) { ret = -1; break; } |
d397712bc Btrfs: Fix checkp... |
355 |
if (total_out <= start_byte) |
c8b978188 Btrfs: Add zlib c... |
356 |
goto next; |
c8b978188 Btrfs: Add zlib c... |
357 |
|
d397712bc Btrfs: Fix checkp... |
358 |
if (total_out > start_byte && buf_start < start_byte) |
c8b978188 Btrfs: Add zlib c... |
359 |
buf_offset = start_byte - buf_start; |
d397712bc Btrfs: Fix checkp... |
360 |
else |
c8b978188 Btrfs: Add zlib c... |
361 |
buf_offset = 0; |
c8b978188 Btrfs: Add zlib c... |
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
bytes = min(PAGE_CACHE_SIZE - pg_offset, PAGE_CACHE_SIZE - buf_offset); bytes = min(bytes, bytes_left); kaddr = kmap_atomic(dest_page, KM_USER0); memcpy(kaddr + pg_offset, workspace->buf + buf_offset, bytes); kunmap_atomic(kaddr, KM_USER0); pg_offset += bytes; bytes_left -= bytes; next: workspace->inf_strm.next_out = workspace->buf; workspace->inf_strm.avail_out = PAGE_CACHE_SIZE; } |
d397712bc Btrfs: Fix checkp... |
377 378 |
if (ret != Z_STREAM_END && bytes_left != 0) |
c8b978188 Btrfs: Add zlib c... |
379 |
ret = -1; |
d397712bc Btrfs: Fix checkp... |
380 |
else |
c8b978188 Btrfs: Add zlib c... |
381 |
ret = 0; |
d397712bc Btrfs: Fix checkp... |
382 |
|
c8b978188 Btrfs: Add zlib c... |
383 |
zlib_inflateEnd(&workspace->inf_strm); |
c8b978188 Btrfs: Add zlib c... |
384 385 |
return ret; } |
261507a02 btrfs: Allow to a... |
386 387 388 389 390 391 392 |
struct btrfs_compress_op btrfs_zlib_compress = { .alloc_workspace = zlib_alloc_workspace, .free_workspace = zlib_free_workspace, .compress_pages = zlib_compress_pages, .decompress_biovec = zlib_decompress_biovec, .decompress = zlib_decompress, }; |