linux/fs/jffs2/compr.c
<<
>>
Prefs
   1/*
   2 * JFFS2 -- Journalling Flash File System, Version 2.
   3 *
   4 * Copyright \xC2\xA9 2001-2007 Red Hat, Inc.
   5 * Created by Arjan van de Ven <arjanv@redhat.com>
   6 *
   7 * Copyright \xC2\xA9 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
   8 *                  University of Szeged, Hungary
   9 *
  10 * For licensing information, see the file 'LICENCE' in this directory.
  11 *
  12 */
  13
  14#include "compr.h"
  15
  16static DEFINE_SPINLOCK(jffs2_compressor_list_lock);
  17
  18/* Available compressors are on this list */
  19static LIST_HEAD(jffs2_compressor_list);
  20
  21/* Actual compression mode */
  22static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
  23
  24/* Statistics for blocks stored without compression */
  25static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0;
  26
  27
  28/*
  29 * Return 1 to use this compression
  30 */
  31static int jffs2_is_best_compression(struct jffs2_compressor *this,
  32                struct jffs2_compressor *best, uint32_t size, uint32_t bestsize)
  33{
  34        switch (jffs2_compression_mode) {
  35        case JFFS2_COMPR_MODE_SIZE:
  36                if (bestsize > size)
  37                        return 1;
  38                return 0;
  39        case JFFS2_COMPR_MODE_FAVOURLZO:
  40                if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > size))
  41                        return 1;
  42                if ((best->compr != JFFS2_COMPR_LZO) && (bestsize > size))
  43                        return 1;
  44                if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > (size * FAVOUR_LZO_PERCENT / 100)))
  45                        return 1;
  46                if ((bestsize * FAVOUR_LZO_PERCENT / 100) > size)
  47                        return 1;
  48
  49                return 0;
  50        }
  51        /* Shouldn't happen */
  52        return 0;
  53}
  54
  55/* jffs2_compress:
  56 * @data: Pointer to uncompressed data
  57 * @cdata: Pointer to returned pointer to buffer for compressed data
  58 * @datalen: On entry, holds the amount of data available for compression.
  59 *      On exit, expected to hold the amount of data actually compressed.
  60 * @cdatalen: On entry, holds the amount of space available for compressed
  61 *      data. On exit, expected to hold the actual size of the compressed
  62 *      data.
  63 *
  64 * Returns: Lower byte to be stored with data indicating compression type used.
  65 * Zero is used to show that the data could not be compressed - the
  66 * compressed version was actually larger than the original.
  67 * Upper byte will be used later. (soon)
  68 *
  69 * If the cdata buffer isn't large enough to hold all the uncompressed data,
  70 * jffs2_compress should compress as much as will fit, and should set
  71 * *datalen accordingly to show the amount of data which were compressed.
  72 */
  73uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
  74                        unsigned char *data_in, unsigned char **cpage_out,
  75                        uint32_t *datalen, uint32_t *cdatalen)
  76{
  77        int ret = JFFS2_COMPR_NONE;
  78        int compr_ret;
  79        struct jffs2_compressor *this, *best=NULL;
  80        unsigned char *output_buf = NULL, *tmp_buf;
  81        uint32_t orig_slen, orig_dlen;
  82        uint32_t best_slen=0, best_dlen=0;
  83
  84        switch (jffs2_compression_mode) {
  85        case JFFS2_COMPR_MODE_NONE:
  86                break;
  87        case JFFS2_COMPR_MODE_PRIORITY:
  88                output_buf = kmalloc(*cdatalen,GFP_KERNEL);
  89                if (!output_buf) {
  90                        printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n");
  91                        goto out;
  92                }
  93                orig_slen = *datalen;
  94                orig_dlen = *cdatalen;
  95                spin_lock(&jffs2_compressor_list_lock);
  96                list_for_each_entry(this, &jffs2_compressor_list, list) {
  97                        /* Skip decompress-only backwards-compatibility and disabled modules */
  98                        if ((!this->compress)||(this->disabled))
  99                                continue;
 100
 101                        this->usecount++;
 102                        spin_unlock(&jffs2_compressor_list_lock);
 103                        *datalen  = orig_slen;
 104                        *cdatalen = orig_dlen;
 105                        compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL);
 106                        spin_lock(&jffs2_compressor_list_lock);
 107                        this->usecount--;
 108                        if (!compr_ret) {
 109                                ret = this->compr;
 110                                this->stat_compr_blocks++;
 111                                this->stat_compr_orig_size += *datalen;
 112                                this->stat_compr_new_size  += *cdatalen;
 113                                break;
 114                        }
 115                }
 116                spin_unlock(&jffs2_compressor_list_lock);
 117                if (ret == JFFS2_COMPR_NONE)
 118                        kfree(output_buf);
 119                break;
 120        case JFFS2_COMPR_MODE_SIZE:
 121        case JFFS2_COMPR_MODE_FAVOURLZO:
 122                orig_slen = *datalen;
 123                orig_dlen = *cdatalen;
 124                spin_lock(&jffs2_compressor_list_lock);
 125                list_for_each_entry(this, &jffs2_compressor_list, list) {
 126                        /* Skip decompress-only backwards-compatibility and disabled modules */
 127                        if ((!this->compress)||(this->disabled))
 128                                continue;
 129                        /* Allocating memory for output buffer if necessary */
 130                        if ((this->compr_buf_size < orig_slen) && (this->compr_buf)) {
 131                                spin_unlock(&jffs2_compressor_list_lock);
 132                                kfree(this->compr_buf);
 133                                spin_lock(&jffs2_compressor_list_lock);
 134                                this->compr_buf_size=0;
 135                                this->compr_buf=NULL;
 136                        }
 137                        if (!this->compr_buf) {
 138                                spin_unlock(&jffs2_compressor_list_lock);
 139                                tmp_buf = kmalloc(orig_slen, GFP_KERNEL);
 140                                spin_lock(&jffs2_compressor_list_lock);
 141                                if (!tmp_buf) {
 142                                        printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n", orig_slen);
 143                                        continue;
 144                                }
 145                                else {
 146                                        this->compr_buf = tmp_buf;
 147                                        this->compr_buf_size = orig_slen;
 148                                }
 149                        }
 150                        this->usecount++;
 151                        spin_unlock(&jffs2_compressor_list_lock);
 152                        *datalen  = orig_slen;
 153                        *cdatalen = orig_dlen;
 154                        compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL);
 155                        spin_lock(&jffs2_compressor_list_lock);
 156                        this->usecount--;
 157                        if (!compr_ret) {
 158                                if (((!best_dlen) || jffs2_is_best_compression(this, best, *cdatalen, best_dlen))
 159                                                && (*cdatalen < *datalen)) {
 160                                        best_dlen = *cdatalen;
 161                                        best_slen = *datalen;
 162                                        best = this;
 163                                }
 164                        }
 165                }
 166                if (best_dlen) {
 167                        *cdatalen = best_dlen;
 168                        *datalen  = best_slen;
 169                        output_buf = best->compr_buf;
 170                        best->compr_buf = NULL;
 171                        best->compr_buf_size = 0;
 172                        best->stat_compr_blocks++;
 173                        best->stat_compr_orig_size += best_slen;
 174                        best->stat_compr_new_size  += best_dlen;
 175                        ret = best->compr;
 176                }
 177                spin_unlock(&jffs2_compressor_list_lock);
 178                break;
 179        default:
 180                printk(KERN_ERR "JFFS2: unknow compression mode.\n");
 181        }
 182 out:
 183        if (ret == JFFS2_COMPR_NONE) {
 184                *cpage_out = data_in;
 185                *datalen = *cdatalen;
 186                none_stat_compr_blocks++;
 187                none_stat_compr_size += *datalen;
 188        }
 189        else {
 190                *cpage_out = output_buf;
 191        }
 192        return ret;
 193}
 194
 195int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
 196                     uint16_t comprtype, unsigned char *cdata_in,
 197                     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen)
 198{
 199        struct jffs2_compressor *this;
 200        int ret;
 201
 202        /* Older code had a bug where it would write non-zero 'usercompr'
 203           fields. Deal with it. */
 204        if ((comprtype & 0xff) <= JFFS2_COMPR_ZLIB)
 205                comprtype &= 0xff;
 206
 207        switch (comprtype & 0xff) {
 208        case JFFS2_COMPR_NONE:
 209                /* This should be special-cased elsewhere, but we might as well deal with it */
 210                memcpy(data_out, cdata_in, datalen);
 211                none_stat_decompr_blocks++;
 212                break;
 213        case JFFS2_COMPR_ZERO:
 214                memset(data_out, 0, datalen);
 215                break;
 216        default:
 217                spin_lock(&jffs2_compressor_list_lock);
 218                list_for_each_entry(this, &jffs2_compressor_list, list) {
 219                        if (comprtype == this->compr) {
 220                                this->usecount++;
 221                                spin_unlock(&jffs2_compressor_list_lock);
 222                                ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL);
 223                                spin_lock(&jffs2_compressor_list_lock);
 224                                if (ret) {
 225                                        printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret);
 226                                }
 227                                else {
 228                                        this->stat_decompr_blocks++;
 229                                }
 230                                this->usecount--;
 231                                spin_unlock(&jffs2_compressor_list_lock);
 232                                return ret;
 233                        }
 234                }
 235                printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype);
 236                spin_unlock(&jffs2_compressor_list_lock);
 237                return -EIO;
 238        }
 239        return 0;
 240}
 241
 242int jffs2_register_compressor(struct jffs2_compressor *comp)
 243{
 244        struct jffs2_compressor *this;
 245
 246        if (!comp->name) {
 247                printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n");
 248                return -1;
 249        }
 250        comp->compr_buf_size=0;
 251        comp->compr_buf=NULL;
 252        comp->usecount=0;
 253        comp->stat_compr_orig_size=0;
 254        comp->stat_compr_new_size=0;
 255        comp->stat_compr_blocks=0;
 256        comp->stat_decompr_blocks=0;
 257        D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name));
 258
 259        spin_lock(&jffs2_compressor_list_lock);
 260
 261        list_for_each_entry(this, &jffs2_compressor_list, list) {
 262                if (this->priority < comp->priority) {
 263                        list_add(&comp->list, this->list.prev);
 264                        goto out;
 265                }
 266        }
 267        list_add_tail(&comp->list, &jffs2_compressor_list);
 268out:
 269        D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
 270                printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
 271        })
 272
 273        spin_unlock(&jffs2_compressor_list_lock);
 274
 275        return 0;
 276}
 277
 278int jffs2_unregister_compressor(struct jffs2_compressor *comp)
 279{
 280        D2(struct jffs2_compressor *this;)
 281
 282        D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name));
 283
 284        spin_lock(&jffs2_compressor_list_lock);
 285
 286        if (comp->usecount) {
 287                spin_unlock(&jffs2_compressor_list_lock);
 288                printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n");
 289                return -1;
 290        }
 291        list_del(&comp->list);
 292
 293        D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
 294                printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
 295        })
 296        spin_unlock(&jffs2_compressor_list_lock);
 297        return 0;
 298}
 299
 300void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
 301{
 302        if (orig != comprbuf)
 303                kfree(comprbuf);
 304}
 305
 306int __init jffs2_compressors_init(void)
 307{
 308/* Registering compressors */
 309#ifdef CONFIG_JFFS2_ZLIB
 310        jffs2_zlib_init();
 311#endif
 312#ifdef CONFIG_JFFS2_RTIME
 313        jffs2_rtime_init();
 314#endif
 315#ifdef CONFIG_JFFS2_RUBIN
 316        jffs2_rubinmips_init();
 317        jffs2_dynrubin_init();
 318#endif
 319#ifdef CONFIG_JFFS2_LZO
 320        jffs2_lzo_init();
 321#endif
 322/* Setting default compression mode */
 323#ifdef CONFIG_JFFS2_CMODE_NONE
 324        jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
 325        D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");)
 326#else
 327#ifdef CONFIG_JFFS2_CMODE_SIZE
 328        jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
 329        D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");)
 330#else
 331#ifdef CONFIG_JFFS2_CMODE_FAVOURLZO
 332        jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO;
 333        D1(printk(KERN_INFO "JFFS2: default compression mode: favourlzo\n");)
 334#else
 335        D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");)
 336#endif
 337#endif
 338#endif
 339        return 0;
 340}
 341
 342int jffs2_compressors_exit(void)
 343{
 344/* Unregistering compressors */
 345#ifdef CONFIG_JFFS2_LZO
 346        jffs2_lzo_exit();
 347#endif
 348#ifdef CONFIG_JFFS2_RUBIN
 349        jffs2_dynrubin_exit();
 350        jffs2_rubinmips_exit();
 351#endif
 352#ifdef CONFIG_JFFS2_RTIME
 353        jffs2_rtime_exit();
 354#endif
 355#ifdef CONFIG_JFFS2_ZLIB
 356        jffs2_zlib_exit();
 357#endif
 358        return 0;
 359}
 360