changeset 1372:121121d3d18f

Merge changes from David Woodhouse (bring up to date with MTD project)
author gthomas
date Thu, 20 Nov 2003 16:52:35 +0000
parents e4d78a7a628d
children 00a9f2cb1081
files packages/fs/jffs2/current/cdl/jffs2.cdl packages/fs/jffs2/current/include/linux/jffs2.h packages/fs/jffs2/current/include/linux/jffs2_fs_sb.h packages/fs/jffs2/current/src/LICENCE packages/fs/jffs2/current/src/build.c packages/fs/jffs2/current/src/compr.c packages/fs/jffs2/current/src/compr_rtime.c packages/fs/jffs2/current/src/compr_zlib.c packages/fs/jffs2/current/src/dir-ecos.c packages/fs/jffs2/current/src/erase.c packages/fs/jffs2/current/src/file-ecos.c packages/fs/jffs2/current/src/fs-ecos.c packages/fs/jffs2/current/src/gc.c packages/fs/jffs2/current/src/jffs2port.h packages/fs/jffs2/current/src/nodelist.c packages/fs/jffs2/current/src/nodelist.h packages/fs/jffs2/current/src/nodemgmt.c packages/fs/jffs2/current/src/os-ecos.h packages/fs/jffs2/current/src/pushpull.h packages/fs/jffs2/current/src/read.c packages/fs/jffs2/current/src/readinode.c packages/fs/jffs2/current/src/scan.c packages/fs/jffs2/current/src/write.c
diffstat 23 files changed, 1106 insertions(+), 588 deletions(-) [+]
line wrap: on
line diff
--- a/packages/fs/jffs2/current/cdl/jffs2.cdl
+++ b/packages/fs/jffs2/current/cdl/jffs2.cdl
@@ -4,7 +4,7 @@
 #
 #      JFFS2 Filesystem configuration data
 #
-#      $Id: jffs2.cdl,v 1.8 2003/07/15 20:45:32 gthomas Exp $
+#      $Id: jffs2.cdl,v 1.6 2003/11/20 16:41:57 dwmw2 Exp $
 #
 # ====================================================================
 #####ECOSGPLCOPYRIGHTBEGIN####
@@ -73,7 +73,80 @@ cdl_package CYGPKG_FS_JFFS2 {
     implements     CYGINT_IO_FILEIO_FS      
 
     compile        -library=libextras.a fs-ecos.c
-    compile        build.c scan.c malloc-ecos.c nodelist.c nodemgmt.c readinode.c erase.c dir-ecos.c write.c gc.c read.c compr.c compr_zlib.c compr_rtime.c compr_rubin.c file-ecos.c
+    compile        build.c scan.c malloc-ecos.c nodelist.c nodemgmt.c readinode.c erase.c dir-ecos.c write.c gc.c read.c compr.c file-ecos.c
+
+    cdl_option CYGOPT_FS_JFFS2_NAND {
+	display         "Support for NAND flash"
+	flavor          bool
+	define          CONFIG_JFFS2_FS_NAND
+	compile         wbuf.c
+        requires        0
+        description     "
+            Enable support for JFFS2 on NAND flash."
+    }
+
+    cdl_option CYGOPT_FS_JFFS2_DEBUG {
+	display         "Debug level"
+	flavor          data
+        legal_values    0 to 2
+	define          CONFIG_JFFS2_FS_DEBUG
+        description     "
+            Debug verbosity of JFFS2 code. Zero is normal operation
+            without debugging. Level 1 adds extra sanity checks and
+            fairly verbose output. Level 2 is insanely loquacious."
+    }
+
+    cdl_option CYGOPT_FS_JFFS2_COMPRESS {
+        display         "Compress data"
+        flavor          bool
+        define          JFFS2_COMPRESSION
+        default_value   1
+        description     "
+            Compression and decompression are entirely handled by the file
+            system and are fully transparent to applications. However,
+            selecting this option increases the amount of RAM required and
+            slows down read and write operations considerably if you have a
+            slow CPU."
+    }
+
+    cdl_option CYGOPT_FS_JFFS2_COMPRESS_ZLIB {
+        display         "Compress data using zlib"
+        flavor          bool
+        define          JFFS2_USE_ZLIB
+        requires        CYGPKG_COMPRESS_ZLIB
+        requires        CYGOPT_FS_JFFS2_COMPRESS
+        compile         compr_zlib.c
+        default_value   1
+        description     "
+            Use zlib for compression of data. This is the slowest of the
+            compression options available but the most effective."
+    }
+
+    cdl_option CYGOPT_FS_JFFS2_COMPRESS_RTIME {
+        display         "Compress data using rtime"
+        flavor          bool
+        define          JFFS2_USE_RTIME
+        requires        CYGOPT_FS_JFFS2_COMPRESS
+        compile         compr_rtime.c
+        default_value   1
+        description     "
+            Use the rtime algorithm for compression of data. This 
+            simple algorithm often manages to squeeze and extra few
+            bytes from data already compressed with gzip."
+    }
+
+    cdl_option CYGOPT_FS_JFFS2_COMPRESS_RUBIN {
+        display         "Compress data using rubin"
+        flavor          bool
+        define          JFFS2_USE_RTIME
+        requires        CYGOPT_FS_JFFS2_COMPRESS
+        compile         compr_rubin.c
+        default_value   !CYGOPT_FS_JFFS2_COMPRESS_ZLIB
+        description     "
+            Use the rubin algorithm for compression of data. This 
+            simple algorithm is faster than zlib but not quite as
+            effective."
+    }
 
     cdl_option CYGPKG_FS_JFFS2_CFLAGS_ADD {
 	display "Additional compiler flags"
@@ -81,7 +154,7 @@ cdl_package CYGPKG_FS_JFFS2 {
 	no_define
 	# We add -D__ECOS to trigger eCos-specific code in places.
 	# We add -Werror because I find it useful.
-	default_value { "-D__ECOS -nostdinc -iwithprefix include " }
+	default_value { "-D__ECOS -nostdinc -iwithprefix include" }
 	description   "
 	    This option modifies the set of compiler flags for
             building the JFFS2 package.
@@ -110,7 +183,7 @@ cdl_package CYGPKG_FS_JFFS2 {
 	display "JFFS2 FS tests"
 	flavor  data
 	no_define
-	calculated { "tests/fileio1.c tests/jffs2_exerciser.c" }
+	calculated { "tests/fileio1.c" }
             description   "
                 This option specifies the set of tests for the JFFS2 FS package."
         }
--- a/packages/fs/jffs2/current/include/linux/jffs2.h
+++ b/packages/fs/jffs2/current/include/linux/jffs2.h
@@ -1,14 +1,14 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in the 
  * jffs2 directory.
  *
- * $Id: jffs2.h,v 1.30 2003/02/15 00:15:22 dwmw2 Exp $
+ * $Id: jffs2.h,v 1.31 2003/10/04 08:33:05 dwmw2 Exp $
  *
  */
 
--- a/packages/fs/jffs2/current/include/linux/jffs2_fs_sb.h
+++ b/packages/fs/jffs2/current/include/linux/jffs2_fs_sb.h
@@ -1,4 +1,4 @@
-/* $Id: jffs2_fs_sb.h,v 1.39 2003/06/30 14:36:47 dwmw2 Exp $ */
+/* $Id: jffs2_fs_sb.h,v 1.45 2003/10/08 11:46:27 dwmw2 Exp $ */
 
 #ifndef _JFFS2_FS_SB
 #define _JFFS2_FS_SB
@@ -15,6 +15,8 @@
 #define JFFS2_SB_FLAG_RO 1
 #define JFFS2_SB_FLAG_MOUNTING 2
 
+struct jffs2_inodirty;
+
 /* A struct for the overall file system control.  Pointers to
    jffs2_sb_info structs are named `c' in the source code.  
    Nee jffs_control
@@ -52,6 +54,15 @@ struct jffs2_sb_info {
 	uint32_t nr_free_blocks;
 	uint32_t nr_erasing_blocks;
 
+	/* Number of free blocks there must be before we... */
+	uint8_t resv_blocks_write;	/* ... allow a normal filesystem write */
+	uint8_t resv_blocks_deletion;	/* ... allow a normal filesystem deletion */
+	uint8_t resv_blocks_gctrigger;	/* ... wake up the GC thread */
+	uint8_t resv_blocks_gcbad;	/* ... pick a block from the bad_list to GC */
+	uint8_t resv_blocks_gcmerge;	/* ... merge pages when garbage collecting */
+
+	uint32_t nospc_dirty_size;
+
 	uint32_t nr_blocks;
 	struct jffs2_eraseblock *blocks;	/* The whole array of blocks. Used for getting blocks 
 						 * from the offset (blocks[ofs / sector_size]) */
@@ -90,8 +101,7 @@ struct jffs2_sb_info {
 	uint32_t wbuf_ofs;
 	uint32_t wbuf_len;
 	uint32_t wbuf_pagesize;
-	struct work_struct wbuf_task;		/* task for timed wbuf flush */
-	struct timer_list wbuf_timer;		/* timer for flushing wbuf */
+	struct jffs2_inodirty *wbuf_inodes;
 
 	/* Information about out-of-band area usage... */
 	struct nand_oobinfo *oobinfo;
--- a/packages/fs/jffs2/current/src/LICENCE
+++ b/packages/fs/jffs2/current/src/LICENCE
@@ -1,7 +1,7 @@
 The files in this directory and elsewhere which refer to this LICENCE
 file are part of JFFS2, the Journalling Flash File System v2.
 
-	Copyright (C) 2001, 2002 Red Hat, Inc.
+	Copyright (C) 2001-2003 Red Hat, Inc.
 
 JFFS2 is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free
@@ -32,4 +32,4 @@ For information on obtaining alternative
 http://sources.redhat.com/jffs2/jffs2-licence.html
 
 
-	$Id: LICENCE,v 1.1 2002/05/20 14:56:37 dwmw2 Exp $
+	$Id: LICENCE,v 1.2 2003/10/04 08:33:05 dwmw2 Exp $
--- a/packages/fs/jffs2/current/src/build.c
+++ b/packages/fs/jffs2/current/src/build.c
@@ -1,13 +1,13 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: build.c,v 1.46 2003/04/29 17:12:26 gleixner Exp $
+ * $Id: build.c,v 1.55 2003/10/28 17:02:44 dwmw2 Exp $
  *
  */
 
@@ -16,8 +16,7 @@
 #include <linux/slab.h>
 #include "nodelist.h"
 
-int jffs2_build_inode_pass1(struct jffs2_sb_info *, struct jffs2_inode_cache *);
-int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *);
+static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **);
 
 static inline struct jffs2_inode_cache *
 first_inode_chain(int *i, struct jffs2_sb_info *c)
@@ -44,6 +43,41 @@ next_inode(int *i, struct jffs2_inode_ca
 	     ic;					\
 	     ic = next_inode(&i, ic, (c)))
 
+
+static inline void jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+{
+	struct jffs2_full_dirent *fd;
+
+	D1(printk(KERN_DEBUG "jffs2_build_inode building directory inode #%u\n", ic->ino));
+
+	/* For each child, increase nlink */
+	for(fd = ic->scan_dents; fd; fd = fd->next) {
+		struct jffs2_inode_cache *child_ic;
+		if (!fd->ino)
+			continue;
+
+		/* XXX: Can get high latency here with huge directories */
+
+		child_ic = jffs2_get_ino_cache(c, fd->ino);
+		if (!child_ic) {
+			printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
+				  fd->name, fd->ino, ic->ino);
+			continue;
+		}
+
+		if (child_ic->nlink++ && fd->type == DT_DIR) {
+			printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino);
+			if (fd->ino == 1 && ic->ino == 1) {
+				printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n");
+				printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n");
+			}
+			/* What do we do about it? */
+		}
+		D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino));
+		/* Can't free them. We might need them in pass 2 */
+	}
+}
+
 /* Scan plan:
  - Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go
  - Scan directory tree from top down, setting nlink in inocaches
@@ -54,6 +88,7 @@ static int jffs2_build_filesystem(struct
 	int ret;
 	int i;
 	struct jffs2_inode_cache *ic;
+	struct jffs2_full_dirent *dead_fds = NULL;
 
 	/* First, scan the medium and build all the inode caches with
 	   lists of physical nodes */
@@ -71,47 +106,51 @@ static int jffs2_build_filesystem(struct
 	/* 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));
-		ret = jffs2_build_inode_pass1(c, ic);
-		if (ret) {
-			D1(printk(KERN_WARNING "Eep. jffs2_build_inode_pass1 for ino %d returned %d\n", ic->ino, ret));
-			return ret;
+
+		D1(BUG_ON(ic->ino > c->highest_ino));
+
+		if (ic->scan_dents) {
+			jffs2_build_inode_pass1(c, ic);
+			cond_resched();
 		}
-		cond_resched();
 	}
 	D1(printk(KERN_DEBUG "Pass 1 complete\n"));
-	D1(jffs2_dump_block_lists(c));
 
 	/* Next, scan for inodes with nlink == 0 and remove them. If
 	   they were directories, then decrement the nlink of their
 	   children too, and repeat the scan. As that's going to be
 	   a fairly uncommon occurrence, it's not so evil to do it this
 	   way. Recursion bad. */
-	do { 
-		D1(printk(KERN_DEBUG "Pass 2 (re)starting\n"));
-		ret = 0;
-		for_each_inode(i, c, ic) {
-			D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes));
-			if (ic->nlink)
-				continue;
+	D1(printk(KERN_DEBUG "Pass 2 starting\n"));
+
+	for_each_inode(i, c, ic) {
+		D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes));
+		if (ic->nlink)
+			continue;
 			
-			/* XXX: Can get high latency here. Move the cond_resched() from the end of the loop? */
+		jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
+		cond_resched();
+	} 
+
+	D1(printk(KERN_DEBUG "Pass 2a starting\n"));
 
-			ret = jffs2_build_remove_unlinked_inode(c, ic);
-			if (ret)
-				break;
-		/* -EAGAIN means the inode's nlink was zero, so we deleted it,
-		   and furthermore that it had children and their nlink has now
-		   gone to zero too. So we have to restart the scan. */
-		} 
-		D1(jffs2_dump_block_lists(c));
+	while (dead_fds) {
+		struct jffs2_inode_cache *ic;
+		struct jffs2_full_dirent *fd = dead_fds;
+
+		dead_fds = fd->next;
 
-		cond_resched();
-	
-	} while(ret == -EAGAIN);
+		ic = jffs2_get_ino_cache(c, fd->ino);
+		D1(printk(KERN_DEBUG "Removing dead_fd ino #%u (\"%s\"), ic at %p\n", fd->ino, fd->name, ic));
+
+		if (ic)
+			jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
+		jffs2_free_full_dirent(fd);
+	}
 
 	D1(printk(KERN_DEBUG "Pass 2 complete\n"));
 	
