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