summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@g5.osdl.org>2005-07-11 10:18:18 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2005-07-11 10:18:18 -0700
commit200d481f28be4522464bb849dd0eb5f8cb6be781 (patch)
tree8cd00ead1b202dfd377cf34000a5193959aa2e8b /fs
parentf43a64c5e1a65d12b9b53a35ed2d5db441fcb64c (diff)
parent97f927a4d7dbccde0a854a62c3ea54d90bae8679 (diff)
Merge master.kernel.org:/pub/scm/linux/kernel/git/tglx/mtd-2.6
Diffstat (limited to 'fs')
-rw-r--r--fs/Kconfig26
-rw-r--r--fs/jffs2/Makefile5
-rw-r--r--fs/jffs2/README.Locking6
-rw-r--r--fs/jffs2/background.c13
-rw-r--r--fs/jffs2/build.c9
-rw-r--r--fs/jffs2/compr_zlib.c4
-rw-r--r--fs/jffs2/dir.c46
-rw-r--r--fs/jffs2/erase.c24
-rw-r--r--fs/jffs2/file.c5
-rw-r--r--fs/jffs2/fs.c24
-rw-r--r--fs/jffs2/gc.c41
-rw-r--r--fs/jffs2/nodelist.c93
-rw-r--r--fs/jffs2/nodelist.h21
-rw-r--r--fs/jffs2/nodemgmt.c31
-rw-r--r--fs/jffs2/os-linux.h60
-rw-r--r--fs/jffs2/read.c32
-rw-r--r--fs/jffs2/readinode.c96
-rw-r--r--fs/jffs2/scan.c39
-rw-r--r--fs/jffs2/super.c8
-rw-r--r--fs/jffs2/symlink.c42
-rw-r--r--fs/jffs2/wbuf.c164
-rw-r--r--fs/jffs2/write.c55
22 files changed, 533 insertions, 311 deletions
diff --git a/fs/Kconfig b/fs/Kconfig
index aae0686a15fb..f93fd41b025d 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -1039,26 +1039,18 @@ config JFFS2_FS_DEBUG
If reporting bugs, please try to have available a full dump of the
messages at debug level 1 while the misbehaviour was occurring.
-config JFFS2_FS_NAND
- bool "JFFS2 support for NAND flash"
+config JFFS2_FS_WRITEBUFFER
+ bool "JFFS2 write-buffering support"
depends on JFFS2_FS
- default n
+ default y
help
- This enables the support for NAND flash in JFFS2. NAND is a newer
- type of flash chip design than the traditional NOR flash, with
- higher density but a handful of characteristics which make it more
- interesting for the file system to use.
+ This enables the write-buffering support in JFFS2.
- Say 'N' unless you have NAND flash.
-
-config JFFS2_FS_NOR_ECC
- bool "JFFS2 support for ECC'd NOR flash (EXPERIMENTAL)"
- depends on JFFS2_FS && EXPERIMENTAL
- default n
- help
- This enables the experimental support for NOR flash with transparent
- ECC for JFFS2. This type of flash chip is not common, however it is
- available from ST Microelectronics.
+ This functionality is required to support JFFS2 on the following
+ types of flash devices:
+ - NAND flash
+ - NOR flash with transparent ECC
+ - DataFlash
config JFFS2_COMPRESSION_OPTIONS
bool "Advanced compression options for JFFS2"
diff --git a/fs/jffs2/Makefile b/fs/jffs2/Makefile
index e3c38ccf9c7d..f1afe681ecd6 100644
--- a/fs/jffs2/Makefile
+++ b/fs/jffs2/Makefile
@@ -1,7 +1,7 @@
#
# Makefile for the Linux Journalling Flash File System v2 (JFFS2)
#
-# $Id: Makefile.common,v 1.7 2004/11/03 12:57:38 jwboyer Exp $
+# $Id: Makefile.common,v 1.9 2005/02/09 09:23:53 pavlov Exp $
#
obj-$(CONFIG_JFFS2_FS) += jffs2.o
@@ -11,8 +11,7 @@ jffs2-y += read.o nodemgmt.o readinode.o write.o scan.o gc.o
jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o
jffs2-y += super.o
-jffs2-$(CONFIG_JFFS2_FS_NAND) += wbuf.o
-jffs2-$(CONFIG_JFFS2_FS_NOR_ECC) += wbuf.o
+jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER) += wbuf.o
jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o
jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o
jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o
diff --git a/fs/jffs2/README.Locking b/fs/jffs2/README.Locking
index 49771cf8513a..b7943439b6ec 100644
--- a/fs/jffs2/README.Locking
+++ b/fs/jffs2/README.Locking
@@ -1,4 +1,4 @@
- $Id: README.Locking,v 1.9 2004/11/20 10:35:40 dwmw2 Exp $
+ $Id: README.Locking,v 1.12 2005/04/13 13:22:35 dwmw2 Exp $
JFFS2 LOCKING DOCUMENTATION
---------------------------
@@ -108,6 +108,10 @@ in-core jffs2_inode_cache objects (each inode in JFFS2 has the
correspondent jffs2_inode_cache object). So, the inocache_lock
has to be locked while walking the c->inocache_list hash buckets.
+This spinlock also covers allocation of new inode numbers, which is
+currently just '++->highest_ino++', but might one day get more complicated
+if we need to deal with wrapping after 4 milliard inode numbers are used.
+
Note, the f->sem guarantees that the correspondent jffs2_inode_cache
will not be removed. So, it is allowed to access it without locking
the inocache_lock spinlock.
diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c
index 638836b277d4..0f224384f176 100644
--- a/fs/jffs2/background.c
+++ b/fs/jffs2/background.c
@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: background.c,v 1.50 2004/11/16 20:36:10 dwmw2 Exp $
+ * $Id: background.c,v 1.54 2005/05/20 21:37:12 gleixner Exp $
*
*/
@@ -37,7 +37,7 @@ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
if (c->gc_task)
BUG();
- init_MUTEX_LOCKED(&c->gc_thread_start);
+ init_completion(&c->gc_thread_start);
init_completion(&c->gc_thread_exit);
pid = kernel_thread(jffs2_garbage_collect_thread, c, CLONE_FS|CLONE_FILES);
@@ -48,7 +48,7 @@ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
} else {
/* Wait for it... */
D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", pid));
- down(&c->gc_thread_start);
+ wait_for_completion(&c->gc_thread_start);
}
return ret;
@@ -56,13 +56,16 @@ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c)
{
+ int wait = 0;
spin_lock(&c->erase_completion_lock);
if (c->gc_task) {
D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid));
send_sig(SIGKILL, c->gc_task, 1);
+ wait = 1;
}
spin_unlock(&c->erase_completion_lock);
- wait_for_completion(&c->gc_thread_exit);
+ if (wait)
+ wait_for_completion(&c->gc_thread_exit);
}
static int jffs2_garbage_collect_thread(void *_c)
@@ -75,7 +78,7 @@ static int jffs2_garbage_collect_thread(void *_c)
allow_signal(SIGCONT);
c->gc_task = current;
- up(&c->gc_thread_start);
+ complete(&c->gc_thread_start);
set_user_nice(current, 10);
diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c
index a01dd5fdbb95..3dd5394921c9 100644
--- a/fs/jffs2/build.c
+++ b/fs/jffs2/build.c
@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: build.c,v 1.69 2004/12/16 20:22:18 dmarlin Exp $
+ * $Id: build.c,v 1.70 2005/02/28 08:21:05 dedekind Exp $
*
*/
@@ -97,14 +97,16 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
/* First, scan the medium and build all the inode caches with
lists of physical nodes */
- c->flags |= JFFS2_SB_FLAG_MOUNTING;
+ c->flags |= JFFS2_SB_FLAG_SCANNING;
ret = jffs2_scan_medium(c);
+ c->flags &= ~JFFS2_SB_FLAG_SCANNING;
if (ret)
goto exit;
D1(printk(KERN_DEBUG "Scanned flash completely\n"));
D2(jffs2_dump_block_lists(c));
+ c->flags |= JFFS2_SB_FLAG_BUILDING;
/* Now scan the directory tree, increasing nlink according to every dirent found. */
for_each_inode(i, c, ic) {
D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino));
@@ -116,7 +118,6 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
cond_resched();
}
}
- c->flags &= ~JFFS2_SB_FLAG_MOUNTING;
D1(printk(KERN_DEBUG "Pass 1 complete\n"));
@@ -164,6 +165,8 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
ic->scan_dents = NULL;
cond_resched();
}
+ c->flags &= ~JFFS2_SB_FLAG_BUILDING;
+
D1(printk(KERN_DEBUG "Pass 3 complete\n"));
D2(jffs2_dump_block_lists(c));
diff --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c
index 078a30e406b5..83f7e0788fd0 100644
--- a/fs/jffs2/compr_zlib.c
+++ b/fs/jffs2/compr_zlib.c
@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: compr_zlib.c,v 1.29 2004/11/16 20:36:11 dwmw2 Exp $
+ * $Id: compr_zlib.c,v 1.31 2005/05/20 19:30:06 gleixner Exp $
*
*/
@@ -17,10 +17,10 @@
#include <linux/config.h>
#include <linux/kernel.h>
+#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/zlib.h>
#include <linux/zutil.h>
-#include <asm/semaphore.h>
#include "nodelist.h"
#include "compr.h"
diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c
index 757306fa3ff4..3ca0d25eef1d 100644
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: dir.c,v 1.84 2004/11/16 20:36:11 dwmw2 Exp $
+ * $Id: dir.c,v 1.86 2005/07/06 12:13:09 dwmw2 Exp $
*
*/
@@ -22,16 +22,6 @@
#include <linux/time.h>
#include "nodelist.h"
-/* Urgh. Please tell me there's a nicer way of doing these. */
-#include <linux/version.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
-typedef int mknod_arg_t;
-#define NAMEI_COMPAT(x) ((void *)x)
-#else
-typedef dev_t mknod_arg_t;
-#define NAMEI_COMPAT(x) (x)
-#endif
-
static int jffs2_readdir (struct file *, void *, filldir_t);
static int jffs2_create (struct inode *,struct dentry *,int,
@@ -43,7 +33,7 @@ static int jffs2_unlink (struct inode *,struct dentry *);
static int jffs2_symlink (struct inode *,struct dentry *,const char *);
static int jffs2_mkdir (struct inode *,struct dentry *,int);
static int jffs2_rmdir (struct inode *,struct dentry *);
-static int jffs2_mknod (struct inode *,struct dentry *,int,mknod_arg_t);
+static int jffs2_mknod (struct inode *,struct dentry *,int,dev_t);
static int jffs2_rename (struct inode *, struct dentry *,
struct inode *, struct dentry *);
@@ -58,8 +48,8 @@ struct file_operations jffs2_dir_operations =
struct inode_operations jffs2_dir_inode_operations =
{
- .create = NAMEI_COMPAT(jffs2_create),
- .lookup = NAMEI_COMPAT(jffs2_lookup),
+ .create = jffs2_create,
+ .lookup = jffs2_lookup,
.link = jffs2_link,
.unlink = jffs2_unlink,
.symlink = jffs2_symlink,
@@ -296,11 +286,11 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
struct jffs2_full_dirent *fd;
int namelen;
uint32_t alloclen, phys_ofs;
- int ret;
+ int ret, targetlen = strlen(target);
/* FIXME: If you care. We'd need to use frags for the target
if it grows much more than this */
- if (strlen(target) > 254)
+ if (targetlen > 254)
return -EINVAL;
ri = jffs2_alloc_raw_inode();
@@ -314,7 +304,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
* Just the node will do for now, though
*/
namelen = dentry->d_name.len;
- ret = jffs2_reserve_space(c, sizeof(*ri) + strlen(target), &phys_ofs, &alloclen, ALLOC_NORMAL);
+ ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &phys_ofs, &alloclen, ALLOC_NORMAL);
if (ret) {
jffs2_free_raw_inode(ri);
@@ -333,16 +323,16 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
f = JFFS2_INODE_INFO(inode);
- inode->i_size = strlen(target);
+ inode->i_size = targetlen;
ri->isize = ri->dsize = ri->csize = cpu_to_je32(inode->i_size);
ri->totlen = cpu_to_je32(sizeof(*ri) + inode->i_size);
ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
ri->compr = JFFS2_COMPR_NONE;
- ri->data_crc = cpu_to_je32(crc32(0, target, strlen(target)));
+ ri->data_crc = cpu_to_je32(crc32(0, target, targetlen));
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
- fn = jffs2_write_dnode(c, f, ri, target, strlen(target), phys_ofs, ALLOC_NORMAL);
+ fn = jffs2_write_dnode(c, f, ri, target, targetlen, phys_ofs, ALLOC_NORMAL);
jffs2_free_raw_inode(ri);
@@ -353,6 +343,20 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
jffs2_clear_inode(inode);
return PTR_ERR(fn);
}
+
+ /* We use f->dents field to store the target path. */
+ f->dents = kmalloc(targetlen + 1, GFP_KERNEL);
+ if (!f->dents) {
+ printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1);
+ up(&f->sem);
+ jffs2_complete_reservation(c);
+ jffs2_clear_inode(inode);
+ return -ENOMEM;
+ }
+
+ memcpy(f->dents, target, targetlen + 1);
+ D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->dents));
+
/* No data here. Only a metadata node, which will be
obsoleted by the first data write
*/
@@ -564,7 +568,7 @@ static int jffs2_rmdir (struct inode *dir_i, struct dentry *dentry)
return ret;
}
-static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mknod_arg_t rdev)
+static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, dev_t rdev)
{
struct jffs2_inode_info *f, *dir_f;
struct jffs2_sb_info *c;
diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c
index 41451e8bf361..6a4c0a3685da 100644
--- a/fs/jffs2/erase.c
+++ b/fs/jffs2/erase.c
@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: erase.c,v 1.66 2004/11/16 20:36:11 dwmw2 Exp $
+ * $Id: erase.c,v 1.76 2005/05/03 15:11:40 dedekind Exp $
*
*/
@@ -48,6 +48,7 @@ static void jffs2_erase_block(struct jffs2_sb_info *c,
#else /* Linux */
struct erase_info *instr;
+ D1(printk(KERN_DEBUG "jffs2_erase_block(): erase block %#x (range %#x-%#x)\n", jeb->offset, jeb->offset, jeb->offset + c->sector_size));
instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
if (!instr) {
printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
@@ -233,7 +234,7 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
continue;
}
- if (((*prev)->flash_offset & ~(c->sector_size -1)) == jeb->offset) {
+ if (SECTOR_ADDR((*prev)->flash_offset) == jeb->offset) {
/* It's in the block we're erasing */
struct jffs2_raw_node_ref *this;
@@ -277,11 +278,8 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
printk("\n");
});
- if (ic->nodes == (void *)ic) {
- D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino));
+ if (ic->nodes == (void *)ic && ic->nlink == 0)
jffs2_del_ino_cache(c, ic);
- jffs2_free_inode_cache(ic);
- }
}
static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
@@ -310,7 +308,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
int ret;
uint32_t bad_offset;
- if (!jffs2_cleanmarker_oob(c)) {
+ if ((!jffs2_cleanmarker_oob(c)) && (c->cleanmarker_size > 0)) {
marker_ref = jffs2_alloc_raw_node_ref();
if (!marker_ref) {
printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n");
@@ -335,7 +333,8 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
bad_offset = ofs;
- ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf);
+ ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf);
+
if (ret) {
printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
goto bad;
@@ -351,7 +350,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
bad_offset += i;
printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, bad_offset);
bad:
- if (!jffs2_cleanmarker_oob(c))
+ if ((!jffs2_cleanmarker_oob(c)) && (c->cleanmarker_size > 0))
jffs2_free_raw_node_ref(marker_ref);
kfree(ebuf);
bad2:
@@ -387,6 +386,13 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
jeb->used_size = 0;
jeb->dirty_size = 0;
jeb->wasted_size = 0;
+ } else if (c->cleanmarker_size == 0) {
+ jeb->first_node = jeb->last_node = NULL;
+
+ jeb->free_size = c->sector_size;
+ jeb->used_size = 0;
+ jeb->dirty_size = 0;
+ jeb->wasted_size = 0;
} else {
struct kvec vecs[1];
struct jffs2_unknown_node marker = {
diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c
index 771a554701d6..bd9ed9b0247b 100644
--- a/fs/jffs2/file.c
+++ b/fs/jffs2/file.c
@@ -7,11 +7,10 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: file.c,v 1.99 2004/11/16 20:36:11 dwmw2 Exp $
+ * $Id: file.c,v 1.102 2005/07/06 12:13:09 dwmw2 Exp $
*
*/
-#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
@@ -51,9 +50,7 @@ struct file_operations jffs2_file_operations =
.ioctl = jffs2_ioctl,
.mmap = generic_file_readonly_mmap,
.fsync = jffs2_fsync,
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,29)
.sendfile = generic_file_sendfile
-#endif
};
/* jffs2_file_inode_operations */
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index 30ab233fe423..5687c3f42002 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -7,11 +7,10 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: fs.c,v 1.51 2004/11/28 12:19:37 dedekind Exp $
+ * $Id: fs.c,v 1.56 2005/07/06 12:13:09 dwmw2 Exp $
*
*/
-#include <linux/version.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
@@ -450,11 +449,15 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
c = JFFS2_SB_INFO(sb);
-#ifndef CONFIG_JFFS2_FS_NAND
+#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
if (c->mtd->type == MTD_NANDFLASH) {
printk(KERN_ERR "jffs2: Cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n");
return -EINVAL;
}
+ if (c->mtd->type == MTD_DATAFLASH) {
+ printk(KERN_ERR "jffs2: Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in.\n");
+ return -EINVAL;
+ }
#endif
c->flash_size = c->mtd->size;
@@ -522,9 +525,7 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
if (!sb->s_root)
goto out_root_i;
-#if LINUX_VERSION_CODE >= 0x20403
sb->s_maxbytes = 0xFFFFFFFF;
-#endif
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = JFFS2_SUPER_MAGIC;
@@ -661,6 +662,14 @@ static int jffs2_flash_setup(struct jffs2_sb_info *c) {
if (ret)
return ret;
}
+
+ /* and Dataflash */
+ if (jffs2_dataflash(c)) {
+ ret = jffs2_dataflash_setup(c);
+ if (ret)
+ return ret;
+ }
+
return ret;
}
@@ -674,4 +683,9 @@ void jffs2_flash_cleanup(struct jffs2_sb_info *c) {
if (jffs2_nor_ecc(c)) {
jffs2_nor_ecc_flash_cleanup(c);
}
+
+ /* and DataFlash */
+ if (jffs2_dataflash(c)) {
+ jffs2_dataflash_cleanup(c);
+ }
}
diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c
index 87ec74ff5930..7086cd634503 100644
--- a/fs/jffs2/gc.c
+++ b/fs/jffs2/gc.c
@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: gc.c,v 1.144 2004/12/21 11:18:50 dwmw2 Exp $
+ * $Id: gc.c,v 1.148 2005/04/09 10:47:00 dedekind Exp $
*
*/
@@ -50,6 +50,7 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
put the clever wear-levelling algorithms. Eventually. */
/* We possibly want to favour the dirtier blocks more when the
number of free blocks is low. */
+again:
if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > c->resv_blocks_gcbad) {
D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n"));
nextlist = &c->bad_used_list;
@@ -79,6 +80,13 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n"));
nextlist = &c->erasable_list;
+ } else if (!list_empty(&c->erasable_pending_wbuf_list)) {
+ /* There are blocks are wating for the wbuf sync */
+ D1(printk(KERN_DEBUG "Synching wbuf in order to reuse erasable_pending_wbuf_list blocks\n"));
+ spin_unlock(&c->erase_completion_lock);
+ jffs2_flush_wbuf_pad(c);
+ spin_lock(&c->erase_completion_lock);
+ goto again;
} else {
/* Eep. All were empty */
D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n"));
@@ -661,9 +669,10 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
{
struct jffs2_full_dnode *new_fn;
struct jffs2_raw_inode ri;
+ struct jffs2_node_frag *last_frag;
jint16_t dev;
char *mdata = NULL, mdatalen = 0;
- uint32_t alloclen, phys_ofs;
+ uint32_t alloclen, phys_ofs, ilen;
int ret;
if (S_ISBLK(JFFS2_F_I_MODE(f)) ||
@@ -699,6 +708,14 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
goto out;
}
+ last_frag = frag_last(&f->fragtree);
+ if (last_frag)
+ /* Fetch the inode length from the fragtree rather then
+ * from i_size since i_size may have not been updated yet */
+ ilen = last_frag->ofs + last_frag->size;
+ else
+ ilen = JFFS2_F_I_SIZE(f);
+
memset(&ri, 0, sizeof(ri));
ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
@@ -710,7 +727,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
- ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
+ ri.isize = cpu_to_je32(ilen);
ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
@@ -816,8 +833,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct
/* Doesn't matter if there's one in the same erase block. We're going to
delete it too at the same time. */
- if ((raw->flash_offset & ~(c->sector_size-1)) ==
- (fd->raw->flash_offset & ~(c->sector_size-1)))
+ if (SECTOR_ADDR(raw->flash_offset) == SECTOR_ADDR(fd->raw->flash_offset))
continue;
D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw)));
@@ -891,7 +907,7 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
struct jffs2_raw_inode ri;
struct jffs2_node_frag *frag;
struct jffs2_full_dnode *new_fn;
- uint32_t alloclen, phys_ofs;
+ uint32_t alloclen, phys_ofs, ilen;
int ret;
D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n",
@@ -951,10 +967,19 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
ri.csize = cpu_to_je32(0);
ri.compr = JFFS2_COMPR_ZERO;
}
+
+ frag = frag_last(&f->fragtree);
+ if (frag)
+ /* Fetch the inode length from the fragtree rather then
+ * from i_size since i_size may have not been updated yet */
+ ilen = frag->ofs + frag->size;
+ else
+ ilen = JFFS2_F_I_SIZE(f);
+
ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
- ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
+ ri.isize = cpu_to_je32(ilen);
ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
@@ -1161,7 +1186,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
D1(printk(KERN_DEBUG "Expanded dnode to write from (0x%x-0x%x) to (0x%x-0x%x)\n",
orig_start, orig_end, start, end));
- BUG_ON(end > JFFS2_F_I_SIZE(f));
+ D1(BUG_ON(end > frag_last(&f->fragtree)->ofs + frag_last(&f->fragtree)->size));
BUG_ON(end < orig_end);
BUG_ON(start > orig_start);
}
diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c
index cd6a8bd13e0b..c7bbdeec93a6 100644
--- a/fs/jffs2/nodelist.c
+++ b/fs/jffs2/nodelist.c
@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: nodelist.c,v 1.90 2004/12/08 17:59:20 dwmw2 Exp $
+ * $Id: nodelist.c,v 1.97 2005/07/06 15:18:41 dwmw2 Exp $
*
*/
@@ -58,27 +58,60 @@ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new
/* Put a new tmp_dnode_info into the list, keeping the list in
order of increasing version
*/
-static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list)
+
+static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct rb_root *list)
{
- struct jffs2_tmp_dnode_info **prev = list;
-
- while ((*prev) && (*prev)->version < tn->version) {
- prev = &((*prev)->next);
- }
- tn->next = (*prev);
- *prev = tn;
+ struct rb_node **p = &list->rb_node;
+ struct rb_node * parent = NULL;
+ struct jffs2_tmp_dnode_info *this;
+
+ while (*p) {
+ parent = *p;
+ this = rb_entry(parent, struct jffs2_tmp_dnode_info, rb);
+
+ /* There may actually be a collision here, but it doesn't
+ actually matter. As long as the two nodes with the same
+ version are together, it's all fine. */
+ if (tn->version < this->version)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ rb_link_node(&tn->rb, parent, p);
+ rb_insert_color(&tn->rb, list);
}
-static void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
+static void jffs2_free_tmp_dnode_info_list(struct rb_root *list)
{
- struct jffs2_tmp_dnode_info *next;
+ struct rb_node *this;
+ struct jffs2_tmp_dnode_info *tn;
+
+ this = list->rb_node;
+
+ /* Now at bottom of tree */
+ while (this) {
+ if (this->rb_left)
+ this = this->rb_left;
+ else if (this->rb_right)
+ this = this->rb_right;
+ else {
+ tn = rb_entry(this, struct jffs2_tmp_dnode_info, rb);
+ jffs2_free_full_dnode(tn->fn);
+ jffs2_free_tmp_dnode_info(tn);
+
+ this = this->rb_parent;
+ if (!this)
+ break;
- while (tn) {
- next = tn;
- tn = tn->next;
- jffs2_free_full_dnode(next->fn);
- jffs2_free_tmp_dnode_info(next);
+ if (this->rb_left == &tn->rb)
+ this->rb_left = NULL;
+ else if (this->rb_right == &tn->rb)
+ this->rb_right = NULL;
+ else BUG();
+ }
}
+ list->rb_node = NULL;
}
static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
@@ -108,12 +141,13 @@ static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_r
with this ino, returning the former in order of version */
int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
- struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
+ struct rb_root *tnp, struct jffs2_full_dirent **fdp,
uint32_t *highest_version, uint32_t *latest_mctime,
uint32_t *mctime_ver)
{
struct jffs2_raw_node_ref *ref, *valid_ref;
- struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL;
+ struct jffs2_tmp_dnode_info *tn;
+ struct rb_root ret_tn = RB_ROOT;
struct jffs2_full_dirent *fd, *ret_fd = NULL;
union jffs2_node_union node;
size_t retlen;
@@ -127,7 +161,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
valid_ref = jffs2_first_valid_node(f->inocache->nodes);
- if (!valid_ref)
+ if (!valid_ref && (f->inocache->ino != 1))
printk(KERN_WARNING "Eep. No valid nodes for ino #%u\n", f->inocache->ino);
while (valid_ref) {
@@ -450,7 +484,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
return 0;
free_out:
- jffs2_free_tmp_dnode_info_list(ret_tn);
+ jffs2_free_tmp_dnode_info_list(&ret_tn);
jffs2_free_full_dirent_list(ret_fd);
return err;
}
@@ -489,9 +523,13 @@ struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t
void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new)
{
struct jffs2_inode_cache **prev;
- D2(printk(KERN_DEBUG "jffs2_add_ino_cache: Add %p (ino #%u)\n", new, new->ino));
+
spin_lock(&c->inocache_lock);
-
+ if (!new->ino)
+ new->ino = ++c->highest_ino;
+
+ D2(printk(KERN_DEBUG "jffs2_add_ino_cache: Add %p (ino #%u)\n", new, new->ino));
+
prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE];
while ((*prev) && (*prev)->ino < new->ino) {
@@ -506,7 +544,7 @@ void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new
void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
{
struct jffs2_inode_cache **prev;
- D2(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
+ D1(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
spin_lock(&c->inocache_lock);
prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE];
@@ -518,6 +556,14 @@ void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
*prev = old->next;
}
+ /* Free it now unless it's in READING or CLEARING state, which
+ are the transitions upon read_inode() and clear_inode(). The
+ rest of the time we know nobody else is looking at it, and
+ if it's held by read_inode() or clear_inode() they'll free it
+ for themselves. */
+ if (old->state != INO_STATE_READING && old->state != INO_STATE_CLEARING)
+ jffs2_free_inode_cache(old);
+
spin_unlock(&c->inocache_lock);
}
@@ -530,7 +576,6 @@ void jffs2_free_ino_caches(struct jffs2_sb_info *c)
this = c->inocache_list[i];
while (this) {
next = this->next;
- D2(printk(KERN_DEBUG "jffs2_free_ino_caches: Freeing ino #%u at %p\n", this->ino, this));
jffs2_free_inode_cache(this);
this = next;
}
diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h
index a4864d05ea92..b34c397909ef 100644
--- a/fs/jffs2/nodelist.h
+++ b/fs/jffs2/nodelist.h
@@ -7,7 +7,7 @@
*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: nodelist.h,v 1.126 2004/11/19 15:06:29 dedekind Exp $
+ * $Id: nodelist.h,v 1.131 2005/07/05 21:03:07 dwmw2 Exp $
*
*/
@@ -135,6 +135,7 @@ struct jffs2_inode_cache {
#define INO_STATE_CHECKEDABSENT 3 /* Checked, cleared again */
#define INO_STATE_GC 4 /* GCing a 'pristine' node */
#define INO_STATE_READING 5 /* In read_inode() */
+#define INO_STATE_CLEARING 6 /* In clear_inode() */
#define INOCACHE_HASHSIZE 128
@@ -160,7 +161,7 @@ struct jffs2_full_dnode
*/
struct jffs2_tmp_dnode_info
{
- struct jffs2_tmp_dnode_info *next;
+ struct rb_node rb;
struct jffs2_full_dnode *fn;
uint32_t version;
};
@@ -362,6 +363,18 @@ static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
node = node->rb_left;
return rb_entry(node, struct jffs2_node_frag, rb);
}
+
+static inline struct jffs2_node_frag *frag_last(struct rb_root *root)
+{
+ struct rb_node *node = root->rb_node;
+
+ if (!node)
+ return NULL;
+ while(node->rb_right)
+ node = node->rb_right;
+ return rb_entry(node, struct jffs2_node_frag, rb);
+}
+
#define rb_parent(rb) ((rb)->rb_parent)
#define frag_next(frag) rb_entry(rb_next(&(frag)->rb)