-	/* Finally, we can scan again and free the dirent nodes and scan_info structs */
+	/* Finally, we can scan again and free the dirent structs */
 	for_each_inode(i, c, ic) {
 		struct jffs2_full_dirent *fd;
 		D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes));
@@ -133,49 +172,10 @@ static int jffs2_build_filesystem(struct
 	return ret;
 }
 
-int jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
-{
-	struct jffs2_full_dirent *fd;
-
-	D1(printk(KERN_DEBUG "jffs2_build_inode building inode #%u\n", ic->ino));
-
-	if (ic->ino > c->highest_ino)
-		c->highest_ino = ic->ino;
-
-	/* For each child, increase nlink */
-	for(fd=ic->scan_dents; fd; fd = fd->next) {
-		struct jffs2_inode_cache *child_ic;
-		if (!fd->ino)
-			continue;
-		
-		/* XXX: Can get high latency here with huge directories */
-
-		child_ic = jffs2_get_ino_cache(c, fd->ino);
-		if (!child_ic) {
-			printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
-				  fd->name, fd->ino, ic->ino);
-			continue;
-		}
-
-		if (child_ic->nlink++ && fd->type == DT_DIR) {
-			printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino);
-			if (fd->ino == 1 && ic->ino == 1) {
-				printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n");
-				printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n");
-			}
-			/* What do we do about it? */
-		}
-		D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino));
-		/* Can't free them. We might need them in pass 2 */
-	}
-	return 0;
-}
-
-int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_full_dirent **dead_fds)
 {
 	struct jffs2_raw_node_ref *raw;
 	struct jffs2_full_dirent *fd;
-	int ret = 0;
 
 	D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino));
 	
@@ -214,19 +214,82 @@ int jffs2_build_remove_unlinked_inode(st
 				jffs2_free_full_dirent(fd);
 				continue;
 			}
-			jffs2_free_full_dirent(fd);
+
+			/* Reduce nlink of the child. If it's now zero, stick it on the 
+			   dead_fds list to be cleaned up later. Else just free the fd */
+
 			child_ic->nlink--;
+			
+			if (!child_ic->nlink) {
+				D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got zero nlink. Adding to dead_fds list.\n",
+					  fd->ino, fd->name));
+				fd->next = *dead_fds;
+				*dead_fds = fd;
+			} else {
+				D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got nlink %d. Ignoring.\n",
+					  fd->ino, fd->name, child_ic->nlink));
+				jffs2_free_full_dirent(fd);
+			}
 		}
-		ret = -EAGAIN;
 	}
 
 	/*
 	   We don't delete the inocache from the hash list and free it yet. 
 	   The erase code will do that, when all the nodes are completely gone.
 	*/
+}
 
-	return ret;
-}
+static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c)
+{
+	uint32_t size;
+
+	/* Deletion should almost _always_ be allowed. We're fairly
+	   buggered once we stop allowing people to delete stuff
+	   because there's not enough free space... */
+	c->resv_blocks_deletion = 2;
+
+	/* Be conservative about how much space we need before we allow writes. 
+	   On top of that which is required for deletia, require an extra 2%
+	   of the medium to be available, for overhead caused by nodes being
+	   split across blocks, etc. */
+
+	size = c->flash_size / 50; /* 2% of flash size */
+	size += c->nr_blocks * 100; /* And 100 bytes per eraseblock */
+	size += c->sector_size - 1; /* ... and round up */
+
+	c->resv_blocks_write = c->resv_blocks_deletion + (size / c->sector_size);
+
+	/* When do we let the GC thread run in the background */
+
+	c->resv_blocks_gctrigger = c->resv_blocks_write + 1;
+
+	/* When do we allow garbage collection to merge nodes to make 
+	   long-term progress at the expense of short-term space exhaustion? */
+	c->resv_blocks_gcmerge = c->resv_blocks_deletion + 1;
+
+	/* When do we allow garbage collection to eat from bad blocks rather
+	   than actually making progress? */
+	c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2;
+
+	/* If there's less than this amount of dirty space, don't bother
+	   trying to GC to make more space. It'll be a fruitless task */
+	c->nospc_dirty_size = c->sector_size + (c->flash_size / 100);
+
+	D1(printk(KERN_DEBUG "JFFS2 trigger levels (size %d KiB, block size %d KiB, %d blocks)\n",
+		  c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks));
+	D1(printk(KERN_DEBUG "Blocks required to allow deletion:    %d (%d KiB)\n",
+		  c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024));
+	D1(printk(KERN_DEBUG "Blocks required to allow writes:      %d (%d KiB)\n",
+		  c->resv_blocks_write, c->resv_blocks_write*c->sector_size/1024));
+	D1(printk(KERN_DEBUG "Blocks required to quiesce GC thread: %d (%d KiB)\n",
+		  c->resv_blocks_gctrigger, c->resv_blocks_gctrigger*c->sector_size/1024));
+	D1(printk(KERN_DEBUG "Blocks required to allow GC merges:   %d (%d KiB)\n",
+		  c->resv_blocks_gcmerge, c->resv_blocks_gcmerge*c->sector_size/1024));
+	D1(printk(KERN_DEBUG "Blocks required to GC bad blocks:     %d (%d KiB)\n",
+		  c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024));
+	D1(printk(KERN_DEBUG "Amount of dirty space required to GC: %d bytes\n",
+		  c->nospc_dirty_size));
+} 
 
 int jffs2_do_mount_fs(struct jffs2_sb_info *c)
 {
@@ -276,5 +339,8 @@ int jffs2_do_mount_fs(struct jffs2_sb_in
 		kfree(c->blocks);
 		return -EIO;
 	}
+
+	jffs2_calc_trigger_levels(c);
+
 	return 0;
 }
--- a/packages/fs/jffs2/current/src/compr.c
+++ b/packages/fs/jffs2/current/src/compr.c
@@ -1,31 +1,21 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
  * Created by Arjan van de Ven <arjanv@redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: compr.c,v 1.26 2003/01/12 13:21:28 dwmw2 Exp $
+ * $Id: compr.c,v 1.29 2003/11/20 16:40:35 dwmw2 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 <linux/slab.h>
 #include <linux/jffs2.h>
 
 int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
@@ -40,7 +30,7 @@ void jffs2_dynrubin_decompress(unsigned 
 
 /* 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
@@ -55,44 +45,55 @@ void jffs2_dynrubin_decompress(unsigned 
  * 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, 
+unsigned char jffs2_compress(unsigned char *data_in, unsigned char **cpage_out, 
 		    uint32_t *datalen, uint32_t *cdatalen)
 {
+#ifdef JFFS2_COMPRESSION
 	int ret;
 
+	*cpage_out = kmalloc(*cdatalen, GFP_KERNEL);
+	if (!*cpage_out) {
+		printk(KERN_WARNING "No memory for compressor allocation. Compression failed\n");
+		return JFFS2_COMPR_NONE;
+	}
+
+#ifdef JFFS2_USE_ZLIB
 	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 */
+#endif
+#ifdef JFFS2_USE_DYNRUBIN
 	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 */
+#ifdef JFFS2_USE_RUBINMIPS
 	ret = jffs2_rubinmips_compress(data_in, cpage_out, datalen, cdatalen);
 	if (!ret) {
 		return JFFS2_COMPR_RUBINMIPS;
 	}
 #endif
+#ifdef JFFS2_USE_RTIME
 	/* 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		
+#endif
+	kfree(*cpage_out);
+#endif /* Compression */
+	*cpage_out = data_in;
+	*datalen = *cdatalen;
 	return JFFS2_COMPR_NONE; /* We failed to compress */
-
 }
 
+void jffs2_free_comprbuf(unsigned char *orig, unsigned char *comprbuf)
+{
+	if (orig != comprbuf)
+		kfree(comprbuf);
+}
 
 int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, 
 		     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen)
