summaryrefslogtreecommitdiffstats
path: root/fs/jffs2
diff options
context:
space:
mode:
Diffstat (limited to 'fs/jffs2')
-rw-r--r--fs/jffs2/compr.c128
-rw-r--r--fs/jffs2/compr.h2
-rw-r--r--fs/jffs2/fs.c2
-rw-r--r--fs/jffs2/jffs2_fs_sb.h6
-rw-r--r--fs/jffs2/os-linux.h2
-rw-r--r--fs/jffs2/scan.c4
-rw-r--r--fs/jffs2/super.c119
-rw-r--r--fs/jffs2/wbuf.c9
8 files changed, 225 insertions, 47 deletions
diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c
index de4247021d25..5b6c9d1a2fb9 100644
--- a/fs/jffs2/compr.c
+++ b/fs/jffs2/compr.c
@@ -53,6 +53,78 @@ static int jffs2_is_best_compression(struct jffs2_compressor *this,
return 0;
}
+/*
+ * jffs2_selected_compress:
+ * @compr: Explicit compression type to use (ie, JFFS2_COMPR_ZLIB).
+ * If 0, just take the first available compression mode.
+ * @data_in: Pointer to uncompressed data
+ * @cpage_out: 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: the compression type used. Zero is used to show that the data
+ * could not be compressed; probably because we couldn't find the requested
+ * compression mode.
+ */
+static int jffs2_selected_compress(u8 compr, unsigned char *data_in,
+ unsigned char **cpage_out, u32 *datalen, u32 *cdatalen)
+{
+ struct jffs2_compressor *this;
+ int err, ret = JFFS2_COMPR_NONE;
+ uint32_t orig_slen, orig_dlen;
+ char *output_buf;
+
+ output_buf = kmalloc(*cdatalen, GFP_KERNEL);
+ if (!output_buf) {
+ printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n");
+ return ret;
+ }
+ orig_slen = *datalen;
+ orig_dlen = *cdatalen;
+ spin_lock(&jffs2_compressor_list_lock);
+ list_for_each_entry(this, &jffs2_compressor_list, list) {
+ /* Skip decompress-only and disabled modules */
+ if (!this->compress || this->disabled)
+ continue;
+
+ /* Skip if not the desired compression type */
+ if (compr && (compr != this->compr))
+ continue;
+
+ /*
+ * Either compression type was unspecified, or we found our
+ * compressor; either way, we're good to go.
+ */
+ this->usecount++;
+ spin_unlock(&jffs2_compressor_list_lock);
+
+ *datalen = orig_slen;
+ *cdatalen = orig_dlen;
+ err = this->compress(data_in, output_buf, datalen, cdatalen);
+
+ spin_lock(&jffs2_compressor_list_lock);
+ this->usecount--;
+ if (!err) {
+ /* Success */
+ 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);
+ else
+ *cpage_out = output_buf;
+
+ return ret;
+}
+
/* jffs2_compress:
* @data_in: Pointer to uncompressed data
* @cpage_out: Pointer to returned pointer to buffer for compressed data
@@ -76,47 +148,23 @@ uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
uint32_t *datalen, uint32_t *cdatalen)
{
int ret = JFFS2_COMPR_NONE;
- int compr_ret;
+ int mode, 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;
- switch (jffs2_compression_mode) {
+ if (c->mount_opts.override_compr)
+ mode = c->mount_opts.compr;
+ else
+ mode = jffs2_compression_mode;
+
+ switch (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);
- 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);
+ ret = jffs2_selected_compress(0, data_in, cpage_out, datalen,
+ cdatalen);
break;
case JFFS2_COMPR_MODE_SIZE:
case JFFS2_COMPR_MODE_FAVOURLZO:
@@ -174,22 +222,28 @@ uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
best->stat_compr_orig_size += best_slen;
best->stat_compr_new_size += best_dlen;
ret = best->compr;
+ *cpage_out = output_buf;
}
spin_unlock(&jffs2_compressor_list_lock);
break;
+ case JFFS2_COMPR_MODE_FORCELZO:
+ ret = jffs2_selected_compress(JFFS2_COMPR_LZO, data_in,
+ cpage_out, datalen, cdatalen);
+ break;
+ case JFFS2_COMPR_MODE_FORCEZLIB:
+ ret = jffs2_selected_compress(JFFS2_COMPR_ZLIB, data_in,
+ cpage_out, datalen, cdatalen);
+ break;
default:
printk(KERN_ERR "JFFS2: unknown 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;
}
diff --git a/fs/jffs2/compr.h b/fs/jffs2/compr.h
index 13bb7597ab39..5e91d578f4ed 100644
--- a/fs/jffs2/compr.h
+++ b/fs/jffs2/compr.h
@@ -40,6 +40,8 @@
#define JFFS2_COMPR_MODE_PRIORITY 1
#define JFFS2_COMPR_MODE_SIZE 2
#define JFFS2_COMPR_MODE_FAVOURLZO 3
+#define JFFS2_COMPR_MODE_FORCELZO 4
+#define JFFS2_COMPR_MODE_FORCEZLIB 5
#define FAVOUR_LZO_PERCENT 80
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index 7286e44ac665..4b8afe39a87f 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -379,7 +379,7 @@ void jffs2_dirty_inode(struct inode *inode, int flags)
jffs2_do_setattr(inode, &iattr);
}
-int jffs2_remount_fs (struct super_block *sb, int *flags, char *data)
+int jffs2_do_remount_fs(struct super_block *sb, int *flags, char *data)
{
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h
index 0bc6a6c80a56..55a0c1dceadf 100644
--- a/fs/jffs2/jffs2_fs_sb.h
+++ b/fs/jffs2/jffs2_fs_sb.h
@@ -29,6 +29,11 @@
struct jffs2_inodirty;
+struct jffs2_mount_opts {
+ bool override_compr;
+ unsigned int compr;
+};
+
/* A struct for the overall file system control. Pointers to
jffs2_sb_info structs are named `c' in the source code.
Nee jffs_control
@@ -126,6 +131,7 @@ struct jffs2_sb_info {
#endif
struct jffs2_summary *summary; /* Summary information */
+ struct jffs2_mount_opts mount_opts;
#ifdef CONFIG_JFFS2_FS_XATTR
#define XATTRINDEX_HASHSIZE (57)
diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h
index 6c1755c59c0f..ab65ee3ec858 100644
--- a/fs/jffs2/os-linux.h
+++ b/fs/jffs2/os-linux.h
@@ -176,7 +176,7 @@ void jffs2_dirty_inode(struct inode *inode, int flags);
struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode,
struct jffs2_raw_inode *ri);
int jffs2_statfs (struct dentry *, struct kstatfs *);
-int jffs2_remount_fs (struct super_block *, int *, char *);
+int jffs2_do_remount_fs(struct super_block *, int *, char *);
int jffs2_do_fill_super(struct super_block *sb, void *data, int silent);
void jffs2_gc_release_inode(struct jffs2_sb_info *c,
struct jffs2_inode_info *f);
diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c
index 8d8cd3419d02..28107ca136e4 100644
--- a/fs/jffs2/scan.c
+++ b/fs/jffs2/scan.c
@@ -275,9 +275,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
else
c->mtd->unpoint(c->mtd, 0, c->mtd->size);
#endif
- if (s)
- kfree(s);
-
+ kfree(s);
return ret;
}
diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
index 853b8e300084..e7e974454115 100644
--- a/fs/jffs2/super.c
+++ b/fs/jffs2/super.c
@@ -17,11 +17,13 @@
#include <linux/fs.h>
#include <linux/err.h>
#include <linux/mount.h>
+#include <linux/parser.h>
#include <linux/jffs2.h>
#include <linux/pagemap.h>
#include <linux/mtd/super.h>
#include <linux/ctype.h>
#include <linux/namei.h>
+#include <linux/seq_file.h>
#include <linux/exportfs.h>
#include "compr.h"
#include "nodelist.h"
@@ -75,6 +77,37 @@ static void jffs2_write_super(struct super_block *sb)
unlock_super(sb);
}
+static const char *jffs2_compr_name(unsigned int compr)
+{
+ switch (compr) {
+ case JFFS2_COMPR_MODE_NONE:
+ return "none";
+#ifdef CONFIG_JFFS2_LZO
+ case JFFS2_COMPR_MODE_FORCELZO:
+ return "lzo";
+#endif
+#ifdef CONFIG_JFFS2_ZLIB
+ case JFFS2_COMPR_MODE_FORCEZLIB:
+ return "zlib";
+#endif
+ default:
+ /* should never happen; programmer error */
+ WARN_ON(1);
+ return "";
+ }
+}
+
+static int jffs2_show_options(struct seq_file *s, struct vfsmount *mnt)
+{
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(mnt->mnt_sb);
+ struct jffs2_mount_opts *opts = &c->mount_opts;
+
+ if (opts->override_compr)
+ seq_printf(s, ",compr=%s", jffs2_compr_name(opts->compr));
+
+ return 0;
+}
+
static int jffs2_sync_fs(struct super_block *sb, int wait)
{
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
@@ -133,6 +166,85 @@ static const struct export_operations jffs2_export_ops = {
.fh_to_parent = jffs2_fh_to_parent,
};
+/*
+ * JFFS2 mount options.
+ *
+ * Opt_override_compr: override default compressor
+ * Opt_err: just end of array marker
+ */
+enum {
+ Opt_override_compr,
+ Opt_err,
+};
+
+static const match_table_t tokens = {
+ {Opt_override_compr, "compr=%s"},
+ {Opt_err, NULL},
+};
+
+static int jffs2_parse_options(struct jffs2_sb_info *c, char *data)
+{
+ substring_t args[MAX_OPT_ARGS];
+ char *p, *name;
+
+ if (!data)
+ return 0;
+
+ while ((p = strsep(&data, ","))) {
+ int token;
+
+ if (!*p)
+ continue;
+
+ token = match_token(p, tokens, args);
+ switch (token) {
+ case Opt_override_compr:
+ name = match_strdup(&args[0]);
+
+ if (!name)
+ return -ENOMEM;
+ if (!strcmp(name, "none"))
+ c->mount_opts.compr = JFFS2_COMPR_MODE_NONE;
+#ifdef CONFIG_JFFS2_LZO
+ else if (!strcmp(name, "lzo"))
+ c->mount_opts.compr = JFFS2_COMPR_MODE_FORCELZO;
+#endif
+#ifdef CONFIG_JFFS2_ZLIB
+ else if (!strcmp(name, "zlib"))
+ c->mount_opts.compr =
+ JFFS2_COMPR_MODE_FORCEZLIB;
+#endif
+ else {
+ printk(KERN_ERR "JFFS2 Error: unknown compressor \"%s\"",
+ name);
+ kfree(name);
+ return -EINVAL;
+ }
+ kfree(name);
+ c->mount_opts.override_compr = true;
+ break;
+ default:
+ printk(KERN_ERR "JFFS2 Error: unrecognized mount option '%s' or missing value\n",
+ p);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int jffs2_remount_fs(struct super_block *sb, int *flags, char *data)
+{
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+ int err;
+
+ err = jffs2_parse_options(c, data);
+ if (err)
+ return -EINVAL;
+
+ return jffs2_do_remount_fs(sb, flags, data);
+}
+
static const struct super_operations jffs2_super_operations =
{
.alloc_inode = jffs2_alloc_inode,
@@ -143,6 +255,7 @@ static const struct super_operations jffs2_super_operations =
.remount_fs = jffs2_remount_fs,
.evict_inode = jffs2_evict_inode,
.dirty_inode = jffs2_dirty_inode,
+ .show_options = jffs2_show_options,
.sync_fs = jffs2_sync_fs,
};
@@ -166,6 +279,12 @@ static int jffs2_fill_super(struct super_block *sb, void *data, int silent)
c->os_priv = sb;
sb->s_fs_info = c;
+ ret = jffs2_parse_options(c, data);
+ if (ret) {
+ kfree(c);
+ return -EINVAL;
+ }
+
/* Initialize JFFS2 superblock locks, the further initialization will
* be done later */
mutex_init(&c->alloc_sem);
diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c
index 4515bea0268f..b09e51d2f81f 100644
--- a/fs/jffs2/wbuf.c
+++ b/fs/jffs2/wbuf.c
@@ -578,8 +578,7 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
if (!jffs2_is_writebuffered(c))
return 0;
- if (mutex_trylock(&c->alloc_sem)) {
- mutex_unlock(&c->alloc_sem);
+ if (!mutex_is_locked(&c->alloc_sem)) {
printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n");
BUG();
}
@@ -1026,7 +1025,7 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c,
int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
struct mtd_oob_ops ops;
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.ooblen = NR_OOB_SCAN_PAGES * c->oobavail;
ops.oobbuf = c->oobbuf;
ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
@@ -1069,7 +1068,7 @@ int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c,
struct mtd_oob_ops ops;
int ret, cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.ooblen = cmlen;
ops.oobbuf = c->oobbuf;
ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
@@ -1095,7 +1094,7 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c,
struct mtd_oob_ops ops;
int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
- ops.mode = MTD_OOB_AUTO;
+ ops.mode = MTD_OPS_AUTO_OOB;
ops.ooblen = cmlen;
ops.oobbuf = (uint8_t *)&oob_cleanmarker;
ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;