LCOV - code coverage report
Current view: top level - fs/btrfs - lzo.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 140 0.0 %
Date: 2014-02-18 Functions: 0 5 0.0 %
Branches: 0 74 0.0 %

           Branch data     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                 :          0 : static struct list_head *lzo_alloc_workspace(void)
      50                 :            : {
      51                 :            :         struct workspace *workspace;
      52                 :            : 
      53                 :            :         workspace = kzalloc(sizeof(*workspace), GFP_NOFS);
      54         [ #  # ]:          0 :         if (!workspace)
      55                 :            :                 return ERR_PTR(-ENOMEM);
      56                 :            : 
      57                 :          0 :         workspace->mem = vmalloc(LZO1X_MEM_COMPRESS);
      58                 :          0 :         workspace->buf = vmalloc(lzo1x_worst_compress(PAGE_CACHE_SIZE));
      59                 :          0 :         workspace->cbuf = vmalloc(lzo1x_worst_compress(PAGE_CACHE_SIZE));
      60 [ #  # ][ #  # ]:          0 :         if (!workspace->mem || !workspace->buf || !workspace->cbuf)
                 [ #  # ]
      61                 :            :                 goto fail;
      62                 :            : 
      63                 :          0 :         INIT_LIST_HEAD(&workspace->list);
      64                 :            : 
      65                 :          0 :         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                 :          0 :         dlen = cpu_to_le32(len);
      76                 :          0 :         memcpy(buf, &dlen, LZO_LEN);
      77                 :            : }
      78                 :            : 
      79                 :            : static inline size_t read_compress_length(char *buf)
      80                 :            : {
      81                 :            :         __le32 dlen;
      82                 :            : 
      83                 :          0 :         memcpy(&dlen, buf, LZO_LEN);
      84                 :          0 :         return le32_to_cpu(dlen);
      85                 :            : }
      86                 :            : 
      87                 :          0 : 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                 :          0 :         *out_pages = 0;
     116                 :          0 :         *total_out = 0;
     117                 :          0 :         *total_in = 0;
     118                 :            : 
     119                 :          0 :         in_page = find_get_page(mapping, start >> PAGE_CACHE_SHIFT);
     120                 :          0 :         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         [ #  # ]:          0 :         if (out_page == NULL) {
     128                 :            :                 ret = -ENOMEM;
     129                 :            :                 goto out;
     130                 :            :         }
     131                 :          0 :         cpage_out = kmap(out_page);
     132                 :            :         out_offset = LZO_LEN;
     133                 :            :         tot_out = LZO_LEN;
     134                 :          0 :         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                 :          0 :         in_len = min(len, PAGE_CACHE_SIZE);
     140         [ #  # ]:          0 :         while (tot_in < len) {
     141                 :          0 :                 ret = lzo1x_1_compress(data_in, in_len, workspace->cbuf,
     142                 :            :                                        &out_len, workspace->mem);
     143         [ #  # ]:          0 :                 if (ret != LZO_E_OK) {
     144                 :          0 :                         printk(KERN_DEBUG "btrfs deflate in loop returned %d\n",
     145                 :            :                                ret);
     146                 :            :                         ret = -1;
     147                 :          0 :                         goto out;
     148                 :            :                 }
     149                 :            : 
     150                 :            :                 /* store the size of this chunk of compressed data */
     151                 :          0 :                 write_compress_length(cpage_out + out_offset, out_len);
     152                 :          0 :                 tot_out += LZO_LEN;
     153                 :          0 :                 out_offset += LZO_LEN;
     154                 :          0 :                 pg_bytes_left -= LZO_LEN;
     155                 :            : 
     156                 :          0 :                 tot_in += in_len;
     157                 :          0 :                 tot_out += out_len;
     158                 :            : 
     159                 :            :                 /* copy bytes from the working buffer into the pages */
     160                 :          0 :                 buf = workspace->cbuf;
     161         [ #  # ]:          0 :                 while (out_len) {
     162                 :          0 :                         bytes = min_t(unsigned long, pg_bytes_left, out_len);
     163                 :            : 
     164                 :          0 :                         memcpy(cpage_out + out_offset, buf, bytes);
     165                 :            : 
     166                 :          0 :                         out_len -= bytes;
     167                 :          0 :                         pg_bytes_left -= bytes;
     168                 :          0 :                         buf += bytes;
     169                 :          0 :                         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 [ #  # ][ #  # ]:          0 :                         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                 :          0 :                                 kunmap(out_page);
     190         [ #  # ]:          0 :                                 if (nr_pages == nr_dest_pages) {
     191                 :            :                                         out_page = NULL;
     192                 :            :                                         ret = -1;
     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                 :          0 :                                 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         [ #  # ]:          0 :                 if (tot_in > 8192 && tot_in < tot_out) {
     211                 :            :                         ret = -1;
     212                 :            :                         goto out;
     213                 :            :                 }
     214                 :            : 
     215                 :            :                 /* we're all done */
     216         [ #  # ]:          0 :                 if (tot_in >= len)
     217                 :            :                         break;
     218                 :            : 
     219         [ #  # ]:          0 :                 if (tot_out > max_out)
     220                 :            :                         break;
     221                 :            : 
     222                 :          0 :                 bytes_left = len - tot_in;
     223                 :          0 :                 kunmap(in_page);
     224                 :          0 :                 page_cache_release(in_page);
     225                 :            : 
     226                 :          0 :                 start += PAGE_CACHE_SIZE;
     227                 :          0 :                 in_page = find_get_page(mapping, start >> PAGE_CACHE_SHIFT);
     228                 :          0 :                 data_in = kmap(in_page);
     229                 :          0 :                 in_len = min(bytes_left, PAGE_CACHE_SIZE);
     230                 :            :         }
     231                 :            : 
     232         [ #  # ]:          0 :         if (tot_out > tot_in)
     233                 :            :                 goto out;
     234                 :            : 
     235                 :            :         /* store the size of all chunks of compressed data */
     236                 :          0 :         cpage_out = kmap(pages[0]);
     237                 :            :         write_compress_length(cpage_out, tot_out);
     238                 :            : 
     239                 :          0 :         kunmap(pages[0]);
     240                 :            : 
     241                 :            :         ret = 0;
     242                 :          0 :         *total_out = tot_out;
     243                 :          0 :         *total_in = tot_in;
     244                 :            : out:
     245                 :          0 :         *out_pages = nr_pages;
     246         [ #  # ]:          0 :         if (out_page)
     247                 :          0 :                 kunmap(out_page);
     248                 :            : 
     249         [ #  # ]:          0 :         if (in_page) {
     250                 :          0 :                 kunmap(in_page);
     251                 :          0 :                 page_cache_release(in_page);
     252                 :            :         }
     253                 :            : 
     254                 :          0 :         return ret;
     255                 :            : }
     256                 :            : 
     257                 :          0 : 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                 :          0 :         unsigned long page_out_index = 0;
     269                 :          0 :         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                 :          0 :         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                 :          0 :         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                 :          0 :         pg_offset = 0;
     297                 :            : 
     298         [ #  # ]:          0 :         while (tot_in < tot_len) {
     299                 :          0 :                 in_len = read_compress_length(data_in + in_offset);
     300                 :          0 :                 in_page_bytes_left -= LZO_LEN;
     301                 :          0 :                 in_offset += LZO_LEN;
     302                 :          0 :                 tot_in += LZO_LEN;
     303                 :            : 
     304                 :          0 :                 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         [ #  # ]:          0 :                 if (in_page_bytes_left >= in_len) {
     310                 :          0 :                         buf = data_in + in_offset;
     311                 :            :                         bytes = in_len;
     312                 :            :                         may_late_unmap = true;
     313                 :          0 :                         goto cont;
     314                 :            :                 }
     315                 :            : 
     316                 :            :                 /* copy bytes from the pages into the working buffer */
     317                 :          0 :                 buf = workspace->cbuf;
     318                 :            :                 buf_offset = 0;
     319         [ #  # ]:          0 :                 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                 :          0 :                         working_bytes -= bytes;
     326                 :          0 :                         in_page_bytes_left -= bytes;
     327                 :          0 :                         in_offset += bytes;
     328                 :            : 
     329                 :            :                         /* check if we need to pick another page */
     330         [ #  # ]:          0 :                         if ((working_bytes == 0 && in_page_bytes_left < LZO_LEN)
     331         [ #  # ]:          0 :                             || 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 = -1;
     339                 :            :                                         goto done;
     340                 :            :                                 }
     341                 :            : 
     342         [ #  # ]:          0 :                                 if (may_late_unmap)
     343                 :            :                                         need_unmap = true;
     344                 :            :                                 else
     345                 :          0 :                                         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                 :          0 :                 out_len = lzo1x_worst_compress(PAGE_CACHE_SIZE);
     355                 :          0 :                 ret = lzo1x_decompress_safe(buf, in_len, workspace->buf,
     356                 :            :                                             &out_len);
     357         [ #  # ]:          0 :                 if (need_unmap)
     358                 :          0 :                         kunmap(pages_in[page_in_index - 1]);
     359         [ #  # ]:          0 :                 if (ret != LZO_E_OK) {
     360                 :          0 :                         printk(KERN_WARNING "btrfs decompress failed\n");
     361                 :            :                         ret = -1;
     362                 :          0 :                         break;
     363                 :            :                 }
     364                 :            : 
     365                 :            :                 buf_start = tot_out;
     366                 :          0 :                 tot_out += out_len;
     367                 :            : 
     368                 :          0 :                 ret2 = btrfs_decompress_buf2page(workspace->buf, buf_start,
     369                 :            :                                                  tot_out, disk_start,
     370                 :            :                                                  bvec, vcnt,
     371                 :            :                                                  &page_out_index, &pg_offset);
     372         [ #  # ]:          0 :                 if (ret2 == 0)
     373                 :            :                         break;
     374                 :            :         }
     375                 :            : done:
     376                 :          0 :         kunmap(pages_in[page_in_index]);
     377                 :          0 :         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                 :          0 :         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 = -1;
     406                 :          0 :                 goto out;
     407                 :            :         }
     408                 :            : 
     409         [ #  # ]:          0 :         if (out_len < start_byte) {
     410                 :            :                 ret = -1;
     411                 :            :                 goto out;
     412                 :            :         }
     413                 :            : 
     414                 :          0 :         bytes = min_t(unsigned long, destlen, out_len - start_byte);
     415                 :            : 
     416                 :          0 :         kaddr = kmap_atomic(dest_page);
     417                 :          0 :         memcpy(kaddr, workspace->buf + start_byte, bytes);
     418                 :          0 :         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                 :            : };

Generated by: LCOV version 1.9