@@ -106,30 +107,27 @@ int jffs2_decompress(unsigned char compr
 	case JFFS2_COMPR_ZERO:
 		memset(data_out, 0, datalen);
 		break;
-
+#ifdef JFFS2_USE_ZLIB
 	case JFFS2_COMPR_ZLIB:
 		jffs2_zlib_decompress(cdata_in, data_out, cdatalen, datalen);
 		break;
-
+#endif
+#ifdef JFFS2_USE_RTIME
 	case JFFS2_COMPR_RTIME:
 		jffs2_rtime_decompress(cdata_in, data_out, cdatalen, datalen);
 		break;
-
+#endif
+#ifdef JFFS2_USE_RUBINMIPS
 	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;
+#endif
+#ifdef JFFS2_USE_DYNRUBIN
 	case JFFS2_COMPR_DYNRUBIN:
-#if 1 /* Phase this one out */
+
 		jffs2_dynrubin_decompress(cdata_in, data_out, cdatalen, datalen);
-#else
-		printk(KERN_WARNING "JFFS2: Dynrubin compression encountered but support not compiled in!\n");
+		break;
 #endif
-		break;
-
 	default:
 		printk(KERN_NOTICE "Unknown JFFS2 compression type 0x%02x\n", comprtype);
 		return -EIO;
--- a/packages/fs/jffs2/current/src/compr_rtime.c
+++ b/packages/fs/jffs2/current/src/compr_rtime.c
@@ -1,13 +1,13 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
  * Created by Arjan van de Ven <arjanv@redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: compr_rtime.c,v 1.10 2003/05/11 10:47:13 dwmw2 Exp $
+ * $Id: compr_rtime.c,v 1.11 2003/10/04 08:33:06 dwmw2 Exp $
  *
  *
  * Very simple lz77-ish encoder.
--- a/packages/fs/jffs2/current/src/compr_zlib.c
+++ b/packages/fs/jffs2/current/src/compr_zlib.c
@@ -1,13 +1,13 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: compr_zlib.c,v 1.23 2003/05/26 09:15:19 dwmw2 Exp $
+ * $Id: compr_zlib.c,v 1.24 2003/10/04 08:33:06 dwmw2 Exp $
  *
  */
 
--- a/packages/fs/jffs2/current/src/dir-ecos.c
+++ b/packages/fs/jffs2/current/src/dir-ecos.c
@@ -7,7 +7,7 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: dir-ecos.c,v 1.3 2003/01/22 00:04:13 dwmw2 Exp $
+ * $Id: dir-ecos.c,v 1.4 2003/11/20 16:41:58 dwmw2 Exp $
  *
  */
 
@@ -162,7 +162,6 @@ int jffs2_mkdir (struct inode *dir_i, st
 	struct jffs2_full_dirent *fd;
 	int namelen;
 	uint32_t alloclen, phys_ofs;
-	uint32_t writtenlen;
 	int ret;
 
 	mode |= S_IFDIR;
@@ -197,7 +196,7 @@ int jffs2_mkdir (struct inode *dir_i, st
 	ri->data_crc = cpu_to_je32(0);
 	ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 	
-	fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, &writtenlen);
+	fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
 
 	jffs2_free_raw_inode(ri);
 
@@ -214,20 +213,12 @@ int jffs2_mkdir (struct inode *dir_i, st
 	f->metadata = fn;
 	up(&f->sem);
 
-	/* Work out where to put the dirent node now. */
-	writtenlen = PAD(writtenlen);
-	phys_ofs += writtenlen;
-	alloclen -= writtenlen;
-
-	if (alloclen < sizeof(*rd)+namelen) {
-		/* Not enough space left in this chunk. Get some more */
-		jffs2_complete_reservation(c);
-		ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
-		if (ret) {
-			/* Eep. */
-			jffs2_clear_inode(inode);
-			return ret;
-		}
+	jffs2_complete_reservation(c);
+	ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+	if (ret) {
+		/* Eep. */
+		jffs2_clear_inode(inode);
+		return ret;
 	}
 	
 	rd = jffs2_alloc_raw_dirent();
@@ -255,7 +246,7 @@ int jffs2_mkdir (struct inode *dir_i, st
 	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
 	rd->name_crc = cpu_to_je32(crc32(0, d_name->name, namelen));
 
-	fd = jffs2_write_dirent(c, dir_f, rd, d_name->name, namelen, phys_ofs, &writtenlen);
+	fd = jffs2_write_dirent(c, dir_f, rd, d_name->name, namelen, phys_ofs, ALLOC_NORMAL);
 	
 	jffs2_complete_reservation(c);
 	jffs2_free_raw_dirent(rd);
--- a/packages/fs/jffs2/current/src/erase.c
+++ b/packages/fs/jffs2/current/src/erase.c
@@ -1,13 +1,13 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: erase.c,v 1.51 2003/05/11 22:47:36 dwmw2 Exp $
+ * $Id: erase.c,v 1.57 2003/11/04 14:46:13 dwmw2 Exp $
  *
  */
 
@@ -101,7 +101,7 @@ void jffs2_erase_block(struct jffs2_sb_i
 	jffs2_erase_failed(c, jeb);
 }
 
-void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
+void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
 {
 	struct jffs2_eraseblock *jeb;
 
@@ -118,15 +118,21 @@ void jffs2_erase_pending_blocks(struct j
 			spin_unlock(&c->erase_completion_lock);
 			jffs2_mark_erased_block(c, jeb);
 
+			if (!--count) {
+				D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n"));
+				goto done;
+			}
+
 		} else if (!list_empty(&c->erase_pending_list)) {
 			jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
 			D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
 			list_del(&jeb->list);
 			c->erasing_size += c->sector_size;
+			c->wasted_size -= jeb->wasted_size;
 			c->free_size -= jeb->free_size;
 			c->used_size -= jeb->used_size;
 			c->dirty_size -= jeb->dirty_size;
-			jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
+			jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
 			jffs2_free_all_node_refs(c, jeb);
 			list_add(&jeb->list, &c->erasing_list);
 			spin_unlock(&c->erase_completion_lock);
@@ -143,6 +149,7 @@ void jffs2_erase_pending_blocks(struct j
 	}
 
 	spin_unlock(&c->erase_completion_lock);
+ done:
 	D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
 
 	up(&c->erase_free_sem);
@@ -369,29 +376,30 @@ static void jffs2_mark_erased_block(stru
 			.totlen =	cpu_to_je32(c->cleanmarker_size)
 		};
 
-		marker.hdr_crc = cpu_to_je32(crc32(0, &marker, je32_to_cpu(marker.totlen) - 4));
+		marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
 
-		ret = jffs2_flash_write(c, jeb->offset, je32_to_cpu(marker.totlen), &retlen, (char *)&marker);
+		/* We only write the header; the rest was noise or padding anyway */
+		ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
 		if (ret) {
 			printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
 			       jeb->offset, ret);
 			goto bad2;
 		}
-		if (retlen != je32_to_cpu(marker.totlen)) {
+		if (retlen != sizeof(marker)) {
 			printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %zd\n",
-			       jeb->offset, je32_to_cpu(marker.totlen), retlen);
+			       jeb->offset, sizeof(marker), retlen);
 			goto bad2;
 		}
 
 		marker_ref->next_in_ino = NULL;
 		marker_ref->next_phys = NULL;
 		marker_ref->flash_offset = jeb->offset | REF_NORMAL;
-		marker_ref->totlen = PAD(je32_to_cpu(marker.totlen));
+		marker_ref->__totlen = c->cleanmarker_size;
 			
 		jeb->first_node = jeb->last_node = marker_ref;
 			
-		jeb->free_size = c->sector_size - marker_ref->totlen;
-		jeb->used_size = marker_ref->totlen;
+		jeb->free_size = c->sector_size - c->cleanmarker_size;
+		jeb->used_size = c->cleanmarker_size;
 		jeb->dirty_size = 0;
 		jeb->wasted_size = 0;
 	}
--- a/packages/fs/jffs2/current/src/file-ecos.c
+++ b/packages/fs/jffs2/current/src/file-ecos.c
@@ -7,7 +7,7 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: file-ecos.c,v 1.5 2003/01/22 00:04:13 dwmw2 Exp $
+ * $Id: file-ecos.c,v 1.6 2003/11/20 16:41:58 dwmw2 Exp $
  *
  */
 
@@ -115,7 +115,7 @@ int jffs2_prepare_write (struct inode *d
 		ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
 		ri.data_crc = cpu_to_je32(0);
 		
-		fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, NULL);
+		fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
 		jffs2_complete_reservation(c);
 		if (IS_ERR(fn)) {
 			ret = PTR_ERR(fn);
--- a/packages/fs/jffs2/current/src/fs-ecos.c
+++ b/packages/fs/jffs2/current/src/fs-ecos.c
@@ -8,7 +8,7 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: fs-ecos.c,v 1.9 2003/07/15 20:42:33 gthomas Exp $
+ * $Id: fs-ecos.c,v 1.11 2003/11/20 16:41:58 dwmw2 Exp $
  *
  */
 
@@ -567,7 +567,7 @@ static int jffs2_mount(cyg_fstab_entry *
 		jffs2_sb->s_root->i_count = 1;	// Ensures the root inode is always in ram until umount
 
 		D2(printf("jffs2_mount erasing pending blocks\n"));
-		jffs2_erase_pending_blocks(c);
+		jffs2_erase_pending_blocks(c,0);
 	}
 	mte->data = (CYG_ADDRWORD) jffs2_sb;
 
@@ -1705,6 +1705,21 @@ struct inode *new_inode(struct super_blo
 
 	return inode;
 }
+struct inode *ilookup(struct super_block *sb, cyg_uint32 ino)
+{
+	struct inode *inode = NULL;
+
+	D2(printf("ilookup\n"));
+	// Check for this inode in the cache
+	for (inode = sb->s_root; inode != NULL; inode = inode->i_cache_next) {
+		if (inode->i_ino == ino) {
+			inode->i_count++;
+			break;
+		}
+	}
+
+	return inode;
+}
 
 struct inode *iget(struct super_block *sb, cyg_uint32 ino)
 {
@@ -1722,12 +1737,9 @@ struct inode *iget(struct super_block *s
 
 	D2(printf("iget\n"));
 
-	// Check for this inode in the cache
-	for (inode = sb->s_root; inode != NULL; inode = inode->i_cache_next) {
-		if (inode->i_ino == ino)
-			return inode;
-	}
-	inode = NULL;
+	inode = ilookup(sb, ino);
+	if (inode)
+		return inode;
 
 	// Not cached, so malloc it
 	inode = new_inode(sb);
@@ -1736,7 +1748,7 @@ struct inode *iget(struct super_block *s
 
 	inode->i_ino = ino;
 	jffs2_read_inode(inode);
-
+	inode->i_count = 1;
 	return inode;
 }
 
@@ -1851,7 +1863,7 @@ cyg_bool jffs2_flash_write(struct jffs2_
 }
 
 int
-jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *vecs,
+jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs,
 		   unsigned long count, loff_t to, size_t * retlen)
 {
 	unsigned long i;
--- a/packages/fs/jffs2/current/src/gc.c
+++ b/packages/fs/jffs2/current/src/gc.c
@@ -1,13 +1,13 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: gc.c,v 1.103 2003/05/22 18:01:02 dwmw2 Exp $
+ * $Id: gc.c,v 1.129 2003/11/20 16:40:14 dwmw2 Exp $
  *
  */
 
@@ -49,7 +49,7 @@ static struct jffs2_eraseblock *jffs2_fi
 	   put the clever wear-levelling algorithms. Eventually.  */
 	/* We possibly want to favour the dirtier blocks more when the
 	   number of free blocks is low. */
-	if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > JFFS2_RESERVED_BLOCKS_GCBAD) {
+	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;
 	} else if (n < 50 && !list_empty(&c->erasable_list)) {
@@ -115,7 +115,6 @@ int jffs2_garbage_collect_pass(struct jf
 	struct jffs2_inode_cache *ic;
 	struct jffs2_eraseblock *jeb;
 	struct jffs2_raw_node_ref *raw;
-	uint32_t inum;
 	int ret = 0;
 
 	if (down_interruptible(&c->alloc_sem))
@@ -186,7 +185,7 @@ int jffs2_garbage_collect_pass(struct jf
 		ic->state = INO_STATE_CHECKING;
 		spin_unlock(&c->inocache_lock);
 
-		D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%d\n", ic->ino));
+		D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%u\n", ic->ino));
 
 		ret = jffs2_do_crccheck_inode(c, ic);
 		if (ret)
@@ -223,17 +222,21 @@ int jffs2_garbage_collect_pass(struct jf
 			
 	while(ref_obsolete(raw)) {
 		D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw)));
-		jeb->gc_node = raw = raw->next_phys;
-		if (!raw) {
+		raw = raw->next_phys;
+		if (unlikely(!raw)) {
 			printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n");
 			printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n", 
 			       jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size);
+			jeb->gc_node = raw;
 			spin_unlock(&c->erase_completion_lock);
 			up(&c->alloc_sem);
 			BUG();
 		}
 	}
+	jeb->gc_node = raw;
+
 	D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", ref_offset(raw)));
+
 	if (!raw->next_in_ino) {
 		/* Inode-less node. Clean marker, snapshot or something like that */
 		/* FIXME: If it's something that needs to be copied, including something
@@ -243,13 +246,15 @@ int jffs2_garbage_collect_pass(struct jf
 		up(&c->alloc_sem);
 		goto eraseit_lock;
 	}
-						     
-	inum = jffs2_raw_ref_to_inum(raw);
-	D1(printk(KERN_DEBUG "Inode number is #%u\n", inum));
+
+	ic = jffs2_raw_ref_to_ic(raw);
+
+	/* We need to hold the inocache */
+	spin_lock(&c->inocache_lock);
 
 	spin_unlock(&c->erase_completion_lock);
 
-	D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), inum));
+	D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), ic->ino));
 
 	/* Three possibilities:
 	   1. Inode is already in-core. We must iget it and do proper
@@ -259,11 +264,6 @@ int jffs2_garbage_collect_pass(struct jf
 	   3. Inode is not in-core, node is not pristine. We must iget()
 	      and take the slow path.
 	*/
-	spin_lock(&c->inocache_lock);
-	ic = jffs2_get_ino_cache(c, inum);
-
-	/* This should never fail unless I'm particularly stupid.
-	   So we don't check before dereferencing it */
 
 	switch(ic->state) {
 	case INO_STATE_CHECKEDABSENT:
@@ -274,29 +274,29 @@ int jffs2_garbage_collect_pass(struct jf
 		if (ref_flags(raw) == REF_PRISTINE)
 			ic->state = INO_STATE_GC;
 		else {
-			D1(printk("Ino #%u is absent but node not REF_PRISTINE. Reading.\n", 
-				  inum));
+			D1(printk(KERN_DEBUG "Ino #%u is absent but node not REF_PRISTINE. Reading.\n", 
+				  ic->ino));
 		}
 		break;
 
 	case INO_STATE_PRESENT:
-	case INO_STATE_UNCHECKED:
-		/* It's in-core or hasn't been checked. GC must iget() it. */
+		/* It's in-core. GC must iget() it. */
 		break;
 
+	case INO_STATE_UNCHECKED:
 	case INO_STATE_CHECKING:
+	case INO_STATE_GC:
 		/* Should never happen. We should have finished checking
-		   by the time we actually start doing any GC. */
+		   by the time we actually start doing any GC, and since 
+		   we're holding the alloc_sem, no other garbage collection 
+		   can happen.
+		*/
+		printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n",
+		       ic->ino, ic->state);
+		up(&c->alloc_sem);
+		spin_unlock(&c->inocache_lock);
 		BUG();
 
-	
-	case INO_STATE_GC:
-		/* Should never happen. We are holding the alloc_sem, 
-		   no other garbage collection can happen. Note that we
-		   do depend on this later when deciding to do a simple
-		   node copy */
-		BUG();
-			 
 	case INO_STATE_READING:
 		/* Someone's currently trying to read it. We must wait for
 		   them to finish and then go through the full iget() route
@@ -306,7 +306,7 @@ int jffs2_garbage_collect_pass(struct jf
 
 		up(&c->alloc_sem);
 		D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n",
-			  inum, ic->state));
+			  ic->ino, ic->state));
 		sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
 		/* And because we dropped the alloc_sem we must start again from the 
 		   beginning. Ponder chance of livelock here -- we're returning success
@@ -319,26 +319,37 @@ int jffs2_garbage_collect_pass(struct jf
 		   A: Small enough that I don't care :) 
 		*/
 		return 0;
-
 	}
 
-	spin_unlock(&c->inocache_lock);
-
 	/* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the
 	   node intact, and we don't have to muck about with the fragtree etc. 
 	   because we know it's not in-core. If it _was_ in-core, we go through
 	   all the iget() crap anyway */
 
 	if (ic->state == INO_STATE_GC) {
+		spin_unlock(&c->inocache_lock);
+
 		ret = jffs2_garbage_collect_pristine(c, ic, raw);
-		jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT);
 
-		if (ret != -EBADFD)
+		spin_lock(&c->inocache_lock);
+		ic->state = INO_STATE_CHECKEDABSENT;
+		wake_up(&c->inocache_wq);
+
+		if (ret != -EBADFD) {
+			spin_unlock(&c->inocache_lock);
 			goto release_sem;
+		}
 
-		/* Fall through if it wanted us to */
+		/* Fall through if it wanted us to, with inocache_lock held */
 	}
 
+	/* FIXME: We still have the inocache_lock held. This is ugly.
+	   It's done to prevent the fairly unlikely race where the
+	   gcblock is entirely obsoleted by the final close of a file
+	   which had the only valid nodes in the block, followed by
+	   erasure, followed by freeing of the ic because the erased
+	   block(s) held _all_ the nodes of that inode.... never been
+	   seen but it's vaguely possible. */
 	ret = jffs2_garbage_collect_live(c, jeb, raw, ic);
 
  release_sem:
@@ -362,9 +373,8 @@ int jffs2_garbage_collect_pass(struct jf
 	return ret;
 }
 
-
 static int jffs2_garbage_collect_live(struct jffs2_sb_info *c,  struct jffs2_eraseblock *jeb,
-			       struct jffs2_raw_node_ref *raw, struct jffs2_inode_cache *ic)
+				      struct jffs2_raw_node_ref *raw, struct jffs2_inode_cache *ic)
 {
 	struct jffs2_inode_info *f;
 	struct jffs2_node_frag *frag;
@@ -374,13 +384,65 @@ static int jffs2_garbage_collect_live(st
 	struct inode *inode;
 	int ret = 0;
 
-	inode = iget(OFNI_BS_2SFFJ(c), ic->ino);
+	if (!ic->nlink) {
+		int inum = ic->ino;
+
+		spin_unlock(&c->inocache_lock);
+
+		/* The inode has zero nlink but its nodes weren't yet marked
+		   obsolete. This has to be because we're still waiting for 
+		   the final (close() and) iput() to happen.
+
+		   There's a possibility that the final iput() could have 
+		   happened while we were contemplating. In order to ensure
+		   that we don't cause a new read_inode() (which would fail)
+		   for the inode in question, we use ilookup() in this case
+		   instead of iget().
+
+		   The nlink can't _become_ zero at this point because we're 
+		   holding the alloc_sem, and jffs2_do_unlink() would also
+		   need that while decrementing nlink on any inode.
+		*/
+		inode = ilookup(OFNI_BS_2SFFJ(c), inum);
+		if (!inode) {
+			D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n",
+				  inum));
+
+			spin_lock(&c->inocache_lock);
+			ic = jffs2_get_ino_cache(c, inum);
+			if (!ic) {
+				D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum));
+				spin_unlock(&c->inocache_lock);
+				return 0;
+			}
+			if (ic->state != INO_STATE_CHECKEDABSENT) {
+				/* Wait for progress. Don't just loop */
+				D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n",
+					  ic->ino, ic->state));
+				sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+			} else {
+				spin_unlock(&c->inocache_lock);
+			}
+
+			return 0;
+		}
+	} else {
+		spin_unlock(&c->inocache_lock);
+
+		/* Inode has links to it still; they're not going away because
+		   jffs2_do_unlink() would need the alloc_sem and we have it.
+		   Just iget() it, and if read_inode() is necessary that's OK.
+		*/
+		inode = iget(OFNI_BS_2SFFJ(c), ic->ino);
+		if (!inode)
+			return -ENOMEM;
+	}
 	if (is_bad_inode(inode)) {
-		printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", ic->ino);
+		printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d, state %d\n",
+		       ic->ino, ic->nlink, ic->state);
 		/* NB. This will happen again. We need to do something appropriate here. */
-		up(&c->alloc_sem);
-		iput(inode);
-		return -EIO;
+		ret = -EIO;
+		goto put_out;
 	}
 
 	f = JFFS2_INODE_INFO(inode);
