Blame view

fs/btrfs/zlib.c 10.4 KB
c8b978188   Chris Mason   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   Christoph Hellwig   Btrfs: make thing...
33
  #include "compression.h"
c8b978188   Chris Mason   Btrfs: Add zlib c...
34

c8b978188   Chris Mason   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   Li Zefan   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   Chris Mason   Btrfs: Add zlib c...
44

261507a02   Li Zefan   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   Chris Mason   Btrfs: Add zlib c...
52
53
  {
  	struct workspace *workspace;
8844355df   Li Zefan   btrfs: Fix bugs i...
54

c8b978188   Chris Mason   Btrfs: Add zlib c...
55
  	workspace = kzalloc(sizeof(*workspace), GFP_NOFS);
261507a02   Li Zefan   btrfs: Allow to a...
56
57
  	if (!workspace)
  		return ERR_PTR(-ENOMEM);
c8b978188   Chris Mason   Btrfs: Add zlib c...
58

565d76cb7   Jim Keniston   zlib: slim down z...
59
60
  	workspace->def_strm.workspace = vmalloc(zlib_deflate_workspacesize(
  						MAX_WBITS, MAX_MEM_LEVEL));
c8b978188   Chris Mason   Btrfs: Add zlib c...
61
  	workspace->inf_strm.workspace = vmalloc(zlib_inflate_workspacesize());
c8b978188   Chris Mason   Btrfs: Add zlib c...
62
  	workspace->buf = kmalloc(PAGE_CACHE_SIZE, GFP_NOFS);
261507a02   Li Zefan   btrfs: Allow to a...
63
64
65
  	if (!workspace->def_strm.workspace ||
  	    !workspace->inf_strm.workspace || !workspace->buf)
  		goto fail;
c8b978188   Chris Mason   Btrfs: Add zlib c...
66

261507a02   Li Zefan   btrfs: Allow to a...
67
  	INIT_LIST_HEAD(&workspace->list);
c8b978188   Chris Mason   Btrfs: Add zlib c...
68

261507a02   Li Zefan   btrfs: Allow to a...
69
70
71
72
  	return &workspace->list;
  fail:
  	zlib_free_workspace(&workspace->list);
  	return ERR_PTR(-ENOMEM);
c8b978188   Chris Mason   Btrfs: Add zlib c...
73
  }
261507a02   Li Zefan   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   Chris Mason   Btrfs: Add zlib c...
83
  {
261507a02   Li Zefan   btrfs: Allow to a...
84
  	struct workspace *workspace = list_entry(ws, struct workspace, list);
c8b978188   Chris Mason   Btrfs: Add zlib c...
85
  	int ret;
c8b978188   Chris Mason   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   Chris Mason   Btrfs: Add zlib c...
91
92
93
94
95
  	unsigned long bytes_left;
  
  	*out_pages = 0;
  	*total_out = 0;
  	*total_in = 0;
c8b978188   Chris Mason   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   Li Zefan   btrfs: Fix error ...
110
111
112
113
  	if (out_page == NULL) {
  		ret = -1;
  		goto out;
  	}
c8b978188   Chris Mason   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   Chris Mason   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   Li Zefan   btrfs: Fix error ...
152
153
154
155
  			if (out_page == NULL) {
  				ret = -1;
  				goto out;
  			}
c8b978188   Chris Mason   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   Chris Mason   Btrfs: Add zlib c...
210
211
  	return ret;
  }
261507a02   Li Zefan   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   Chris Mason   Btrfs: Add zlib c...
217
  {
261507a02   Li Zefan   btrfs: Allow to a...
218
  	struct workspace *workspace = list_entry(ws, struct workspace, list);
3a39c18d6   Li Zefan   btrfs: Extract du...
219
  	int ret = 0, ret2;
c8b978188   Chris Mason   Btrfs: Add zlib c...
220
  	int wbits = MAX_WBITS;
c8b978188   Chris Mason   Btrfs: Add zlib c...
221
222
  	char *data_in;
  	size_t total_out = 0;
c8b978188   Chris Mason   Btrfs: Add zlib c...
223
224
  	unsigned long page_in_index = 0;
  	unsigned long page_out_index = 0;
c8b978188   Chris Mason   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   Chris Mason   Btrfs: Add zlib c...
228
  	unsigned long pg_offset;
c8b978188   Chris Mason   Btrfs: Add zlib c...
229

c8b978188   Chris Mason   Btrfs: Add zlib c...
230
231
  	data_in = kmap(pages_in[page_in_index]);
  	workspace->inf_strm.next_in = data_in;
5b050f04c   Chris Mason   Btrfs: Fix compil...
232
  	workspace->inf_strm.avail_in = min_t(size_t, srclen, PAGE_CACHE_SIZE);
c8b978188   Chris Mason   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   Chris Mason   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   Li Zefan   btrfs: Allow to a...
254
  		return -1;
c8b978188   Chris Mason   Btrfs: Add zlib c...
255
  	}
