VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[linux-2.6.git] / fs / jffs2 / compr.c
index 471ec55..9e5138a 100644 (file)
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
  * Copyright (C) 2001-2003 Red Hat, Inc.
- *
  * Created by Arjan van de Ven <arjanv@redhat.com>
  *
+ * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ *                    University of Szeged, Hungary
+ *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: compr.c,v 1.27 2003/10/04 08:33:06 dwmw2 Exp $
+ * $Id: compr.c,v 1.41 2004/06/24 09:51:38 havasi Exp $
  *
  */
 
-#if defined(__KERNEL__) || defined (__ECOS)
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#else 
-#define KERN_DEBUG
-#define KERN_NOTICE
-#define KERN_WARNING
-#define printk printf
-#include <stdio.h>
-#include <stdint.h>
-#include <errno.h>
-#endif
+#include "compr.h"
+
+static spinlock_t jffs2_compressor_list_lock = SPIN_LOCK_UNLOCKED;
+
+/* Available compressors are on this list */
+static LIST_HEAD(jffs2_compressor_list);
+
+/* Actual compression mode */
+static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
 
-#include <linux/jffs2.h>
+void jffs2_set_compression_mode(int mode) 
+{
+        jffs2_compression_mode = mode;
+}
 
-int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
-void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
-int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
-void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
-int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
-void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
-int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
-void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
+int jffs2_get_compression_mode(void)
+{
+        return jffs2_compression_mode;
+}
 
+/* Statistics for blocks stored without compression */
+static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0;
 
 /* jffs2_compress:
  * @data: Pointer to uncompressed data
- * @cdata: Pointer to buffer for compressed data
+ * @cdata: Pointer to returned pointer to buffer for compressed data
  * @datalen: On entry, holds the amount of data available for compression.
  *     On exit, expected to hold the amount of data actually compressed.
  * @cdatalen: On entry, holds the amount of space available for compressed
  *     data. On exit, expected to hold the actual size of the compressed
  *     data.
  *
- * Returns: Byte to be stored with data indicating compression type used.
+ * Returns: Lower byte to be stored with data indicating compression type used.
  * Zero is used to show that the data could not be compressed - the 
  * compressed version was actually larger than the original.
+ * Upper byte will be used later. (soon)
  *
  * If the cdata buffer isn't large enough to hold all the uncompressed data,
  * jffs2_compress should compress as much as will fit, and should set 
  * *datalen accordingly to show the amount of data which were compressed.
  */
-unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, 
-                   uint32_t *datalen, uint32_t *cdatalen)
+uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+                            unsigned char *data_in, unsigned char **cpage_out, 
+                            uint32_t *datalen, uint32_t *cdatalen)
 {
-       int ret;
+       int ret = JFFS2_COMPR_NONE;
+        int compr_ret;
+        struct jffs2_compressor *this, *best=NULL;
+        unsigned char *output_buf = NULL, *tmp_buf;
+        uint32_t orig_slen, orig_dlen;
+        uint32_t best_slen=0, best_dlen=0;
 
-       ret = jffs2_zlib_compress(data_in, cpage_out, datalen, cdatalen);
-       if (!ret) {
-               return JFFS2_COMPR_ZLIB;
-       }
-#if 0 /* Disabled 23/9/1. With zlib it hardly ever gets a look in */
-       ret = jffs2_dynrubin_compress(data_in, cpage_out, datalen, cdatalen);
-       if (!ret) {
-               return JFFS2_COMPR_DYNRUBIN;
-       }
-#endif
-#if 0 /* Disabled 26/2/1. Obsoleted by dynrubin */
-       ret = jffs2_rubinmips_compress(data_in, cpage_out, datalen, cdatalen);
-       if (!ret) {
-               return JFFS2_COMPR_RUBINMIPS;
-       }
-#endif
-       /* rtime does manage to recompress already-compressed data */
-       ret = jffs2_rtime_compress(data_in, cpage_out, datalen, cdatalen);
-       if (!ret) {
-               return JFFS2_COMPR_RTIME;
-       }
-#if 0
-       /* We don't need to copy. Let the caller special-case the COMPR_NONE case. */
-       /* If we get here, no compression is going to work */
-       /* But we might want to use the fragmentation part -- Arjan */
-       memcpy(cpage_out,data_in,min(*datalen,*cdatalen));
-       if (*datalen > *cdatalen)
-               *datalen = *cdatalen;
-#endif         
-       return JFFS2_COMPR_NONE; /* We failed to compress */
+        switch (jffs2_compression_mode) {
+        case JFFS2_COMPR_MODE_NONE:
+                break;
+        case JFFS2_COMPR_MODE_PRIORITY:
+                output_buf = kmalloc(*cdatalen,GFP_KERNEL);
+                if (!output_buf) {
+                        printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n");
+                        goto out;
+                }
+                orig_slen = *datalen;
+                orig_dlen = *cdatalen;
+                spin_lock(&jffs2_compressor_list_lock);
+                list_for_each_entry(this, &jffs2_compressor_list, list) {
+                        /* Skip decompress-only backwards-compatibility and disabled modules */
+                        if ((!this->compress)||(this->disabled))
+                                continue;
 
+                        this->usecount++;
+                        spin_unlock(&jffs2_compressor_list_lock);
+                        *datalen  = orig_slen;
+                        *cdatalen = orig_dlen;
+                        compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL);
+                        spin_lock(&jffs2_compressor_list_lock);
+                        this->usecount--;
+                        if (!compr_ret) {
+                                ret = this->compr;
+                                this->stat_compr_blocks++;
+                                this->stat_compr_orig_size += *datalen;
+                                this->stat_compr_new_size  += *cdatalen;
+                                break;
+                        }
+                }
+                spin_unlock(&jffs2_compressor_list_lock);
+                if (ret == JFFS2_COMPR_NONE) kfree(output_buf);
+                break;
+        case JFFS2_COMPR_MODE_SIZE:
+                orig_slen = *datalen;
+                orig_dlen = *cdatalen;
+                spin_lock(&jffs2_compressor_list_lock);
+                list_for_each_entry(this, &jffs2_compressor_list, list) {
+                        /* Skip decompress-only backwards-compatibility and disabled modules */
+                        if ((!this->compress)||(this->disabled))
+                                continue;
+                        /* Allocating memory for output buffer if necessary */
+                        if ((this->compr_buf_size<orig_dlen)&&(this->compr_buf)) {
+                                spin_unlock(&jffs2_compressor_list_lock);
+                                kfree(this->compr_buf);
+                                spin_lock(&jffs2_compressor_list_lock);
+                                this->compr_buf_size=0;
+                                this->compr_buf=NULL;
+                        }
+                        if (!this->compr_buf) {
+                                spin_unlock(&jffs2_compressor_list_lock);
+                                tmp_buf = kmalloc(orig_dlen,GFP_KERNEL);
+                                spin_lock(&jffs2_compressor_list_lock);
+                                if (!tmp_buf) {
+                                        printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n",orig_dlen);
+                                        continue;
+                                }
+                                else {
+                                        this->compr_buf = tmp_buf;
+                                        this->compr_buf_size = orig_dlen;
+                                }
+                        }
+                        this->usecount++;
+                        spin_unlock(&jffs2_compressor_list_lock);
+                        *datalen  = orig_slen;
+                        *cdatalen = orig_dlen;
+                        compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL);
+                        spin_lock(&jffs2_compressor_list_lock);
+                        this->usecount--;
+                        if (!compr_ret) {
+                                if ((!best_dlen)||(best_dlen>*cdatalen)) {
+                                        best_dlen = *cdatalen;
+                                        best_slen = *datalen;
+                                        best = this;
+                                }
+                        }
+                }
+                if (best_dlen) {
+                        *cdatalen = best_dlen;
+                        *datalen  = best_slen;
+                        output_buf = best->compr_buf;
+                        best->compr_buf = NULL;
+                        best->compr_buf_size = 0;
+                        best->stat_compr_blocks++;
+                        best->stat_compr_orig_size += best_slen;
+                        best->stat_compr_new_size  += best_dlen;
+                        ret = best->compr;
+                }
+                spin_unlock(&jffs2_compressor_list_lock);
+                break;
+        default:
+                printk(KERN_ERR "JFFS2: unknow compression mode.\n");
+        }
+ out:
+        if (ret == JFFS2_COMPR_NONE) {
+               *cpage_out = data_in;
+               *datalen = *cdatalen;
+                none_stat_compr_blocks++;
+                none_stat_compr_size += *datalen;
+        }
+        else {
+                *cpage_out = output_buf;
+        }
+       return ret;
 }
 