@@ -389,11 +451,21 @@ static int jffs2_garbage_collect_live(st
 	/* Now we have the lock for this inode. Check that it's still the one at the head
 	   of the list. */
 
+	spin_lock(&c->erase_completion_lock);
+
+	if (c->gcblock != jeb) {
+		spin_unlock(&c->erase_completion_lock);
+		D1(printk(KERN_DEBUG "GC block is no longer gcblock. Restart\n"));
+		goto upnout;
+	}
 	if (ref_obsolete(raw)) {
+		spin_unlock(&c->erase_completion_lock);
 		D1(printk(KERN_DEBUG "node to be GC'd was obsoleted in the meantime.\n"));
 		/* They'll call again */
 		goto upnout;
 	}
+	spin_unlock(&c->erase_completion_lock);
+
 	/* OK. Looks safe. And nobody can get us now because we have the semaphore. Move the block */
 	if (f->metadata && f->metadata->raw == raw) {
 		fn = f->metadata;
@@ -406,30 +478,6 @@ static int jffs2_garbage_collect_live(st
 		if (frag->node && frag->node->raw == raw) {
 			fn = frag->node;
 			end = frag->ofs + frag->size;
-#if 1 /* Temporary debugging sanity checks, till we're ready to _trust_ the REF_PRISTINE flag stuff */ 
-			if (!nrfrags && ref_flags(fn->raw) == REF_PRISTINE) {
-				if (fn->frags > 1) {
-					printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(raw), fn->frags);
-					mark_ref_normal(raw);
-				}
-				/* A hole node which isn't multi-page should be garbage-collected
-				   and merged anyway, so we just check for the frag size here,
-				   rather than mucking around with actually reading the node
-				   and checking the compression type, which is the real way
-				   to tell a hole node. */
-				if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE) {
-					printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n",
-					       ref_offset(raw));
-					mark_ref_normal(raw);
-				}
-
-				if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE) {
-					printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n",
-					       ref_offset(raw), frag->ofs, frag->ofs+frag->size);
-					mark_ref_normal(raw);
-				}
-			}
-#endif
 			if (!nrfrags++)
 				start = frag->ofs;
 			if (nrfrags == frag->node->frags)
@@ -478,6 +526,7 @@ static int jffs2_garbage_collect_live(st
 	}
  upnout:
 	up(&f->sem);
+ put_out:
 	iput(inode);
 
 	return ret;
@@ -492,29 +541,32 @@ static int jffs2_garbage_collect_pristin
 	size_t retlen;
 	int ret;
 	uint32_t phys_ofs, alloclen;
-	uint32_t crc;
+	uint32_t crc, rawlen;
+	int retried = 0;
 
 	D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw)));
 
+	rawlen = ref_totlen(c, c->gcblock, raw);
+
 	/* Ask for a small amount of space (or the totlen if smaller) because we
 	   don't want to force wastage of the end of a block if splitting would
 	   work. */
-	ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, raw->totlen),
-				     &phys_ofs, &alloclen);
+	ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, 
+					      rawlen), &phys_ofs, &alloclen);
 	if (ret)
 		return ret;
 
-	if (alloclen < raw->totlen) {
+	if (alloclen < rawlen) {
 		/* Doesn't fit untouched. We'll go the old route and split it */
 		return -EBADFD;
 	}
 
-	node = kmalloc(raw->totlen, GFP_KERNEL);
+	node = kmalloc(rawlen, GFP_KERNEL);
 	if (!node)
                return -ENOMEM;
 
-	ret = jffs2_flash_read(c, ref_offset(raw), raw->totlen, &retlen, (char *)node);
-	if (!ret && retlen != raw->totlen)
+	ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node);
+	if (!ret && retlen != rawlen)
 		ret = -EIO;
 	if (ret)
 		goto out_node;
@@ -573,16 +625,18 @@ static int jffs2_garbage_collect_pristin
 		ret = -ENOMEM;
 		goto out_node;
 	}
-	nraw->flash_offset = phys_ofs;
-	nraw->totlen = raw->totlen;
-	nraw->next_phys = NULL;
 
 	/* OK, all the CRCs are good; this node can just be copied as-is. */
+ retry:
+	nraw->flash_offset = phys_ofs;
+	nraw->__totlen = rawlen;
+	nraw->next_phys = NULL;
 
-	ret = jffs2_flash_write(c, phys_ofs, raw->totlen, &retlen, (char *)node);
-	if (ret || (retlen != raw->totlen)) {
+	ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node);
+
+	if (ret || (retlen != rawlen)) {
 		printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
-                       raw->totlen, phys_ofs, ret, retlen);
+                       rawlen, phys_ofs, ret, retlen);
 		if (retlen) {
                         /* Doesn't belong to any inode */
 			nraw->next_in_ino = NULL;
@@ -592,8 +646,34 @@ static int jffs2_garbage_collect_pristin
 			jffs2_mark_node_obsolete(c, nraw);
 		} else {
 			printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset);
-                        jffs2_free_raw_node_ref(raw);
+                        jffs2_free_raw_node_ref(nraw);
 		}
+		if (!retried && (nraw == jffs2_alloc_raw_node_ref())) {
+			/* Try to reallocate space and retry */
+			uint32_t dummy;
+			struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size];
+
+			retried = 1;
+
+			D1(printk(KERN_DEBUG "Retrying failed write of REF_PRISTINE node.\n"));
+			
+			ACCT_SANITY_CHECK(c,jeb);
+			D1(ACCT_PARANOIA_CHECK(jeb));
+
+			ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy);
+
+			if (!ret) {
+				D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs));
+
+				ACCT_SANITY_CHECK(c,jeb);
+				D1(ACCT_PARANOIA_CHECK(jeb));
+
+				goto retry;
+			}
+			D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
+			jffs2_free_raw_node_ref(nraw);
+		}
+
 		if (!ret)
 			ret = -EIO;
 		goto out_node;
@@ -684,7 +764,7 @@ static int jffs2_garbage_collect_metadat
 	ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
 	ri.data_crc = cpu_to_je32(crc32(0, mdata, mdatalen));
 
-	new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, NULL);
+	new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, ALLOC_GC);
 
 	if (IS_ERR(new_fn)) {
 		printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
@@ -728,7 +808,7 @@ static int jffs2_garbage_collect_dirent(
 		       sizeof(rd)+rd.nsize, ret);
 		return ret;
 	}
-	new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, phys_ofs, NULL);
+	new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, phys_ofs, ALLOC_GC);
 
 	if (IS_ERR(new_fd)) {
 		printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd));
@@ -750,13 +830,17 @@ static int jffs2_garbage_collect_deletio
 	   delete a 'real' dirent with the same name that's still
 	   somewhere else on the flash. */
 	if (!jffs2_can_mark_obsolete(c)) {
-		struct jffs2_raw_dirent rd;
+		struct jffs2_raw_dirent *rd;
 		struct jffs2_raw_node_ref *raw;
 		int ret;
 		size_t retlen;
 		int name_len = strlen(fd->name);
 		uint32_t name_crc = crc32(0, fd->name, name_len);
-		char *namebuf = NULL;
+		uint32_t rawlen = ref_totlen(c, jeb, fd->raw);
+
+		rd = kmalloc(rawlen, GFP_KERNEL);
+		if (!rd)
+			return -ENOMEM;
 
 		/* Prevent the erase code from nicking the obsolete node refs while
 		   we're looking at them. I really don't like this extra lock but
@@ -764,91 +848,66 @@ static int jffs2_garbage_collect_deletio
 		down(&c->erase_free_sem);
 
 		for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) {
+
 			/* We only care about obsolete ones */
 			if (!(ref_obsolete(raw)))
 				continue;
 
+			/* Any dirent with the same name is going to have the same length... */
+			if (ref_totlen(c, NULL, raw) != rawlen)
+				continue;
+
 			/* 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)))
 				continue;
 
-			/* This is an obsolete node belonging to the same directory */
-			ret = jffs2_flash_read(c, ref_offset(raw), sizeof(struct jffs2_unknown_node), &retlen, (char *)&rd);
+			D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw)));
+
+			/* This is an obsolete node belonging to the same directory, and it's of the right
+			   length. We need to take a closer look...*/
+			ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)rd);
 			if (ret) {
-				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading header from obsolete node at %08x\n", ret, ref_offset(raw));
+				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading obsolete node at %08x\n", ret, ref_offset(raw));
 				/* If we can't read it, we don't need to continue to obsolete it. Continue */
 				continue;
 			}
-			if (retlen != sizeof(struct jffs2_unknown_node)) {
+			if (retlen != rawlen) {
 				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading header from obsolete node at %08x\n",
-				       retlen, sizeof(struct jffs2_unknown_node), ref_offset(raw));
-				continue;
-			}
-			if (je16_to_cpu(rd.nodetype) != JFFS2_NODETYPE_DIRENT ||
-			    PAD(je32_to_cpu(rd.totlen)) != PAD(sizeof(rd) + name_len))
-				continue;
-
-			/* OK, it's a dirent node, it's the right length. We have to take a 
-			   closer look at it... */
-			ret = jffs2_flash_read(c, ref_offset(raw), sizeof(rd), &retlen, (char *)&rd);
-			if (ret) {
-				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading from obsolete node at %08x\n", ret, ref_offset(raw));
-				/* If we can't read it, we don't need to continune to obsolete it. Continue */
-				continue;
-			}
-			if (retlen != sizeof(rd)) {
-				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading from obsolete node at %08x\n",
-				       retlen, sizeof(rd), ref_offset(raw));
+				       retlen, rawlen, ref_offset(raw));
 				continue;
 			}
 
-			/* If the name CRC doesn't match, skip */
-			if (je32_to_cpu(rd.name_crc) != name_crc)
+			if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT)
 				continue;
+
+			/* If the name CRC doesn't match, skip */
+			if (je32_to_cpu(rd->name_crc) != name_crc)
+				continue;
+
 			/* If the name length doesn't match, or it's another deletion dirent, skip */
-			if (rd.nsize != name_len || !je32_to_cpu(rd.ino))
+			if (rd->nsize != name_len || !je32_to_cpu(rd->ino))
 				continue;
 
 			/* OK, check the actual name now */
-			if (!namebuf) {
-				namebuf = kmalloc(name_len + 1, GFP_KERNEL);
-				if (!namebuf) {
-					up(&c->erase_free_sem);
-					return -ENOMEM;
-				}
-			}
-			/* We read the extra byte before it so it's a word-aligned read */
-			ret = jffs2_flash_read(c, (ref_offset(raw))+sizeof(rd)-1, name_len+1, &retlen, namebuf);
-			if (ret) {
-				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading name from obsolete node at %08x\n", ret, ref_offset(raw));
-				/* If we can't read it, we don't need to continune to obsolete it. Continue */
-				continue;
-			}
-			if (retlen != name_len+1) {
-				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %d) reading name from obsolete node at %08x\n",
-				       retlen, name_len+1, ref_offset(raw));
-				continue;
-			}
-			if (memcmp(namebuf+1, fd->name, name_len))
+			if (memcmp(rd->name, fd->name, name_len))
 				continue;
 
 			/* OK. The name really does match. There really is still an older node on
 			   the flash which our deletion dirent obsoletes. So we have to write out
 			   a new deletion dirent to replace it */
-			
-			if (namebuf)
-				kfree(namebuf);
+			up(&c->erase_free_sem);
 
-			up(&c->erase_free_sem);
+			D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n",
+				  ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino)));
+			kfree(rd);
+
 			return jffs2_garbage_collect_dirent(c, jeb, f, fd);
 		}
 
 		up(&c->erase_free_sem);
-
-		if (namebuf) 
-			kfree(namebuf);
+		kfree(rd);
 	}
 
 	/* No need for it any more. Just mark it obsolete and remove it from the list */
@@ -951,7 +1010,7 @@ static int jffs2_garbage_collect_hole(st
 		       sizeof(ri), ret);
 		return ret;
 	}
-	new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, NULL);
+	new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_GC);
 
 	if (IS_ERR(new_fn)) {
 		printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn));
@@ -979,6 +1038,9 @@ static int jffs2_garbage_collect_hole(st
 		       je32_to_cpu(ri.ino));
 	});
 
+	/* This is a partially-overlapped hole node. Mark it REF_NORMAL not REF_PRISTINE */
+	mark_ref_normal(new_fn->raw);
+
 	for (frag = jffs2_lookup_node_frag(&f->fragtree, fn->ofs); 
 	     frag; frag = frag_next(frag)) {
 		if (frag->ofs > fn->size + fn->ofs)
@@ -1010,7 +1072,7 @@ static int jffs2_garbage_collect_dnode(s
 {
 	struct jffs2_full_dnode *new_fn;
 	struct jffs2_raw_inode ri;
-	uint32_t alloclen, phys_ofs, offset, orig_end;	
+	uint32_t alloclen, phys_ofs, offset, orig_end, orig_start;	
 	int ret = 0;
 	unsigned char *comprbuf = NULL, *writebuf;
 	struct page *pg;
@@ -1023,29 +1085,129 @@ static int jffs2_garbage_collect_dnode(s
 		  f->inocache->ino, start, end));
 
 	orig_end = end;
+	orig_start = start;
 
-	/* If we're looking at the last node in the block we're
-	   garbage-collecting, we allow ourselves to merge as if the
-	   block was already erasing. We're likely to be GC'ing a
-	   partial page, and the next block we GC is likely to have
-	   the other half of this page right at the beginning, which
-	   means we'd expand it _then_, as nr_erasing_blocks would have
-	   increased since we checked, and in doing so would obsolete 
-	   the partial node which we'd have written here. Meaning that 
-	   the GC would churn and churn, and just leave dirty blocks in
-	   it's wake.
-	*/
-	if(c->nr_free_blocks + c->nr_erasing_blocks > JFFS2_RESERVED_BLOCKS_GCMERGE - (fn->raw->next_phys?0:1)) {
-		/* Shitloads of space */
-		/* FIXME: Integrate this properly with GC calculations */
-		start &= ~(PAGE_CACHE_SIZE-1);
-		end = min_t(uint32_t, start + PAGE_CACHE_SIZE, JFFS2_F_I_SIZE(f));
-		D1(printk(KERN_DEBUG "Plenty of free space, so expanding to write from offset 0x%x to 0x%x\n",
-			  start, end));
-		if (end < orig_end) {
-			printk(KERN_WARNING "Eep. jffs2_garbage_collect_dnode extended node to write, but it got smaller: start 0x%x, orig_end 0x%x, end 0x%x\n", start, orig_end, end);
-			end = orig_end;
+	if (c->nr_free_blocks + c->nr_erasing_blocks > c->resv_blocks_gcmerge) {
+		/* Attempt to do some merging. But only expand to cover logically
+		   adjacent frags if the block containing them is already considered
+		   to be dirty. Otherwise we end up with GC just going round in 
+		   circles dirtying the nodes it already wrote out, especially 
+		   on NAND where we have small eraseblocks and hence a much higher
+		   chance of nodes having to be split to cross boundaries. */
+
+		struct jffs2_node_frag *frag;
+		uint32_t min, max;
+
+		min = start & ~(PAGE_CACHE_SIZE-1);
+		max = min + PAGE_CACHE_SIZE;
+
+		frag = jffs2_lookup_node_frag(&f->fragtree, start);
+
+		/* BUG_ON(!frag) but that'll happen anyway... */
+
+		BUG_ON(frag->ofs != start);
+
+		/* First grow down... */
+		while((frag = frag_prev(frag)) && frag->ofs >= min) {
+
+			/* If the previous frag doesn't even reach the beginning, there's
+			   excessive fragmentation. Just merge. */
+			if (frag->ofs > min) {
+				D1(printk(KERN_DEBUG "Expanding down to cover partial frag (0x%x-0x%x)\n",
+					  frag->ofs, frag->ofs+frag->size));
+				start = frag->ofs;
+				continue;
+			}
+			/* OK. This frag holds the first byte of the page. */
+			if (!frag->node || !frag->node->raw) {
+				D1(printk(KERN_DEBUG "First frag in page is hole (0x%x-0x%x). Not expanding down.\n",
+					  frag->ofs, frag->ofs+frag->size));
+				break;
+			} else {
+
+				/* OK, it's a frag which extends to the beginning of the page. Does it live 
+				   in a block which is still considered clean? If so, don't obsolete it.
+				   If not, cover it anyway. */
+
+				struct jffs2_raw_node_ref *raw = frag->node->raw;
+				struct jffs2_eraseblock *jeb;
+
+				jeb = &c->blocks[raw->flash_offset / c->sector_size];
+
+				if (jeb == c->gcblock) {
+					D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in gcblock at %08x\n",
+						  frag->ofs, frag->ofs+frag->size, ref_offset(raw)));
+					start = frag->ofs;
+					break;
+				}
+				if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) {
+					D1(printk(KERN_DEBUG "Not expanding down to cover frag (0x%x-0x%x) in clean block %08x\n",
+						  frag->ofs, frag->ofs+frag->size, jeb->offset));
+					break;
+				}
+
+				D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in dirty block %08x\n",
+						  frag->ofs, frag->ofs+frag->size, jeb->offset));
+				start = frag->ofs;
+				break;
+			}
 		}
