Line data Source code
1 : /*
2 : * Copyright (C) 2008 Oracle. All rights reserved.
3 : *
4 : * This program is free software; you can redistribute it and/or
5 : * modify it under the terms of the GNU General Public
6 : * License v2 as published by the Free Software Foundation.
7 : *
8 : * This program is distributed in the hope that it will be useful,
9 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 : * General Public License for more details.
12 : *
13 : * You should have received a copy of the GNU General Public
14 : * License along with this program; if not, write to the
15 : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 : * Boston, MA 021110-1307, USA.
17 : */
18 :
19 : #include <linux/kernel.h>
20 : #include <linux/slab.h>
21 : #include <linux/vmalloc.h>
22 : #include <linux/init.h>
23 : #include <linux/err.h>
24 : #include <linux/sched.h>
25 : #include <linux/pagemap.h>
26 : #include <linux/bio.h>
27 : #include <linux/lzo.h>
28 : #include "compression.h"
29 :
30 : #define LZO_LEN 4
31 :
32 : struct workspace {
33 : void *mem;
34 : void *buf; /* where decompressed data goes */
35 : void *cbuf; /* where compressed data goes */
36 : struct list_head list;
37 : };
38 :
39 0 : static void lzo_free_workspace(struct list_head *ws)
40 : {
41 0 : struct workspace *workspace = list_entry(ws, struct workspace, list);
42 :
43 0 : vfree(workspace->buf);
44 0 : vfree(workspace->cbuf);
45 0 : vfree(workspace->mem);
46 0 : kfree(workspace);
47 0 : }
48 :
49 1 : static struct list_head *lzo_alloc_workspace(void)
50 : {
51 : struct workspace *workspace;
52 :
53 1 : workspace = kzalloc(sizeof(*workspace), GFP_NOFS);
54 1 : if (!workspace)
55 : return ERR_PTR(-ENOMEM);
56 :
57 1 : workspace->mem = vmalloc(LZO1X_MEM_COMPRESS);
58 1 : workspace->buf = vmalloc(lzo1x_worst_compress(PAGE_CACHE_SIZE));
59 1 : workspace->cbuf = vmalloc(lzo1x_worst_compress(PAGE_CACHE_SIZE));
60 1 : if (!workspace->mem || !workspace->buf || !workspace->cbuf)
61 : goto fail;
62 :
63 1 : INIT_LIST_HEAD(&workspace->list);
64 :
65 1 : return &workspace->list;
66 : fail:
67 0 : lzo_free_workspace(&workspace->list);
68 0 : return ERR_PTR(-ENOMEM);
69 : }
70 :
71 : static inline void write_compress_length(char *buf, size_t len)
72 : {
73 : __le32 dlen;
74 :
75 2131 : dlen = cpu_to_le32(len);
76 2131 : memcpy(buf, &dlen, LZO_LEN);
77 : }
78 :
79 : static inline size_t read_compress_length(char *buf)
80 : {
81 : __le32 dlen;
82 :
83 282 : memcpy(&dlen, buf, LZO_LEN);
84 282 : return le32_to_cpu(dlen);
85 : }
86 :
87 125 : static int lzo_compress_pages(struct list_head *ws,
88 : struct address_space *mapping,
89 : u64 start, unsigned long len,
90 : struct page **pages,
91 : unsigned long nr_dest_pages,
92 : unsigned long *out_pages,
93 : unsigned long *total_in,
94 : unsigned long *total_out,
95 : unsigned long max_out)
96 : {
97 : struct workspace *workspace = list_entry(ws, struct workspace, list);
98 : int ret = 0;
99 : char *data_in;
100 : char *cpage_out;
101 : int nr_pages = 0;
102 : struct page *in_page = NULL;
103 : struct page *out_page = NULL;
104 : unsigned long bytes_left;
105 :
106 : size_t in_len;
107 : size_t out_len;
108 : char *buf;
109 : unsigned long tot_in = 0;
110 : unsigned long tot_out = 0;
111 : unsigned long pg_bytes_left;
112 : unsigned long out_offset;
113 : unsigned long bytes;
114 :
115 125 : *out_pages = 0;
116 125 : *total_out = 0;
117 125 : *total_in = 0;
118 :
119 125 : in_page = find_get_page(mapping, start >> PAGE_CACHE_SHIFT);
120 : data_in = kmap(in_page);
121 :
122 : /*
123 : * store the size of all chunks of compressed data in
124 : * the first 4 bytes
125 : */
126 : out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM);
127 125 : if (out_page == NULL) {
128 : ret = -ENOMEM;
129 : goto out;
130 : }
131 : cpage_out = kmap(out_page);
132 : out_offset = LZO_LEN;
133 : tot_out = LZO_LEN;
134 125 : pages[0] = out_page;
135 : nr_pages = 1;
136 : pg_bytes_left = PAGE_CACHE_SIZE - LZO_LEN;
137 :
138 : /* compress at most one page of data each time */
139 125 : in_len = min(len, PAGE_CACHE_SIZE);
140 2131 : while (tot_in < len) {
141 2006 : ret = lzo1x_1_compress(data_in, in_len, workspace->cbuf,
142 : &out_len, workspace->mem);
143 2006 : if (ret != LZO_E_OK) {
144 0 : printk(KERN_DEBUG "BTRFS: deflate in loop returned %d\n",
145 : ret);
146 : ret = -EIO;
147 0 : goto out;
148 : }
149 :
150 : /* store the size of this chunk of compressed data */
151 2006 : write_compress_length(cpage_out + out_offset, out_len);
152 2006 : tot_out += LZO_LEN;
153 2006 : out_offset += LZO_LEN;
154 2006 : pg_bytes_left -= LZO_LEN;
155 :
156 2006 : tot_in += in_len;
157 2006 : tot_out += out_len;
158 :
159 : /* copy bytes from the working buffer into the pages */
160 2006 : buf = workspace->cbuf;
161 6018 : while (out_len) {
162 2006 : bytes = min_t(unsigned long, pg_bytes_left, out_len);
163 :
164 2006 : memcpy(cpage_out + out_offset, buf, bytes);
165 :
166 2006 : out_len -= bytes;
167 2006 : pg_bytes_left -= bytes;
168 2006 : buf += bytes;
169 2006 : out_offset += bytes;
170 :
171 : /*
172 : * we need another page for writing out.
173 : *
174 : * Note if there's less than 4 bytes left, we just
175 : * skip to a new page.
176 : */
177 2006 : if ((out_len == 0 && pg_bytes_left < LZO_LEN) ||
178 : pg_bytes_left == 0) {
179 0 : if (pg_bytes_left) {
180 0 : memset(cpage_out + out_offset, 0,
181 : pg_bytes_left);
182 0 : tot_out += pg_bytes_left;
183 : }
184 :
185 : /* we're done, don't allocate new page */
186 0 : if (out_len == 0 && tot_in >= len)
187 : break;
188 :
189 : kunmap(out_page);
190 0 : if (nr_pages == nr_dest_pages) {
191 : out_page = NULL;
192 : ret = -E2BIG;
193 : goto out;
194 : }
195 :
196 : out_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM);
197 0 : if (out_page == NULL) {
198 : ret = -ENOMEM;
199 : goto out;
200 : }
201 : cpage_out = kmap(out_page);
202 0 : pages[nr_pages++] = out_page;
203 :
204 : pg_bytes_left = PAGE_CACHE_SIZE;
205 : out_offset = 0;
206 : }
207 : }
208 :
209 : /* we're making it bigger, give up */
210 2006 : if (tot_in > 8192 && tot_in < tot_out) {
211 : ret = -E2BIG;
212 : goto out;
213 : }
214 :
215 : /* we're all done */
216 2006 : if (tot_in >= len)
217 : break;
218 :
219 1881 : if (tot_out > max_out)
220 : break;
221 :
222 1881 : bytes_left = len - tot_in;
223 : kunmap(in_page);
224 1881 : page_cache_release(in_page);
225 :
226 1881 : start += PAGE_CACHE_SIZE;
227 1881 : in_page = find_get_page(mapping, start >> PAGE_CACHE_SHIFT);
228 : data_in = kmap(in_page);
229 1881 : in_len = min(bytes_left, PAGE_CACHE_SIZE);
230 : }
231 :
232 125 : if (tot_out > tot_in)
233 : goto out;
234 :
235 : /* store the size of all chunks of compressed data */
236 125 : cpage_out = kmap(pages[0]);
237 : write_compress_length(cpage_out, tot_out);
238 :
239 : kunmap(pages[0]);
240 :
241 : ret = 0;
242 125 : *total_out = tot_out;
243 125 : *total_in = tot_in;
244 : out:
245 125 : *out_pages = nr_pages;
246 : if (out_page)
247 : kunmap(out_page);
248 :
249 125 : if (in_page) {
250 : kunmap(in_page);
251 125 : page_cache_release(in_page);
252 : }
253 :
254 125 : return ret;
255 : }
256 :
257 38 : static int lzo_decompress_biovec(struct list_head *ws,
258 : struct page **pages_in,
259 : u64 disk_start,
260 : struct bio_vec *bvec,
261 : int vcnt,
262 : size_t srclen)
263 : {
264 : struct workspace *workspace = list_entry(ws, struct workspace, list);
265 : int ret = 0, ret2;
266 : char *data_in;
267 : unsigned long page_in_index = 0;
268 38 : unsigned long page_out_index = 0;
269 38 : unsigned long total_pages_in = (srclen + PAGE_CACHE_SIZE - 1) /
270 : PAGE_CACHE_SIZE;
271 : unsigned long buf_start;
272 : unsigned long buf_offset = 0;
273 : unsigned long bytes;
274 : unsigned long working_bytes;
275 : unsigned long pg_offset;
276 :
277 : size_t in_len;
278 : size_t out_len;
279 : unsigned long in_offset;
280 : unsigned long in_page_bytes_left;
281 : unsigned long tot_in;
282 : unsigned long tot_out;
283 : unsigned long tot_len;
284 : char *buf;
285 : bool may_late_unmap, need_unmap;
286 :
287 38 : data_in = kmap(pages_in[0]);
288 : tot_len = read_compress_length(data_in);
289 :
290 : tot_in = LZO_LEN;
291 : in_offset = LZO_LEN;
292 38 : tot_len = min_t(size_t, srclen, tot_len);
293 : in_page_bytes_left = PAGE_CACHE_SIZE - LZO_LEN;
294 :
295 : tot_out = 0;
296 38 : pg_offset = 0;
297 :
298 312 : while (tot_in < tot_len) {
299 244 : in_len = read_compress_length(data_in + in_offset);
300 244 : in_page_bytes_left -= LZO_LEN;
301 244 : in_offset += LZO_LEN;
302 244 : tot_in += LZO_LEN;
303 :
304 244 : tot_in += in_len;
305 : working_bytes = in_len;
306 : may_late_unmap = need_unmap = false;
307 :
308 : /* fast path: avoid using the working buffer */
309 244 : if (in_page_bytes_left >= in_len) {
310 244 : buf = data_in + in_offset;
311 : bytes = in_len;
312 : may_late_unmap = true;
313 244 : goto cont;
314 : }
315 :
316 : /* copy bytes from the pages into the working buffer */
317 0 : buf = workspace->cbuf;
318 : buf_offset = 0;
319 244 : while (working_bytes) {
320 0 : bytes = min(working_bytes, in_page_bytes_left);
321 :
322 0 : memcpy(buf + buf_offset, data_in + in_offset, bytes);
323 0 : buf_offset += bytes;
324 : cont:
325 244 : working_bytes -= bytes;
326 244 : in_page_bytes_left -= bytes;
327 244 : in_offset += bytes;
328 :
329 : /* check if we need to pick another page */
330 244 : if ((working_bytes == 0 && in_page_bytes_left < LZO_LEN)
331 244 : || in_page_bytes_left == 0) {
332 0 : tot_in += in_page_bytes_left;
333 :
334 0 : if (working_bytes == 0 && tot_in >= tot_len)
335 : break;
336 :
337 0 : if (page_in_index + 1 >= total_pages_in) {
338 : ret = -EIO;
339 : goto done;
340 : }
341 :
342 : if (may_late_unmap)
343 : need_unmap = true;
344 : else
345 : kunmap(pages_in[page_in_index]);
346 :
347 0 : data_in = kmap(pages_in[++page_in_index]);
348 :
349 : in_page_bytes_left = PAGE_CACHE_SIZE;
350 : in_offset = 0;
351 : }
352 : }
353 :
354 244 : out_len = lzo1x_worst_compress(PAGE_CACHE_SIZE);
355 244 : ret = lzo1x_decompress_safe(buf, in_len, workspace->buf,
356 : &out_len);
357 : if (need_unmap)
358 : kunmap(pages_in[page_in_index - 1]);
359 244 : if (ret != LZO_E_OK) {
360 0 : printk(KERN_WARNING "BTRFS: decompress failed\n");
361 : ret = -EIO;
362 0 : break;
363 : }
364 :
365 : buf_start = tot_out;
366 244 : tot_out += out_len;
367 :
368 244 : ret2 = btrfs_decompress_buf2page(workspace->buf, buf_start,
369 : tot_out, disk_start,
370 : bvec, vcnt,
371 : &page_out_index, &pg_offset);
372 244 : if (ret2 == 0)
373 : break;
374 : }
375 : done:
376 : kunmap(pages_in[page_in_index]);
377 38 : return ret;
378 : }
379 :
380 0 : static int lzo_decompress(struct list_head *ws, unsigned char *data_in,
381 : struct page *dest_page,
382 : unsigned long start_byte,
383 : size_t srclen, size_t destlen)
384 : {
385 : struct workspace *workspace = list_entry(ws, struct workspace, list);
386 : size_t in_len;
387 : size_t out_len;
388 : size_t tot_len;
389 : int ret = 0;
390 : char *kaddr;
391 : unsigned long bytes;
392 :
393 0 : BUG_ON(srclen < LZO_LEN);
394 :
395 : tot_len = read_compress_length(data_in);
396 : data_in += LZO_LEN;
397 :
398 : in_len = read_compress_length(data_in);
399 0 : data_in += LZO_LEN;
400 :
401 0 : out_len = PAGE_CACHE_SIZE;
402 0 : ret = lzo1x_decompress_safe(data_in, in_len, workspace->buf, &out_len);
403 0 : if (ret != LZO_E_OK) {
404 0 : printk(KERN_WARNING "BTRFS: decompress failed!\n");
405 : ret = -EIO;
406 0 : goto out;
407 : }
408 :
409 0 : if (out_len < start_byte) {
410 : ret = -EIO;
411 : goto out;
412 : }
413 :
414 0 : bytes = min_t(unsigned long, destlen, out_len - start_byte);
415 :
416 : kaddr = kmap_atomic(dest_page);
417 0 : memcpy(kaddr, workspace->buf + start_byte, bytes);
418 : kunmap_atomic(kaddr);
419 : out:
420 0 : return ret;
421 : }
422 :
423 : struct btrfs_compress_op btrfs_lzo_compress = {
424 : .alloc_workspace = lzo_alloc_workspace,
425 : .free_workspace = lzo_free_workspace,
426 : .compress_pages = lzo_compress_pages,
427 : .decompress_biovec = lzo_decompress_biovec,
428 : .decompress = lzo_decompress,
429 : };
|