-
-int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, 
+int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+                    uint16_t comprtype, unsigned char *cdata_in, 
                     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen)
 {
-       switch (comprtype) {
+        struct jffs2_compressor *this;
+        int ret;
+
+       switch (comprtype & 0xff) {
        case JFFS2_COMPR_NONE:
                /* This should be special-cased elsewhere, but we might as well deal with it */
                memcpy(data_out, cdata_in, datalen);
+                none_stat_decompr_blocks++;
                break;
-
        case JFFS2_COMPR_ZERO:
                memset(data_out, 0, datalen);
                break;
+       default:
+                spin_lock(&jffs2_compressor_list_lock);
+                list_for_each_entry(this, &jffs2_compressor_list, list) {
+                        if (comprtype == this->compr) {
+                                this->usecount++;
+                                spin_unlock(&jffs2_compressor_list_lock);
+                                ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL);
+                                spin_lock(&jffs2_compressor_list_lock);
+                                if (ret) {
+                                        printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret);
+                                }
+                                else {
+                                        this->stat_decompr_blocks++;
+                                }
+                                this->usecount--;
+                                spin_unlock(&jffs2_compressor_list_lock);
+                                return ret;
+                        }
+                }
+               printk(KERN_WARNING "JFFS2 compression type 0x%02x not avaiable.\n", comprtype);
+                spin_unlock(&jffs2_compressor_list_lock);
+               return -EIO;
+       }
+       return 0;
+}
 
-       case JFFS2_COMPR_ZLIB:
-               jffs2_zlib_decompress(cdata_in, data_out, cdatalen, datalen);
-               break;
+int jffs2_register_compressor(struct jffs2_compressor *comp)
+{
+        struct jffs2_compressor *this;
 
-       case JFFS2_COMPR_RTIME:
-               jffs2_rtime_decompress(cdata_in, data_out, cdatalen, datalen);
-               break;
+        if (!comp->name) {
+                printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n");
+                return -1;
+        }
+        comp->compr_buf_size=0;
+        comp->compr_buf=NULL;
+        comp->usecount=0;
+        comp->stat_compr_orig_size=0;
+        comp->stat_compr_new_size=0;
+        comp->stat_compr_blocks=0;
+        comp->stat_decompr_blocks=0;
+        D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name));
+
+        spin_lock(&jffs2_compressor_list_lock);
+
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                if (this->priority < comp->priority) {
+                        list_add(&comp->list, this->list.prev);
+                        goto out;
+                }
+        }
+        list_add_tail(&comp->list, &jffs2_compressor_list);
+out:
+        D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
+                printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
+        })
+
+        spin_unlock(&jffs2_compressor_list_lock);
+
+        return 0;
+}
+
+int jffs2_unregister_compressor(struct jffs2_compressor *comp)
+{
+        D2(struct jffs2_compressor *this;)
+
+        D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name));
+
+        spin_lock(&jffs2_compressor_list_lock);
+
+        if (comp->usecount) {
+                spin_unlock(&jffs2_compressor_list_lock);
+                printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n");
+                return -1;
+        }
+        list_del(&comp->list);
+
+        D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
+                printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
+        })
+        spin_unlock(&jffs2_compressor_list_lock);
+        return 0;
+}
+
+#ifdef CONFIG_JFFS2_PROC
+
+#define JFFS2_STAT_BUF_SIZE 16000
+
+char *jffs2_list_compressors(void)
+{
+        struct jffs2_compressor *this;
+        char *buf, *act_buf;
+
+        act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL);
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority);
+                if ((this->disabled)||(!this->compress))
+                        act_buf += sprintf(act_buf,"disabled");
+                else
+                        act_buf += sprintf(act_buf,"enabled");
+                act_buf += sprintf(act_buf,"\n");
+        }
+        return buf;
+}
+
+char *jffs2_stats(void)
+{
+        struct jffs2_compressor *this;
+        char *buf, *act_buf;
+
+        act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL);
+
+        act_buf += sprintf(act_buf,"JFFS2 compressor statistics:\n");
+        act_buf += sprintf(act_buf,"%10s   ","none");
+        act_buf += sprintf(act_buf,"compr: %d blocks (%d)  decompr: %d blocks\n", none_stat_compr_blocks, 
+                           none_stat_compr_size, none_stat_decompr_blocks);
+        spin_lock(&jffs2_compressor_list_lock);
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                act_buf += sprintf(act_buf,"%10s ",this->name);
+                if ((this->disabled)||(!this->compress))
+                        act_buf += sprintf(act_buf,"- ");
+                else
+                        act_buf += sprintf(act_buf,"+ ");
+                act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d)  decompr: %d blocks ", this->stat_compr_blocks, 
+                                   this->stat_compr_new_size, this->stat_compr_orig_size, 
+                                   this->stat_decompr_blocks);
+                act_buf += sprintf(act_buf,"\n");
+        }
+        spin_unlock(&jffs2_compressor_list_lock);
+
+        return buf;
+}
+
+char *jffs2_get_compression_mode_name(void) 
+{
+        switch (jffs2_compression_mode) {
+        case JFFS2_COMPR_MODE_NONE:
+                return "none";
+        case JFFS2_COMPR_MODE_PRIORITY:
+                return "priority";
+        case JFFS2_COMPR_MODE_SIZE:
+                return "size";
+        }
+        return "unkown";
+}
+
+int jffs2_set_compression_mode_name(const char *name) 
+{
+        if (!strcmp("none",name)) {
+                jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
+                return 0;
+        }
+        if (!strcmp("priority",name)) {
+                jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
+                return 0;
+        }
+        if (!strcmp("size",name)) {
+                jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
+                return 0;
+        }
+        return 1;
+}
+
+static int jffs2_compressor_Xable(const char *name, int disabled)
+{
+        struct jffs2_compressor *this;
+        spin_lock(&jffs2_compressor_list_lock);
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                if (!strcmp(this->name, name)) {
+                        this->disabled = disabled;
+                        spin_unlock(&jffs2_compressor_list_lock);
+                        return 0;                        
+                }
+        }
+        spin_unlock(&jffs2_compressor_list_lock);
+        printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name);
+        return 1;
+}
+
+int jffs2_enable_compressor_name(const char *name)
+{
+        return jffs2_compressor_Xable(name, 0);
+}
+
+int jffs2_disable_compressor_name(const char *name)
+{
+        return jffs2_compressor_Xable(name, 1);
+}
+
+int jffs2_set_compressor_priority(const char *name, int priority)
+{
+        struct jffs2_compressor *this,*comp;
+        spin_lock(&jffs2_compressor_list_lock);
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                if (!strcmp(this->name, name)) {
+                        this->priority = priority;
+                        comp = this;
+                        goto reinsert;
+                }
+        }
+        spin_unlock(&jffs2_compressor_list_lock);
+        printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name);        
+        return 1;
+reinsert:
+        /* list is sorted in the order of priority, so if
+           we change it we have to reinsert it into the
+           good place */
+        list_del(&comp->list);
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                if (this->priority < comp->priority) {
+                        list_add(&comp->list, this->list.prev);
+                        spin_unlock(&jffs2_compressor_list_lock);
+                        return 0;
+                }
+        }
+        list_add_tail(&comp->list, &jffs2_compressor_list);
+        spin_unlock(&jffs2_compressor_list_lock);
+        return 0;
+}
 