+
+		/* ... then up */
+
+		/* Find last frag which is actually part of the node we're to GC. */
+		frag = jffs2_lookup_node_frag(&f->fragtree, end-1);
+
+		while((frag = frag_next(frag)) && frag->ofs+frag->size <= max) {
+
+			/* If the previous frag doesn't even reach the beginning, there's lots
+			   of fragmentation. Just merge. */
+			if (frag->ofs+frag->size < max) {
+				D1(printk(KERN_DEBUG "Expanding up to cover partial frag (0x%x-0x%x)\n",
+					  frag->ofs, frag->ofs+frag->size));
+				end = frag->ofs + frag->size;
+				continue;
+			}
+
+			if (!frag->node || !frag->node->raw) {
+				D1(printk(KERN_DEBUG "Last frag in page is hole (0x%x-0x%x). Not expanding up.\n",
+					  frag->ofs, frag->ofs+frag->size));
+				break;
+			} else {
+
+				/* OK, it's a frag which extends to the beginning of the page. Does it live 
+				   in a block which is still considered clean? If so, don't obsolete it.
+				   If not, cover it anyway. */
+
+				struct jffs2_raw_node_ref *raw = frag->node->raw;
+				struct jffs2_eraseblock *jeb;
+
+				jeb = &c->blocks[raw->flash_offset / c->sector_size];
+
+				if (jeb == c->gcblock) {
+					D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in gcblock at %08x\n",
+						  frag->ofs, frag->ofs+frag->size, ref_offset(raw)));
+					end = frag->ofs + frag->size;
+					break;
+				}
+				if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) {
+					D1(printk(KERN_DEBUG "Not expanding up to cover frag (0x%x-0x%x) in clean block %08x\n",
+						  frag->ofs, frag->ofs+frag->size, jeb->offset));
+					break;
+				}
+
+				D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in dirty block %08x\n",
+						  frag->ofs, frag->ofs+frag->size, jeb->offset));
+				end = frag->ofs + frag->size;
+				break;
+			}
+		}
+		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));
+		BUG_ON(end < orig_end);
+		BUG_ON(start > orig_start);
 	}
 	
 	/* First, use readpage() to read the appropriate page into the page cache */
@@ -1065,7 +1227,6 @@ static int jffs2_garbage_collect_dnode(s
 		return PTR_ERR(pg);
 	}
 	pg_ptr = (char *)kmap(pg);
-	comprbuf = kmalloc(end - start, GFP_KERNEL);
 
 	offset = start;
 	while(offset < orig_end) {
@@ -1085,14 +1246,8 @@ static int jffs2_garbage_collect_dnode(s
 
 		writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1));
 
-		if (comprbuf) {
-			comprtype = jffs2_compress(writebuf, comprbuf, &datalen, &cdatalen);
-		}
-		if (comprtype) {
-			writebuf = comprbuf;
-		} else {
-			datalen = cdatalen;
-		}
+		comprtype = jffs2_compress(writebuf, &comprbuf, &datalen, &cdatalen);
+
 		ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
 		ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
 		ri.totlen = cpu_to_je32(sizeof(ri) + cdatalen);
@@ -1114,7 +1269,9 @@ static int jffs2_garbage_collect_dnode(s
 		ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
 		ri.data_crc = cpu_to_je32(crc32(0, writebuf, cdatalen));
 	
-		new_fn = jffs2_write_dnode(c, f, &ri, writebuf, cdatalen, phys_ofs, NULL);
+		new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC);
+
+		jffs2_free_comprbuf(comprbuf, writebuf);
 
 		if (IS_ERR(new_fn)) {
 			printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
@@ -1129,7 +1286,6 @@ static int jffs2_garbage_collect_dnode(s
 			f->metadata = NULL;
 		}
 	}
-	if (comprbuf) kfree(comprbuf);
 
 	kunmap(pg);
 	/* XXX: Does the page get freed automatically? */
--- a/packages/fs/jffs2/current/src/jffs2port.h
+++ b/packages/fs/jffs2/current/src/jffs2port.h
@@ -1,7 +1,7 @@
 #ifndef __LINUX_JFFS2PORT_H__
 #define __LINUX_JFFS2PORT_H__
 
-/* $Id: jffs2port.h,v 1.3 2003/01/21 18:13:22 dwmw2 Exp $ */
+/* $Id: jffs2port.h,v 1.4 2003/11/20 16:41:58 dwmw2 Exp $ */
 
 #include <pkgconf/system.h>
 #include <pkgconf/hal.h>
@@ -189,7 +189,7 @@ struct jffs2_eraseblock;
 
 cyg_bool jffs2_flash_read(struct jffs2_sb_info *c, cyg_uint32 read_buffer_offset, const size_t size, size_t * return_size, char * write_buffer);
 cyg_bool jffs2_flash_write(struct jffs2_sb_info *c, cyg_uint32 write_buffer_offset, const size_t size, size_t * return_size, char * read_buffer);
-int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen);
+int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen);
 cyg_bool jffs2_flash_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 
 
--- a/packages/fs/jffs2/current/src/nodelist.c
+++ b/packages/fs/jffs2/current/src/nodelist.c
@@ -1,13 +1,13 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: nodelist.c,v 1.79 2003/04/08 08:20:01 dwmw2 Exp $
+ * $Id: nodelist.c,v 1.86 2003/10/31 15:37:51 dwmw2 Exp $
  *
  */
 
@@ -58,7 +58,7 @@ void jffs2_add_fd_to_list(struct jffs2_s
 /* Put a new tmp_dnode_info into the list, keeping the list in 
    order of increasing version
 */
-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 jffs2_tmp_dnode_info **list)
 {
 	struct jffs2_tmp_dnode_info **prev = list;
 	
@@ -133,7 +133,9 @@ int jffs2_get_inode_nodes(struct jffs2_s
 		cond_resched();
 
 		/* FIXME: point() */
-		err = jffs2_flash_read(c, (ref_offset(ref)), min_t(uint32_t, ref->totlen, sizeof(node)), &retlen, (void *)&node);
+		err = jffs2_flash_read(c, (ref_offset(ref)), 
+				       min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node)),
+				       &retlen, (void *)&node);
 		if (err) {
 			printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref));
 			goto free_out;
@@ -141,7 +143,7 @@ int jffs2_get_inode_nodes(struct jffs2_s
 			
 
 			/* Check we've managed to read at least the common node header */
-		if (retlen < min_t(uint32_t, ref->totlen, sizeof(node.u))) {
+		if (retlen < min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node.u))) {
 			printk(KERN_WARNING "short read in get_inode_nodes()\n");
 			err = -EIO;
 			goto free_out;
@@ -246,7 +248,7 @@ int jffs2_get_inode_nodes(struct jffs2_s
 
 			/* If we've never checked the CRCs on this node, check them now. */
 			if (ref_flags(ref) == REF_UNCHECKED) {
-				uint32_t crc;
+				uint32_t crc, len;
 				struct jffs2_eraseblock *jeb;
 
 				crc = crc32(0, &node, sizeof(node.i)-8);
@@ -321,10 +323,12 @@ int jffs2_get_inode_nodes(struct jffs2_s
 				/* Mark the node as having been checked and fix the accounting accordingly */
 				spin_lock(&c->erase_completion_lock);
 				jeb = &c->blocks[ref->flash_offset / c->sector_size];
-				jeb->used_size += ref->totlen;
-				jeb->unchecked_size -= ref->totlen;
-				c->used_size += ref->totlen;
-				c->unchecked_size -= ref->totlen;
+				len = ref_totlen(c, jeb, ref);
+
+				jeb->used_size += len;
+				jeb->unchecked_size -= len;
+				c->used_size += len;
+				c->unchecked_size -= len;
 
 				/* If node covers at least a whole page, or if it starts at the 
 				   beginning of a page and runs to the end of the file, or if 
@@ -377,6 +381,7 @@ int jffs2_get_inode_nodes(struct jffs2_s
 		default:
 			if (ref_flags(ref) == REF_UNCHECKED) {
 				struct jffs2_eraseblock *jeb;
+				uint32_t len;
 
 				printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n",
 				       je16_to_cpu(node.u.nodetype), ref_offset(ref));
@@ -384,10 +389,12 @@ int jffs2_get_inode_nodes(struct jffs2_s
 				/* Mark the node as having been checked and fix the accounting accordingly */
 				spin_lock(&c->erase_completion_lock);
 				jeb = &c->blocks[ref->flash_offset / c->sector_size];
-				jeb->used_size += ref->totlen;
-				jeb->unchecked_size -= ref->totlen;
-				c->used_size += ref->totlen;
-				c->unchecked_size -= ref->totlen;
+				len = ref_totlen(c, jeb, ref);
+
+				jeb->used_size += len;
+				jeb->unchecked_size -= len;
+				c->used_size += len;
+				c->unchecked_size -= len;
 
 				mark_ref_normal(ref);
 				spin_unlock(&c->erase_completion_lock);
@@ -631,6 +638,8 @@ void jffs2_kill_fragtree(struct rb_root 
 
 		jffs2_free_node_frag(frag);
 		frag = parent;
+
+		cond_resched();
 	}
 }
 
--- a/packages/fs/jffs2/current/src/nodelist.h
+++ b/packages/fs/jffs2/current/src/nodelist.h
@@ -1,13 +1,13 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: nodelist.h,v 1.94 2003/07/03 10:35:45 dwmw2 Exp $
+ * $Id: nodelist.h,v 1.113 2003/11/20 16:40:14 dwmw2 Exp $
  *
  */
 
@@ -59,13 +59,12 @@ struct jffs2_raw_node_ref
 		word so you know when you've got there :) */
 	struct jffs2_raw_node_ref *next_phys;
 	uint32_t flash_offset;
-	uint32_t totlen;
-	
+	uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */
+};
+
         /* flash_offset & 3 always has to be zero, because nodes are
 	   always aligned at 4 bytes. So we have a couple of extra bits
-	   to play with. So we set the least significant bit to 1 to
-	   signify that the node is obsoleted by later nodes.
-	*/
+	   to play with, which indicate the node's status; see below: */ 
 #define REF_UNCHECKED	0	/* We haven't yet checked the CRC or built its inode */
 #define REF_OBSOLETE	1	/* Obsolete, can be completely ignored */
 #define REF_PRISTINE	2	/* Completely clean. GC without looking */
@@ -74,7 +73,6 @@ struct jffs2_raw_node_ref
 #define ref_offset(ref)		((ref)->flash_offset & ~3)
 #define ref_obsolete(ref)	(((ref)->flash_offset & 3) == REF_OBSOLETE)
 #define mark_ref_normal(ref)    do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0)
-};
 
 /* 
    Used for keeping track of deletion nodes &c, which can only be marked
@@ -201,10 +199,11 @@ struct jffs2_eraseblock
 };
 
 #define ACCT_SANITY_CHECK(c, jeb) do { \
-	if (jeb->used_size + jeb->dirty_size + jeb->free_size + jeb->wasted_size + jeb->unchecked_size != c->sector_size) { \
-		printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", jeb->offset); \
+		struct jffs2_eraseblock *___j = jeb; \
+		if ((___j) && ___j->used_size + ___j->dirty_size + ___j->free_size + ___j->wasted_size + ___j->unchecked_size != c->sector_size) { \
+		printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", ___j->offset); \
 		printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + wasted %08x + unchecked %08x != total %08x\n", \
-		jeb->free_size, jeb->dirty_size, jeb->used_size, jeb->wasted_size, jeb->unchecked_size, c->sector_size); \
+		___j->free_size, ___j->dirty_size, ___j->used_size, ___j->wasted_size, ___j->unchecked_size, c->sector_size); \
 		BUG(); \
 	} \
 	if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size + c->wasted_size + c->unchecked_size != c->flash_size) { \
@@ -215,15 +214,46 @@ struct jffs2_eraseblock
 	} \
 } while(0)
 
+static inline void paranoia_failed_dump(struct jffs2_eraseblock *jeb)
+{
+	struct jffs2_raw_node_ref *ref;
+	int i=0;
+
+	printk(KERN_NOTICE);
+	for (ref = jeb->first_node; ref; ref = ref->next_phys) {
+		printk("%08x->", ref_offset(ref));
+		if (++i == 8) {
+			i = 0;
+			printk("\n" KERN_NOTICE);
+		}
+	}
+	printk("\n");
+}
+
+
 #define ACCT_PARANOIA_CHECK(jeb) do { \
 		uint32_t my_used_size = 0; \
 		uint32_t my_unchecked_size = 0; \
 		struct jffs2_raw_node_ref *ref2 = jeb->first_node; \
 		while (ref2) { \
+			if (unlikely(ref2->flash_offset < jeb->offset || \
+				     ref2->flash_offset > jeb->offset + c->sector_size)) { \
+				printk(KERN_NOTICE "Node %08x shouldn't be in block at %08x!\n", \
+				       ref_offset(ref2), jeb->offset); \
+				paranoia_failed_dump(jeb); \
+				BUG(); \
+			} \
 			if (ref_flags(ref2) == REF_UNCHECKED) \
-				my_unchecked_size += ref2->totlen; \
+				my_unchecked_size += ref_totlen(c, jeb, ref2); \
 			else if (!ref_obsolete(ref2)) \
-				my_used_size += ref2->totlen; \
+				my_used_size += ref_totlen(c, jeb, ref2); \
+			if (unlikely((!ref2->next_phys) != (ref2 == jeb->last_node))) { \
+				printk("ref for node at %p (phys %08x) has next_phys->%p (%08x), last_node->%p (phys %08x)\n", \
+				       ref2, ref_offset(ref2), ref2->next_phys, ref_offset(ref2->next_phys), \
+				       jeb->last_node, ref_offset(jeb->last_node)); \
+				paranoia_failed_dump(jeb); \
+				BUG(); \
+			} \
 			ref2 = ref2->next_phys; \
 		} \
 		if (my_used_size != jeb->used_size) { \
@@ -236,17 +266,61 @@ struct jffs2_eraseblock
 		} \
 	} while(0)
 
+/* Calculate totlen from surrounding nodes or eraseblock */
+static inline uint32_t __ref_totlen(struct jffs2_sb_info *c,
+				    struct jffs2_eraseblock *jeb,
+				    struct jffs2_raw_node_ref *ref)
+{
+	uint32_t ref_end;
+	
+	if (ref->next_phys)
+		ref_end = ref_offset(ref->next_phys);
+	else {
+		if (!jeb)
+			jeb = &c->blocks[ref->flash_offset / c->sector_size];
+
+		/* Last node in block. Use free_space */
+		BUG_ON(ref != jeb->last_node);
+		ref_end = jeb->offset + c->sector_size - jeb->free_size;
+	}
+	return ref_end - ref_offset(ref);
+}
+
+static inline uint32_t ref_totlen(struct jffs2_sb_info *c,
+				  struct jffs2_eraseblock *jeb,
+				  struct jffs2_raw_node_ref *ref)
+{
+	uint32_t ret;
+
+	D1(if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) {
+		printk(KERN_CRIT "ref_totlen called with wrong block -- at 0x%08x instead of 0x%08x; ref 0x%08x\n",
+		       jeb->offset, c->blocks[ref->flash_offset / c->sector_size].offset, ref_offset(ref));
+		BUG();
+	})
+
+#if 1
+	ret = ref->__totlen;
+#else
+	/* This doesn't actually work yet */
+	ret = __ref_totlen(c, jeb, ref);
+	if (ret != ref->__totlen) {
+		printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n",
+		       ref, ref_offset(ref), ref_offset(ref)+ref->__totlen,
+		       ret, ref->__totlen);
+		if (!jeb)
+			jeb = &c->blocks[ref->flash_offset / c->sector_size];
+		paranoia_failed_dump(jeb);
+		BUG();
+	}
+#endif
+	return ret;
+}
+
+
 #define ALLOC_NORMAL	0	/* Normal allocation */
 #define ALLOC_DELETION	1	/* Deletion node. Best to allow it */
 #define ALLOC_GC	2	/* Space requested for GC. Give it or die */