d397712bc   Chris Mason   Btrfs: Fix checkp...
256
  	while (workspace->inf_strm.total_in < srclen) {
c8b978188   Chris Mason   Btrfs: Add zlib c...
257
  		ret = zlib_inflate(&workspace->inf_strm, Z_NO_FLUSH);
d397712bc   Chris Mason   Btrfs: Fix checkp...
258
  		if (ret != Z_OK && ret != Z_STREAM_END)
c8b978188   Chris Mason   Btrfs: Add zlib c...
259
  			break;
c8b978188   Chris Mason   Btrfs: Add zlib c...
260

3a39c18d6   Li Zefan   btrfs: Extract du...
261
  		buf_start = total_out;
c8b978188   Chris Mason   Btrfs: Add zlib c...
262
  		total_out = workspace->inf_strm.total_out;
3a39c18d6   Li Zefan   btrfs: Extract du...
263
264
  		/* we didn't make progress in this inflate call, we're done */
  		if (buf_start == total_out)
c8b978188   Chris Mason   Btrfs: Add zlib c...
265
  			break;
c8b978188   Chris Mason   Btrfs: Add zlib c...
266

3a39c18d6   Li Zefan   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   Chris Mason   Btrfs: Add zlib c...
274
  		}
3a39c18d6   Li Zefan   btrfs: Extract du...
275

c8b978188   Chris Mason   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   Chris Mason   Btrfs: Fix checkp...
294
  	if (ret != Z_STREAM_END)
c8b978188   Chris Mason   Btrfs: Add zlib c...
295
  		ret = -1;
d397712bc   Chris Mason   Btrfs: Fix checkp...
296
  	else
c8b978188   Chris Mason   Btrfs: Add zlib c...
297
  		ret = 0;
c8b978188   Chris Mason   Btrfs: Add zlib c...
298
299
300
301
  done:
  	zlib_inflateEnd(&workspace->inf_strm);
  	if (data_in)
  		kunmap(pages_in[page_in_index]);
c8b978188   Chris Mason   Btrfs: Add zlib c...
302
303
  	return ret;
  }
261507a02   Li Zefan   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   Chris Mason   Btrfs: Add zlib c...
308
  {
261507a02   Li Zefan   btrfs: Allow to a...
309
  	struct workspace *workspace = list_entry(ws, struct workspace, list);
c8b978188   Chris Mason   Btrfs: Add zlib c...
310
311
  	int ret = 0;
  	int wbits = MAX_WBITS;
c8b978188   Chris Mason   Btrfs: Add zlib c...
312
313
314
  	unsigned long bytes_left = destlen;
  	unsigned long total_out = 0;
  	char *kaddr;
c8b978188   Chris Mason   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   Li Zefan   btrfs: Allow to a...
336
  		return -1;
c8b978188   Chris Mason   Btrfs: Add zlib c...
337
  	}
d397712bc   Chris Mason   Btrfs: Fix checkp...
338
  	while (bytes_left > 0) {
c8b978188   Chris Mason   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   Chris Mason   Btrfs: Fix checkp...
345
  		if (ret != Z_OK && ret != Z_STREAM_END)
c8b978188   Chris Mason   Btrfs: Add zlib c...
346
  			break;
c8b978188   Chris Mason   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   Chris Mason   Btrfs: Fix checkp...
355
  		if (total_out <= start_byte)
c8b978188   Chris Mason   Btrfs: Add zlib c...
356
  			goto next;
c8b978188   Chris Mason   Btrfs: Add zlib c...
357

d397712bc   Chris Mason   Btrfs: Fix checkp...
358
  		if (total_out > start_byte && buf_start < start_byte)
c8b978188   Chris Mason   Btrfs: Add zlib c...
359
  			buf_offset = start_byte - buf_start;
d397712bc   Chris Mason   Btrfs: Fix checkp...
360
  		else
c8b978188   Chris Mason   Btrfs: Add zlib c...
361
  			buf_offset = 0;
c8b978188   Chris Mason   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   Chris Mason   Btrfs: Fix checkp...
377
378
  
  	if (ret != Z_STREAM_END && bytes_left != 0)
c8b978188   Chris Mason   Btrfs: Add zlib c...
379
  		ret = -1;
d397712bc   Chris Mason   Btrfs: Fix checkp...
380
  	else
c8b978188   Chris Mason   Btrfs: Add zlib c...
381
  		ret = 0;
d397712bc   Chris Mason   Btrfs: Fix checkp...
382

c8b978188   Chris Mason   Btrfs: Add zlib c...
383
  	zlib_inflateEnd(&workspace->inf_strm);
c8b978188   Chris Mason   Btrfs: Add zlib c...
384
385
  	return ret;
  }
261507a02   Li Zefan   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,
  };