-       case JFFS2_COMPR_RUBINMIPS:
-#if 0 /* Disabled 23/9/1 */
-               jffs2_rubinmips_decompress(cdata_in, data_out, cdatalen, datalen);
-#else
-               printk(KERN_WARNING "JFFS2: Rubinmips compression encountered but support not compiled in!\n");
 #endif
-               break;
-       case JFFS2_COMPR_DYNRUBIN:
-#if 1 /* Phase this one out */
-               jffs2_dynrubin_decompress(cdata_in, data_out, cdatalen, datalen);
+
+void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
+{
+        if (orig != comprbuf)
+                kfree(comprbuf);
+}
+
+int jffs2_compressors_init(void) 
+{
+/* Registering compressors */
+#ifdef CONFIG_JFFS2_ZLIB
+        jffs2_zlib_init();
+#endif
+#ifdef CONFIG_JFFS2_RTIME
+        jffs2_rtime_init();
+#endif
+#ifdef CONFIG_JFFS2_RUBIN
+        jffs2_rubinmips_init();
+        jffs2_dynrubin_init();
+#endif
+#ifdef CONFIG_JFFS2_LZARI
+        jffs2_lzari_init();
+#endif
+#ifdef CONFIG_JFFS2_LZO
+        jffs2_lzo_init();
+#endif
+/* Setting default compression mode */
+#ifdef CONFIG_JFFS2_CMODE_NONE
+        jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
+        D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");)
 #else
-               printk(KERN_WARNING "JFFS2: Dynrubin compression encountered but support not compiled in!\n");
+#ifdef CONFIG_JFFS2_CMODE_SIZE
+        jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
+        D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");)
+#else
+        D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");)
 #endif
-               break;
+#endif
+        return 0;
+}
 
-       default:
-               printk(KERN_NOTICE "Unknown JFFS2 compression type 0x%02x\n", comprtype);
-               return -EIO;
-       }
-       return 0;
+int jffs2_compressors_exit(void) 
+{
+/* Unregistering compressors */
+#ifdef CONFIG_JFFS2_LZO
+        jffs2_lzo_exit();
+#endif
+#ifdef CONFIG_JFFS2_LZARI
+        jffs2_lzari_exit();
+#endif
+#ifdef CONFIG_JFFS2_RUBIN
+        jffs2_dynrubin_exit();
+        jffs2_rubinmips_exit();
+#endif
+#ifdef CONFIG_JFFS2_RTIME
+        jffs2_rtime_exit();
+#endif
+#ifdef CONFIG_JFFS2_ZLIB
+        jffs2_zlib_exit();
+#endif
+        return 0;
 }