-
-#define JFFS2_RESERVED_BLOCKS_BASE 3						/* Number of free blocks there must be before we... */
-#define JFFS2_RESERVED_BLOCKS_WRITE (JFFS2_RESERVED_BLOCKS_BASE + 2)		/* ... allow a normal filesystem write */
-#define JFFS2_RESERVED_BLOCKS_DELETION (JFFS2_RESERVED_BLOCKS_BASE)		/* ... allow a normal filesystem deletion */
-#define JFFS2_RESERVED_BLOCKS_GCTRIGGER (JFFS2_RESERVED_BLOCKS_BASE + 3)	/* ... wake up the GC thread */
-#define JFFS2_RESERVED_BLOCKS_GCBAD (JFFS2_RESERVED_BLOCKS_BASE + 1)		/* ... pick a block from the bad_list to GC */
-#define JFFS2_RESERVED_BLOCKS_GCMERGE (JFFS2_RESERVED_BLOCKS_BASE)		/* ... merge pages when garbage collecting */
-
+#define ALLOC_NORETRY	3	/* For jffs2_write_dnode: On failure, return -EAGAIN instead of retrying */
 
 /* How much dirty space before it goes on the very_dirty_list */
 #define VERYDIRTY(c, size) ((size) >= ((c)->sector_size / 2))
@@ -256,13 +330,13 @@ struct jffs2_eraseblock
 
 #define PAD(x) (((x)+3)&~3)
 
-static inline int jffs2_raw_ref_to_inum(struct jffs2_raw_node_ref *raw)
+static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw)
 {
 	while(raw->next_in_ino) {
 		raw = raw->next_in_ino;
 	}
 
-	return ((struct jffs2_inode_cache *)raw)->ino;
+	return ((struct jffs2_inode_cache *)raw);
 }
 
 static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
@@ -286,7 +360,6 @@ static inline struct jffs2_node_frag *fr
 /* nodelist.c */
 D1(void jffs2_print_frag_list(struct jffs2_inode_info *f));
 void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list);
-void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list);
 int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
 			  struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
 			  uint32_t *highest_version, uint32_t *latest_mctime,
@@ -314,8 +387,9 @@ void jffs2_dump_block_lists(struct jffs2
 
 /* write.c */
 int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri);
-struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs,  uint32_t *writelen);
-struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs,  uint32_t *writelen);
+
+struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode);
+struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode);
 int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
 			    struct jffs2_raw_inode *ri, unsigned char *buf, 
 			    uint32_t offset, uint32_t writelen, uint32_t *retlen);
@@ -362,10 +436,10 @@ int jffs2_read_inode_range(struct jffs2_
 			   unsigned char *buf, uint32_t offset, uint32_t len);
 char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
 
-
 /* compr.c */
-unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, 
+unsigned char jffs2_compress(unsigned char *data_in, unsigned char **cpage_out, 
 			     uint32_t *datalen, uint32_t *cdatalen);
+void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig);
 int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, 
 		     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
 
@@ -378,12 +452,13 @@ int jffs2_do_mount_fs(struct jffs2_sb_in
 
 /* erase.c */
 void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
-void jffs2_erase_pending_blocks(struct jffs2_sb_info *c);
+void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count);
 void jffs2_erase_pending_trigger(struct jffs2_sb_info *c);
 
 #ifdef CONFIG_JFFS2_FS_NAND
 /* wbuf.c */
-int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad);
+int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino);
+int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
 int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
--- a/packages/fs/jffs2/current/src/nodemgmt.c
+++ b/packages/fs/jffs2/current/src/nodemgmt.c
@@ -1,13 +1,13 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: nodemgmt.c,v 1.95 2003/06/30 10:58:57 dwmw2 Exp $
+ * $Id: nodemgmt.c,v 1.106 2003/11/03 17:33:54 dwmw2 Exp $
  *
  */
 
@@ -43,13 +43,10 @@ static int jffs2_do_reserve_space(struct
 int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio)
 {
 	int ret = -EAGAIN;
-	int blocksneeded = JFFS2_RESERVED_BLOCKS_WRITE;
+	int blocksneeded = c->resv_blocks_write;
 	/* align it */
 	minsize = PAD(minsize);
 
-	if (prio == ALLOC_DELETION)
-		blocksneeded = JFFS2_RESERVED_BLOCKS_DELETION;
-
 	D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize));
 	down(&c->alloc_sem);
 
@@ -63,8 +60,6 @@ int jffs2_reserve_space(struct jffs2_sb_
 			int ret;
 			uint32_t dirty, avail;
 
-			up(&c->alloc_sem);
-			
 			/* calculate real dirty size
 			 * dirty_size contains blocks on erase_pending_list
 			 * those blocks are counted in c->nr_erasing_blocks.
@@ -78,10 +73,16 @@ int jffs2_reserve_space(struct jffs2_sb_
 			 * of nodes.
 			 */
 			dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size;
-			if (dirty < c->sector_size) {
-				D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < sector size 0x%08x, returning -ENOSPC\n",
+			if (dirty < c->nospc_dirty_size) {
+				if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
+					printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n");
+					break;
+				}
+				D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n",
 					  dirty, c->unchecked_size, c->sector_size));
+
 				spin_unlock(&c->erase_completion_lock);
+				up(&c->alloc_sem);
 				return -ENOSPC;
 			}
 			
@@ -96,12 +97,20 @@ int jffs2_reserve_space(struct jffs2_sb_
 			 */
 			avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size;
 			if ( (avail / c->sector_size) <= blocksneeded) {
+				if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
+					printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n");
+					break;
+				}
+
 				D1(printk(KERN_DEBUG "max. available size 0x%08x  < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n",
 					  avail, blocksneeded * c->sector_size));
 				spin_unlock(&c->erase_completion_lock);
+				up(&c->alloc_sem);
 				return -ENOSPC;
 			}
-			
+
+			up(&c->alloc_sem);
+
 			D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n",
 				  c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size,
 				  c->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size));
@@ -161,9 +170,10 @@ static int jffs2_do_reserve_space(struct
 		if (jffs2_wbuf_dirty(c)) {
 			spin_unlock(&c->erase_completion_lock);
 			D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));			    
-			jffs2_flush_wbuf(c, 1);
+			jffs2_flush_wbuf_pad(c);
 			spin_lock(&c->erase_completion_lock);
-			/* We know nobody's going to have changed nextblock. Just continue */
+			jeb = c->nextblock;
+			goto restart;
 		}
 		c->wasted_size += jeb->free_size;
 		c->free_size -= jeb->free_size;
@@ -199,8 +209,6 @@ static int jffs2_do_reserve_space(struct
 
 		if (list_empty(&c->free_list)) {
 
-			DECLARE_WAITQUEUE(wait, current);
-			
 			if (!c->nr_erasing_blocks && 
 			    !list_empty(&c->erasable_list)) {
 				struct jffs2_eraseblock *ejeb;
@@ -219,7 +227,7 @@ static int jffs2_do_reserve_space(struct
 				D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));
 				/* c->nextblock is NULL, no update to c->nextblock allowed */			    
 				spin_unlock(&c->erase_completion_lock);
-				jffs2_flush_wbuf(c, 1);
+				jffs2_flush_wbuf_pad(c);
 				spin_lock(&c->erase_completion_lock);
 				/* Have another go. It'll be on the erasable_list now */
 				return -EAGAIN;
@@ -233,30 +241,12 @@ static int jffs2_do_reserve_space(struct
 				       list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no");
 				return -ENOSPC;
 			}
-			/* Make sure this can't deadlock. Someone has to start the erases
-			   of erase_pending blocks */
-#ifdef __ECOS
-			/* In eCos, we don't have a handy kernel thread doing the erases for
-			   us. We do them ourselves right now. */
-			jffs2_erase_pending_blocks(c);
-#else
-			set_current_state(TASK_INTERRUPTIBLE);
-			add_wait_queue(&c->erase_wait, &wait);
-			D1(printk(KERN_DEBUG "Waiting for erases to complete. erasing_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n", 
-				  c->nr_erasing_blocks, list_empty(&c->erasable_list)?"yes":"no",
-				  list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"));
-			if (!list_empty(&c->erase_pending_list)) {
-				D1(printk(KERN_DEBUG "Triggering pending erases\n"));
-				jffs2_erase_pending_trigger(c);
-			}
+
 			spin_unlock(&c->erase_completion_lock);
-			schedule();
-			remove_wait_queue(&c->erase_wait, &wait);
+			/* Don't wait for it; just erase one right now */
+			jffs2_erase_pending_blocks(c, 1);
 			spin_lock(&c->erase_completion_lock);
-			if (signal_pending(current)) {
-				return -EINTR;
-			}
-#endif
+
 			/* An erase may have failed, decreasing the
 			   amount of free space available. So we must
 			   restart from the beginning */
@@ -311,9 +301,11 @@ static int jffs2_do_reserve_space(struct
 int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new)
 {
 	struct jffs2_eraseblock *jeb;
-	uint32_t len = new->totlen;
+	uint32_t len;
 
 	jeb = &c->blocks[new->flash_offset / c->sector_size];
+	len = ref_totlen(c, jeb, new);
+
 	D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len));
 #if 1
 	if (jeb != c->nextblock || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) {
@@ -347,7 +339,7 @@ int jffs2_add_physical_node_ref(struct j
 		if (jffs2_wbuf_dirty(c)) {
 			/* Flush the last write in the block if it's outstanding */
 			spin_unlock(&c->erase_completion_lock);
-			jffs2_flush_wbuf(c, 1);
+			jffs2_flush_wbuf_pad(c);
 			spin_lock(&c->erase_completion_lock);
 		}
 
@@ -370,6 +362,20 @@ void jffs2_complete_reservation(struct j
 	up(&c->alloc_sem);
 }
 
+static inline int on_list(struct list_head *obj, struct list_head *head)
+{
+	struct list_head *this;
+
+	list_for_each(this, head) {
+		if (this == obj) {
+			D1(printk("%p is on list at %p\n", obj, head));
+			return 1;
+
+		}
+	}
+	return 0;
+}
+
 void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref)
 {
 	struct jffs2_eraseblock *jeb;
@@ -396,38 +402,53 @@ void jffs2_mark_node_obsolete(struct jff
 	spin_lock(&c->erase_completion_lock);
 
 	if (ref_flags(ref) == REF_UNCHECKED) {
-		D1(if (unlikely(jeb->unchecked_size < ref->totlen)) {
+		D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) {
 			printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n",
-			       ref->totlen, blocknr, ref->flash_offset, jeb->used_size);
+			       ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
 			BUG();
 		})
-		D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref->totlen));
-		jeb->unchecked_size -= ref->totlen;
-		c->unchecked_size -= ref->totlen;
+		D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
+		jeb->unchecked_size -= ref_totlen(c, jeb, ref);
+		c->unchecked_size -= ref_totlen(c, jeb, ref);
 	} else {
-		D1(if (unlikely(jeb->used_size < ref->totlen)) {
+		D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) {
 			printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
-			       ref->totlen, blocknr, ref->flash_offset, jeb->used_size);
+			       ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
 			BUG();
 		})
-		D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref->totlen));
-		jeb->used_size -= ref->totlen;
-		c->used_size -= ref->totlen;
+		D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
+		jeb->used_size -= ref_totlen(c, jeb, ref);
+		c->used_size -= ref_totlen(c, jeb, ref);
 	}
 
 	// Take care, that wasted size is taken into concern
-	if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref->totlen)) && jeb != c->nextblock) {
+	if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) {
 		D1(printk("Dirtying\n"));
-		addedsize = ref->totlen + jeb->wasted_size;
-		jeb->dirty_size += addedsize;
-		c->dirty_size += addedsize;
-		c->wasted_size -= jeb->wasted_size;
-		jeb->wasted_size = 0;
+		addedsize = ref_totlen(c, jeb, ref);
+		jeb->dirty_size += ref_totlen(c, jeb, ref);
+		c->dirty_size += ref_totlen(c, jeb, ref);
+
+		/* Convert wasted space to dirty, if not a bad block */
+		if (jeb->wasted_size) {
+			if (on_list(&jeb->list, &c->bad_used_list)) {
+				D1(printk(KERN_DEBUG "Leaving block at %08x on the bad_used_list\n",
+					  jeb->offset));
+				addedsize = 0; /* To fool the refiling code later */
+			} else {
+				D1(printk(KERN_DEBUG "Converting %d bytes of wasted space to dirty in block at %08x\n",
+					  jeb->wasted_size, jeb->offset));
+				addedsize += jeb->wasted_size;
+				jeb->dirty_size += jeb->wasted_size;
+				c->dirty_size += jeb->wasted_size;
+				c->wasted_size -= jeb->wasted_size;
+				jeb->wasted_size = 0;
+			}
+		}
 	} else {
 		D1(printk("Wasting\n"));
 		addedsize = 0;
-		jeb->wasted_size += ref->totlen;
-		c->wasted_size += ref->totlen;	
+		jeb->wasted_size += ref_totlen(c, jeb, ref);
+		c->wasted_size += ref_totlen(c, jeb, ref);	
 	}
 	ref->flash_offset = ref_offset(ref) | REF_OBSOLETE;
 	
@@ -458,30 +479,6 @@ void jffs2_mark_node_obsolete(struct jff
 		if (jffs2_wbuf_dirty(c)) {
 			D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n"));
 			list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list);
-#if 0 /* This check was added to allow us to find places where we added nodes to the lists
-	 after dropping the alloc_sem, and it did that just fine. But it also caused us to
-	 lock the alloc_sem in other places, like clear_inode(), when we wouldn't otherwise
-	 have needed to. So I suspect it's outlived its usefulness. Thomas? */
-
-			/* We've changed the rules slightly. After
-			   writing a node you now mustn't drop the
-			   alloc_sem before you've finished all the
-			   list management - this is so that when we
-			   get here, we know that no other nodes have
-			   been written, and the above check on wbuf
-			   is valid - wbuf_len is nonzero IFF the node
-			   which obsoletes this node is still in the
-			   wbuf.
-
-			   So we BUG() if that new rule is broken, to
-			   make sure we catch it and fix it.
-			*/
-			if (!down_trylock(&c->alloc_sem)) {
-				up(&c->alloc_sem);
-				printk(KERN_CRIT "jffs2_mark_node_obsolete() called with wbuf active but alloc_sem not locked!\n");
-				BUG();
-			}
-#endif
 		} else {
 			if (jiffies & 127) {
 				/* Most of the time, we just erase it immediately. Otherwise we
@@ -506,7 +503,7 @@ void jffs2_mark_node_obsolete(struct jff
 		D1(printk(KERN_DEBUG "...and adding to dirty_list\n"));
 		list_add_tail(&jeb->list, &c->dirty_list);
 	} else if (VERYDIRTY(c, jeb->dirty_size) &&
-		   !VERYDIRTY(c, jeb->dirty_size - ref->totlen)) {
+		   !VERYDIRTY(c, jeb->dirty_size - addedsize)) {
 		D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n", jeb->offset));
 		list_del(&jeb->list);
 		D1(printk(KERN_DEBUG "...and adding to very_dirty_list\n"));
@@ -533,12 +530,12 @@ void jffs2_mark_node_obsolete(struct jff
 		printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
 		return;
 	}
-	if (PAD(je32_to_cpu(n.totlen)) != PAD(ref->totlen)) {
-		printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen in node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref->totlen);
+	if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) {
+		printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref));
 		return;
 	}
 	if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) {
-		D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x\n", ref_offset(ref), je16_to_cpu(n.nodetype)));
+		D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype)));
 		return;
 	}
 	/* XXX FIXME: This is ugly now */
@@ -569,7 +566,7 @@ void jffs2_dump_block_lists(struct jffs2
 	printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size);
 	printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size);
 	printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size);
-	printk(KERN_DEBUG "jffs2_reserved_blocks size: %08x\n",c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE);
+	printk(KERN_DEBUG "jffs2_reserved_blocks size: %08x\n",c->sector_size * c->resv_blocks_write);
 
 	if (c->nextblock) {
 		printk(KERN_DEBUG "nextblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
@@ -672,7 +669,7 @@ void jffs2_dump_block_lists(struct jffs2
 
 		list_for_each(this, &c->erasable_pending_wbuf_list) {
 			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
-			printk(KERN_DEBUG "erase_pending_wbuf_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+			printk(KERN_DEBUG "erasable_pending_wbuf_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
 			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
 		}
 	}
--- a/packages/fs/jffs2/current/src/os-ecos.h
+++ b/packages/fs/jffs2/current/src/os-ecos.h
@@ -7,7 +7,7 @@
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: os-ecos.h,v 1.5 2003/01/21 18:14:27 dwmw2 Exp $
+ * $Id: os-ecos.h,v 1.8 2003/11/20 16:41:58 dwmw2 Exp $
  *
  */
 
@@ -233,13 +233,14 @@ int jffs2_commit_write (struct inode *d_
 #define jffs2_cleanmarker_oob(c) (0)
 #define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
 
-#define jffs2_flush_wbuf(c, flag) do { ; } while(0)
+#define jffs2_flush_wbuf_pad(c) ({ (void)(c), 0; })
+#define jffs2_flush_wbuf_gc(c, i) ({ (void)(c), (void) i, 0; })
 #define jffs2_nand_read_failcnt(c,jeb) do { ; } while(0)
 #define jffs2_write_nand_badblock(c,jeb) do { ; } while(0)
 #define jffs2_nand_flash_setup(c) (0)
 #define jffs2_nand_flash_cleanup(c) do {} while(0)
 #define jffs2_wbuf_dirty(c) (0)
-#define jffs2_flash_writev jffs2_flash_writev
+#define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e)
 #define jffs2_wbuf_timeout NULL
 #define jffs2_wbuf_process NULL
 #else
@@ -255,4 +256,6 @@ static inline void jffs2_init_inode_info
 	init_MUTEX_LOCKED(&f->sem);
 }
 
+#define BUG_ON(x) do { if (unlikely(x)) BUG(); } while(0)
+
 #endif /* __JFFS2_OS_ECOS_H__ */
--- a/packages/fs/jffs2/current/src/pushpull.h
+++ b/packages/fs/jffs2/current/src/pushpull.h
@@ -3,11 +3,11 @@
  *
  * Copyright (C) 2001, 2002 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: pushpull.h,v 1.8 2002/05/20 14:56:38 dwmw2 Exp $
+ * $Id: pushpull.h,v 1.9 2003/10/04 08:33:06 dwmw2 Exp $
  *
  */
 
--- a/packages/fs/jffs2/current/src/read.c
+++ b/packages/fs/jffs2/current/src/read.c
@@ -1,13 +1,13 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: read.c,v 1.32 2003/07/15 10:11:37 dwmw2 Exp $
+ * $Id: read.c,v 1.34 2003/10/04 08:33:06 dwmw2 Exp $
  *
  */
 
@@ -16,6 +16,7 @@
 #include <linux/crc32.h>
 #include <linux/pagemap.h>
 #include <linux/mtd/mtd.h>
+#include <linux/compiler.h>
 #include "nodelist.h"
 
 int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len)
--- a/packages/fs/jffs2/current/src/readinode.c
+++ b/packages/fs/jffs2/current/src/readinode.c
@@ -1,13 +1,13 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: readinode.c,v 1.106 2003/05/14 06:53:26 dwmw2 Exp $
+ * $Id: readinode.c,v 1.113 2003/11/03 13:20:33 dwmw2 Exp $
  *
  */
 
@@ -56,6 +56,66 @@ void jffs2_print_frag_list(struct jffs2_
 		printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
 	}
 }
+
+static int jffs2_sanitycheck_fragtree(struct jffs2_inode_info *f)
+{
+	struct jffs2_node_frag *frag;
+	int bitched = 0;
+
+	for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) {
+
+		struct jffs2_full_dnode *fn = frag->node;
+		if (!fn || !fn->raw)
+			continue;
+
+		if (ref_flags(fn->raw) == REF_PRISTINE) {
+
+			if (fn->frags > 1) {
+				printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(fn->raw), fn->frags);
+				bitched = 1;
+			}
+			/* A hole node which isn't multi-page should be garbage-collected
+			   and merged anyway, so we just check for the frag size here,
+			   rather than mucking around with actually reading the node
+			   and checking the compression type, which is the real way
+			   to tell a hole node. */
+			if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE && frag_prev(frag)->node) {
+				printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n",
+				       ref_offset(fn->raw));
+				bitched = 1;
+			}
+
+			if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) {
+				printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n",
+				       ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size);
+				bitched = 1;
+			}
+		}
+	}
+	
+	if (bitched) {
+		struct jffs2_node_frag *thisfrag;
+
+		printk(KERN_WARNING "Inode is #%u\n", f->inocache->ino);
+		thisfrag = frag_first(&f->fragtree);
+		while (thisfrag) {
+			if (!thisfrag->node) {
+				printk("Frag @0x%x-0x%x; node-less hole\n",
+				       thisfrag->ofs, thisfrag->size + thisfrag->ofs);
+			} else if (!thisfrag->node->raw) {
+				printk("Frag @0x%x-0x%x; raw-less hole\n",
+				       thisfrag->ofs, thisfrag->size + thisfrag->ofs);
+			} else {
+				printk("Frag @0x%x-0x%x; raw at 0x%08x(%d) (0x%x-0x%x)\n",
+				       thisfrag->ofs, thisfrag->size + thisfrag->ofs,
+				       ref_offset(thisfrag->node->raw), ref_flags(thisfrag->node->raw),
+				       thisfrag->node->ofs, thisfrag->node->ofs+thisfrag->node->size);
+			}
+			thisfrag = frag_next(thisfrag);
+		}
+	}
+	return bitched;
+}
 #endif /* D1 */
 
 static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this)
@@ -130,6 +190,11 @@ int jffs2_add_full_dnode_to_inode(struct
 				mark_ref_normal(next->node->raw);
 		}
 	}
+	D2(if (jffs2_sanitycheck_fragtree(f)) {
+		   printk(KERN_WARNING "Just added node %04x-%04x @0x%08x on flash, newfrag *%p\n",
+			  fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag);
+		   return 0;
+	   })
 	D2(jffs2_print_frag_list(f));
 	return 0;
 }
@@ -384,6 +449,7 @@ int jffs2_do_read_inode(struct jffs2_sb_
 		}
 	}
 	spin_unlock(&c->inocache_lock);
+
 	if (!f->inocache && ino == 1) {
 		/* Special case - no root inode on medium */
 		f->inocache = jffs2_alloc_inode_cache();
@@ -460,7 +526,7 @@ static int jffs2_do_read_inode_internal(
 		fn = tn->fn;
 
 		if (f->metadata) {
-			if (tn->version > mdata_ver) {
+			if (likely(tn->version >= mdata_ver)) {
 				D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw)));
 				jffs2_mark_node_obsolete(c, f->metadata->raw);
 				jffs2_free_full_dnode(f->metadata);
@@ -468,10 +534,13 @@ static int jffs2_do_read_inode_internal(
 				
 				mdata_ver = 0;
 			} else {
-				D1(printk(KERN_DEBUG "Er. New metadata at 0x%08x with ver %d is actually older than previous %d\n",
-				       ref_offset(f->metadata->raw), tn->version, mdata_ver));
+				/* This should never happen. */
+				printk(KERN_WARNING "Er. New metadata at 0x%08x with ver %d is actually older than previous ver %d at 0x%08x\n",
+					  ref_offset(fn->raw), tn->version, mdata_ver, ref_offset(f->metadata->raw));
 				jffs2_mark_node_obsolete(c, fn->raw);
 				jffs2_free_full_dnode(fn);
+				/* Fill in latest_node from the metadata, not this one we're about to free... */
+				fn = f->metadata;
 				goto next_tn;
 			}
 		}
@@ -488,6 +557,8 @@ static int jffs2_do_read_inode_internal(
 		tn_list = tn->next;
 		jffs2_free_tmp_dnode_info(tn);
 	}
+	D1(jffs2_sanitycheck_fragtree(f));
+
 	if (!fn) {
 		/* No data nodes for this inode. */
 		if (f->inocache->ino != 1) {
@@ -594,24 +665,10 @@ static int jffs2_do_read_inode_internal(
 void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
 {
 	struct jffs2_full_dirent *fd, *fds;
-	/* I don't think we care about the potential race due to reading this
-	   without f->sem. It can never get undeleted. */
-	int deleted = f->inocache && !f->inocache->nlink;
-
-	/* If it's a deleted inode, grab the alloc_sem. This prevents
-	   jffs2_garbage_collect_pass() from deciding that it wants to
-	   garbage collect one of the nodes we're just about to mark 
-	   obsolete -- by the time we drop alloc_sem and return, all
-	   the nodes are marked obsolete, and jffs2_g_c_pass() won't
-	   call iget() for the inode in question.
-
-	   We also used to do this to keep the temporary BUG() in 
-	   jffs2_mark_node_obsolete() from triggering. 
-	*/
-	if(deleted)
-		down(&c->alloc_sem);
+	int deleted;
 
 	down(&f->sem);
+	deleted = f->inocache && !f->inocache->nlink;
 
 	if (f->metadata) {
 		if (deleted)
@@ -633,7 +690,4 @@ void jffs2_do_clear_inode(struct jffs2_s
 		jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
 
 	up(&f->sem);
-
-	if(deleted)
-		up(&c->alloc_sem);
 }
--- a/packages/fs/jffs2/current/src/scan.c
+++ b/packages/fs/jffs2/current/src/scan.c
@@ -1,13 +1,13 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: scan.c,v 1.101 2003/06/30 10:58:57 dwmw2 Exp $
+ * $Id: scan.c,v 1.106 2003/10/28 17:01:13 dwmw2 Exp $
  *
  */
 #include <linux/kernel.h>
@@ -116,7 +116,7 @@ int jffs2_scan_medium(struct jffs2_sb_in
 		ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), buf_size);
 
 		if (ret < 0)
-			return ret;
+			goto out;
 
 		ACCT_PARANOIA_CHECK(jeb);
 
@@ -240,17 +240,20 @@ int jffs2_scan_medium(struct jffs2_sb_in
 		if ( !c->used_size && ((empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) {
 			printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n");
 			printk(KERN_NOTICE "empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",empty_blocks,bad_blocks,c->nr_blocks);
-			return -EIO;
+			ret = -EIO;
+			goto out;
 		}
 		jffs2_erase_pending_trigger(c);
 	}
+	ret = 0;
+ out:
 	if (buf_size)
 		kfree(flashbuf);
 #ifndef __ECOS
 	else 
 		c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
 #endif
-	return 0;
+	return ret;
 }
 
 static int jffs2_fill_scan_buf (struct jffs2_sb_info *c, unsigned char *buf,
@@ -551,7 +554,7 @@ static int jffs2_scan_eraseblock (struct
 				marker_ref->next_in_ino = NULL;
 				marker_ref->next_phys = NULL;
 				marker_ref->flash_offset = ofs | REF_NORMAL;
-				marker_ref->totlen = c->cleanmarker_size;
+				marker_ref->__totlen = c->cleanmarker_size;
 				jeb->first_node = jeb->last_node = marker_ref;
 			     
 				USED_SPACE(PAD(c->cleanmarker_size));
@@ -631,6 +634,9 @@ static struct jffs2_inode_cache *jffs2_s
 	if (ic)
 		return ic;
 
+	if (ino > c->highest_ino)
+		c->highest_ino = ino;
+
 	ic = jffs2_alloc_inode_cache();
 	if (!ic) {
 		printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of inode cache failed\n");
@@ -642,7 +648,7 @@ static struct jffs2_inode_cache *jffs2_s
 	ic->nodes = (void *)ic;
 	jffs2_add_ino_cache(c, ic);
 	if (ino == 1)
-		ic->nlink=1;
+		ic->nlink = 1;
 	return ic;
 }
 
@@ -682,6 +688,7 @@ static int jffs2_scan_inode_node(struct 
 			       ofs, je32_to_cpu(ri->node_crc), crc);
 			/* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
 			DIRTY_SPACE(PAD(je32_to_cpu(ri->totlen)));
+			jffs2_free_raw_node_ref(raw);
 			return 0;
 		}
 		ic = jffs2_scan_make_ino_cache(c, ino);
@@ -694,7 +701,7 @@ static int jffs2_scan_inode_node(struct 
 	/* Wheee. It worked */
 
 	raw->flash_offset = ofs | REF_UNCHECKED;
-	raw->totlen = PAD(je32_to_cpu(ri->totlen));
+	raw->__totlen = PAD(je32_to_cpu(ri->totlen));
 	raw->next_phys = NULL;
 	raw->next_in_ino = ic->nodes;
 
@@ -771,7 +778,7 @@ static int jffs2_scan_dirent_node(struct
 		return -ENOMEM;
 	}
 	
-	raw->totlen = PAD(je32_to_cpu(rd->totlen));
+	raw->__totlen = PAD(je32_to_cpu(rd->totlen));
 	raw->flash_offset = ofs | REF_PRISTINE;
 	raw->next_phys = NULL;
 	raw->next_in_ino = ic->nodes;
--- a/packages/fs/jffs2/current/src/write.c
+++ b/packages/fs/jffs2/current/src/write.c
@@ -1,13 +1,13 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: write.c,v 1.65 2003/01/21 18:11:29 dwmw2 Exp $
+ * $Id: write.c,v 1.77 2003/11/18 21:14:02 dwmw2 Exp $
  *
  */
 
@@ -86,7 +86,7 @@ static void writecheck(struct jffs2_sb_i
 /* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it, 
    write it to the flash, link it into the existing inode/fragment list */
 
-struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs,  uint32_t *writelen)
+struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode)
 
 {
 	struct jffs2_raw_node_ref *raw;
@@ -94,6 +94,7 @@ struct jffs2_full_dnode *jffs2_write_dno
 	size_t retlen;
 	struct iovec vecs[2];
 	int ret;
+	int retried = 0;
 	unsigned long cnt = 2;
 
 	D1(if(je32_to_cpu(ri->hdr_crc) != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) {
@@ -120,24 +121,28 @@ struct jffs2_full_dnode *jffs2_write_dno
 		jffs2_free_raw_node_ref(raw);
 		return ERR_PTR(-ENOMEM);
 	}
-	raw->flash_offset = flash_ofs;
-	raw->totlen = PAD(sizeof(*ri)+datalen);
-	raw->next_phys = NULL;
 
 	fn->ofs = je32_to_cpu(ri->offset);
 	fn->size = je32_to_cpu(ri->dsize);
 	fn->frags = 0;
-	fn->raw = raw;
 
 	/* check number of valid vecs */
 	if (!datalen || !data)
 		cnt = 1;
+ retry:
+	fn->raw = raw;
 
-	ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen);
-		
+	raw->flash_offset = flash_ofs;
+	raw->__totlen = PAD(sizeof(*ri)+datalen);
+	raw->next_phys = NULL;
+
+	ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen,
+				 (alloc_mode==ALLOC_GC)?0:f->inocache->ino);
+
 	if (ret || (retlen != sizeof(*ri) + datalen)) {
 		printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", 
 		       sizeof(*ri)+datalen, flash_ofs, ret, retlen);
+
 		/* Mark the space as dirtied */
 		if (retlen) {
 			/* Doesn't belong to any inode */
@@ -155,11 +160,42 @@ struct jffs2_full_dnode *jffs2_write_dno
 			printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
 			jffs2_free_raw_node_ref(raw);
 		}
+		if (!retried && alloc_mode != ALLOC_NORETRY && (raw = jffs2_alloc_raw_node_ref())) {
+			/* Try to reallocate space and retry */
+			uint32_t dummy;
+			struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size];
 
+			retried = 1;
+
+			D1(printk(KERN_DEBUG "Retrying failed write.\n"));
+			
+			ACCT_SANITY_CHECK(c,jeb);
+			D1(ACCT_PARANOIA_CHECK(jeb));
+
+			if (alloc_mode == ALLOC_GC) {
+				ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs, &dummy);
+			} else {
+				/* Locking pain */
+				up(&f->sem);
+				jffs2_complete_reservation(c);
+			
+				ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &flash_ofs, &dummy, alloc_mode);
+				down(&f->sem);
+			}
+
+			if (!ret) {
+				D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
+
+				ACCT_SANITY_CHECK(c,jeb);
+				D1(ACCT_PARANOIA_CHECK(jeb));
+
+				goto retry;
+			}
+			D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
+			jffs2_free_raw_node_ref(raw);
+		}
 		/* Release the full_dnode which is now useless, and return */
 		jffs2_free_full_dnode(fn);
-		if (writelen)
-			*writelen = retlen;
 		return ERR_PTR(ret?ret:-EIO);
 	}
 	/* Mark the space used */
@@ -184,19 +220,21 @@ struct jffs2_full_dnode *jffs2_write_dno
 		  flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize), 
 		  je32_to_cpu(ri->csize), je32_to_cpu(ri->node_crc),
 		  je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen)));
-	if (writelen)
-		*writelen = retlen;
 
-	f->inocache->nodes = raw;
+	if (retried) {
+		ACCT_SANITY_CHECK(c,NULL);
+	}
+
 	return fn;
 }
 
-struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs,  uint32_t *writelen)
+struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode)
 {
 	struct jffs2_raw_node_ref *raw;
 	struct jffs2_full_dirent *fd;
 	size_t retlen;
 	struct iovec vecs[2];
+	int retried = 0;
 	int ret;
 
 	D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", 
@@ -225,11 +263,6 @@ struct jffs2_full_dirent *jffs2_write_di
 		jffs2_free_raw_node_ref(raw);
 		return ERR_PTR(-ENOMEM);
 	}
-	raw->flash_offset = flash_ofs;
-	raw->totlen = PAD(sizeof(*rd)+namelen);
-	raw->next_in_ino = f->inocache->nodes;
-	f->inocache->nodes = raw;
-	raw->next_phys = NULL;
 
 	fd->version = je32_to_cpu(rd->version);
 	fd->ino = je32_to_cpu(rd->ino);
@@ -237,14 +270,22 @@ struct jffs2_full_dirent *jffs2_write_di
 	fd->type = rd->type;
 	memcpy(fd->name, name, namelen);
 	fd->name[namelen]=0;
+
+ retry:
 	fd->raw = raw;
 
-	ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen);
+	raw->flash_offset = flash_ofs;
+	raw->__totlen = PAD(sizeof(*rd)+namelen);
+	raw->next_phys = NULL;
+
+	ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen,
+				 (alloc_mode==ALLOC_GC)?0:fd->ino);
 	if (ret || (retlen != sizeof(*rd) + namelen)) {
 		printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", 
 			       sizeof(*rd)+namelen, flash_ofs, ret, retlen);
 		/* Mark the space as dirtied */
 		if (retlen) {
+			raw->next_in_ino = NULL;
 			raw->flash_offset |= REF_OBSOLETE;
 			jffs2_add_physical_node_ref(c, raw);
 			jffs2_mark_node_obsolete(c, raw);
@@ -252,20 +293,53 @@ struct jffs2_full_dirent *jffs2_write_di
 			printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
 			jffs2_free_raw_node_ref(raw);
 		}
+		if (!retried && (raw = jffs2_alloc_raw_node_ref())) {
+			/* Try to reallocate space and retry */
+			uint32_t dummy;
+			struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size];
 
+			retried = 1;
+
+			D1(printk(KERN_DEBUG "Retrying failed write.\n"));
+
+			ACCT_SANITY_CHECK(c,jeb);
+			D1(ACCT_PARANOIA_CHECK(jeb));
+
+			if (alloc_mode == ALLOC_GC) {
+				ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs, &dummy);
+			} else {
+				/* Locking pain */
+				up(&f->sem);
+				jffs2_complete_reservation(c);
+			
+				ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &flash_ofs, &dummy, alloc_mode);
+				down(&f->sem);
+			}
+
+			if (!ret) {
+				D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
+				ACCT_SANITY_CHECK(c,jeb);
+				D1(ACCT_PARANOIA_CHECK(jeb));
+				goto retry;
+			}
+			D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
+			jffs2_free_raw_node_ref(raw);
+		}
 		/* Release the full_dnode which is now useless, and return */
 		jffs2_free_full_dirent(fd);
-		if (writelen)
-			*writelen = retlen;
 		return ERR_PTR(ret?ret:-EIO);
 	}
 	/* Mark the space used */
 	raw->flash_offset |= REF_PRISTINE;
 	jffs2_add_physical_node_ref(c, raw);
-	if (writelen)
-		*writelen = retlen;
+
+	raw->next_in_ino = f->inocache->nodes;
+	f->inocache->nodes = raw;
 
-	f->inocache->nodes = raw;
+	if (retried) {
+		ACCT_SANITY_CHECK(c,NULL);
+	}
+
 	return fd;
 }
 
@@ -288,7 +362,9 @@ int jffs2_write_inode_range(struct jffs2
 		unsigned char comprtype = JFFS2_COMPR_NONE;
 		uint32_t phys_ofs, alloclen;
 		uint32_t datalen, cdatalen;
+		int retried = 0;
 
+	retry:
 		D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset));
 
 		ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL);
@@ -300,21 +376,7 @@ int jffs2_write_inode_range(struct jffs2
 		datalen = writelen;
 		cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), writelen);
 
-		comprbuf = kmalloc(cdatalen, GFP_KERNEL);
-		if (comprbuf) {
-			comprtype = jffs2_compress(buf, comprbuf, &datalen, &cdatalen);
-		}
-		if (comprtype == JFFS2_COMPR_NONE) {
-			/* Either compression failed, or the allocation of comprbuf failed */
-			if (comprbuf)
-				kfree(comprbuf);
-			comprbuf = buf;
-			datalen = cdatalen;
-		}
-		/* Now comprbuf points to the data to be written, be it compressed or not.
-		   comprtype holds the compression type, and comprtype == JFFS2_COMPR_NONE means
-		   that the comprbuf doesn't need to be kfree()d. 
-		*/
+		comprtype = jffs2_compress(buf, &comprbuf, &datalen, &cdatalen);
 
 		ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
 		ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
@@ -331,15 +393,20 @@ int jffs2_write_inode_range(struct jffs2
 		ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 		ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
 
-		fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, NULL);
+		fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY);
 
-		if (comprtype != JFFS2_COMPR_NONE)
-			kfree(comprbuf);
+		jffs2_free_comprbuf(comprbuf, buf);
 
 		if (IS_ERR(fn)) {
 			ret = PTR_ERR(fn);
 			up(&f->sem);
 			jffs2_complete_reservation(c);
+			if (!retried) {
+				/* Write error to be retried */
+				retried = 1;
+				D1(printk(KERN_DEBUG "Retrying node write in jffs2_write_inode_range()\n"));
+				goto retry;
+			}
 			break;
 		}
 		ret = jffs2_add_full_dnode_to_inode(c, f, fn);
@@ -381,7 +448,6 @@ int jffs2_do_create(struct jffs2_sb_info
 	struct jffs2_full_dnode *fn;
 	struct jffs2_full_dirent *fd;
 	uint32_t alloclen, phys_ofs;
-	uint32_t writtenlen;
 	int ret;
 
 	/* Try to reserve enough space for both node and dirent. 
@@ -397,7 +463,7 @@ int jffs2_do_create(struct jffs2_sb_info
 	ri->data_crc = cpu_to_je32(0);
 	ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 
-	fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, &writtenlen);
+	fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
 
 	D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n",
 		  jemode_to_cpu(ri->mode)));
@@ -414,22 +480,14 @@ int jffs2_do_create(struct jffs2_sb_info
 	*/
 	f->metadata = fn;
 
-	/* Work out where to put the dirent node now. */
-	writtenlen = PAD(writtenlen);
-	phys_ofs += writtenlen;
-	alloclen -= writtenlen;
 	up(&f->sem);
-
-	if (alloclen < sizeof(*rd)+namelen) {
-		/* Not enough space left in this chunk. Get some more */
-		jffs2_complete_reservation(c);
-		ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+	jffs2_complete_reservation(c);
+	ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
 		
-		if (ret) {
-			/* Eep. */
-			D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
-			return ret;
-		}
+	if (ret) {
+		/* Eep. */
+		D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
+		return ret;
 	}
 
 	rd = jffs2_alloc_raw_dirent();
@@ -455,7 +513,7 @@ int jffs2_do_create(struct jffs2_sb_info
 	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
 	rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
 
-	fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, &writtenlen);
+	fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
 
 	jffs2_free_raw_dirent(rd);
 	
@@ -513,7 +571,7 @@ int jffs2_do_unlink(struct jffs2_sb_info
 	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
 	rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
 
-	fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, NULL);
+	fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION);
 	
 	jffs2_free_raw_dirent(rd);
 
@@ -598,7 +656,7 @@ int jffs2_do_link (struct jffs2_sb_info 
 	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
 	rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
 
-	fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, NULL);
+	fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
 	
 	jffs2_free_raw_dirent(rd);