yaffs: Refactor allocator
[yaffs2.git] / yaffs_guts.c
index 60227dffee59c50da79f5b13754e2dde20e73c26..604dc0910af9301d9b70d2feb304a9d9b1559b12 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
  *
- * Copyright (C) 2002-2007 Aleph One Ltd.
+ * Copyright (C) 2002-2010 Aleph One Ltd.
  *   for Toby Churchill Ltd and Brightstar Engineering
  *
  * Created by Charles Manning <charles@aleph1.co.uk>
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-
-const char *yaffs_guts_c_version =
-    "$Id: yaffs_guts.c,v 1.114 2010-03-07 22:07:03 charles Exp $";
-
 #include "yportenv.h"
 #include "yaffs_trace.h"
 
@@ -33,11 +29,34 @@ const char *yaffs_guts_c_version =
 #include "yaffs_nand.h"
 #include "yaffs_packedtags2.h"
 
+#include "yaffs_nameval.h"
+#include "yaffs_allocator.h"
+
+/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */
+#define YAFFS_GC_GOOD_ENOUGH 2
+#define YAFFS_GC_PASSIVE_THRESHOLD 4
+
+#define YAFFS_SMALL_HOLE_THRESHOLD 3
 
-#define YAFFS_PASSIVE_GC_CHUNKS 2
+/*
+ * Checkpoints are really no benefit on very small partitions.
+ *
+ * To save space on small partitions don't bother with checkpoints unless
+ * the partition is at least this big.
+ */
+#define YAFFS_CHECKPOINT_MIN_BLOCKS 60
 
 #include "yaffs_ecc.h"
 
+/* Private structure for doing xattr modifications */
+typedef struct {
+       int set; /* If 0 then this is a deletion */
+       const char *name;
+       const void *data;
+       int size;
+       int flags;
+       int result;
+} yaffs_XAttrMod;
 
 /* Robustification (if it ever comes about...) */
 static void yaffs_RetireBlock(yaffs_Device *dev, int blockInNAND);
@@ -68,11 +87,11 @@ static yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev, int number,
 static void yaffs_AddObjectToDirectory(yaffs_Object *directory,
                                yaffs_Object *obj);
 static int yaffs_UpdateObjectHeader(yaffs_Object *in, const YCHAR *name,
-                               int force, int isShrink, int shadows);
+                               int force, int isShrink, int shadows, yaffs_XAttrMod *xop);
+static int yaffs_ApplyXMod(yaffs_Device *dev, char *buffer, yaffs_XAttrMod *xmod);
+
 static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj);
 static int yaffs_CheckStructures(void);
-static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level,
-                       int chunkOffset, int *limit);
 static int yaffs_DoGenericObjectDeletion(yaffs_Object *in);
 
 static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device *dev, int blockNo);
@@ -114,6 +133,7 @@ static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev,
                                        yaffs_FileStructure *fStruct,
                                        __u32 chunkId);
 
+static int yaffs_HandleHole(yaffs_Object *obj, loff_t newSize);
 static void yaffs_SkipRestOfBlock(yaffs_Device *dev);
 static int yaffs_VerifyChunkWritten(yaffs_Device *dev,
                                        int chunkInNAND,
@@ -179,7 +199,7 @@ static __u32 ShiftsGE(__u32 x)
 
 static __u32 Shifts(__u32 x)
 {
-       int nShifts;
+       __u32 nShifts;
 
        nShifts =  0;
 
@@ -606,7 +626,8 @@ static void yaffs_VerifyObjectHeader(yaffs_Object *obj, yaffs_ObjectHeader *oh,
 }
 
 
-
+#if 0
+/* Not being used, but don't want to throw away yet */
 static int yaffs_VerifyTnodeWorker(yaffs_Object *obj, yaffs_Tnode *tn,
                                        __u32 level, int chunkOffset)
 {
@@ -652,6 +673,7 @@ static int yaffs_VerifyTnodeWorker(yaffs_Object *obj, yaffs_Tnode *tn,
 
 }
 
+#endif
 
 static void yaffs_VerifyFile(yaffs_Object *obj)
 {
@@ -1056,38 +1078,41 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev,
  * yaffs_FindOldestDirtySequence()
  * Calculate the oldest dirty sequence number if we don't know it.
  */
-static int yaffs_CalcOldestDirtySequence(yaffs_Device *dev)
+static void yaffs_CalcOldestDirtySequence(yaffs_Device *dev)
 {
        int i;
-       __u32 seq;
-       yaffs_BlockInfo *b = 0;
+       unsigned seq;
+       unsigned blockNo = 0;
+       yaffs_BlockInfo *b;
+
+       if(!dev->param.isYaffs2)
+               return;
 
-       
        /* Find the oldest dirty sequence number. */
-       seq = dev->sequenceNumber;
+       seq = dev->sequenceNumber + 1;
+       b = dev->blockInfo;
        for (i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) {
-               b = yaffs_GetBlockInfo(dev, i);
                if (b->blockState == YAFFS_BLOCK_STATE_FULL &&
-                   (b->pagesInUse - b->softDeletions) < dev->param.nChunksPerBlock &&
-                   b->sequenceNumber < seq) 
-                               seq = b->sequenceNumber;
+                       (b->pagesInUse - b->softDeletions) < dev->param.nChunksPerBlock &&
+                       b->sequenceNumber < seq) {
+                       seq = b->sequenceNumber;
+                       blockNo = i;
+               }
+               b++;
+       }
+
+       if(blockNo){
+               dev->oldestDirtySequence = seq;
+               dev->oldestDirtyBlock = blockNo;
        }
-       return seq;
+
 }
 
 
 static void yaffs_FindOldestDirtySequence(yaffs_Device *dev)
 {
-       if(!dev->oldestDirtySequence)
-               dev->oldestDirtySequence = 
-                       yaffs_CalcOldestDirtySequence(dev);
-
-#if 0
-       if(!yaffs_SkipVerification(dev) &&
-               dev->oldestDirtySequence != yaffs_CalcOldestDirtySequence(dev))
-               YBUG();
-
-#endif
+       if(dev->param.isYaffs2 && !dev->oldestDirtySequence)
+               yaffs_CalcOldestDirtySequence(dev);
 }
 
 /*
@@ -1099,8 +1124,13 @@ static void yaffs_FindOldestDirtySequence(yaffs_Device *dev)
 static void yaffs_ClearOldestDirtySequence(yaffs_Device *dev, yaffs_BlockInfo *bi)
 {
 
-       if(!bi || bi->sequenceNumber == dev->oldestDirtySequence)
+       if(!dev->param.isYaffs2)
+               return;
+
+       if(!bi || bi->sequenceNumber == dev->oldestDirtySequence){
                dev->oldestDirtySequence = 0;
+               dev->oldestDirtyBlock = 0;
+       }
 }
 
 /*
@@ -1108,11 +1138,13 @@ static void yaffs_ClearOldestDirtySequence(yaffs_Device *dev, yaffs_BlockInfo *b
  * Update the oldest dirty sequence number whenever we dirty a block.
  * Only do this if the oldestDirtySequence is actually being tracked.
  */
-static void yaffs_UpdateOldestDirtySequence(yaffs_Device *dev, yaffs_BlockInfo *bi)
+static void yaffs_UpdateOldestDirtySequence(yaffs_Device *dev, unsigned blockNo, yaffs_BlockInfo *bi)
 {
-       if(dev->oldestDirtySequence){
-               if(dev->oldestDirtySequence > bi->sequenceNumber)
+       if(dev->param.isYaffs2 && dev->oldestDirtySequence){
+               if(dev->oldestDirtySequence > bi->sequenceNumber){
                        dev->oldestDirtySequence = bi->sequenceNumber;
+                       dev->oldestDirtyBlock = blockNo;
+               }
        }
 }
  
@@ -1260,194 +1292,33 @@ static void yaffs_SetObjectName(yaffs_Object *obj, const YCHAR *name)
  * in the tnode.
  */
 
-/* yaffs_CreateTnodes creates a bunch more tnodes and
- * adds them to the tnode free list.
- * Don't use this function directly
- */
-static Y_INLINE int yaffs_CalcTnodeSize(yaffs_Device *dev)
-{
-       int tnodeSize;
-       /* Calculate the tnode size in bytes for variable width tnode support.
-        * Must be a multiple of 32-bits  */
-       tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8;
-
-       if (tnodeSize < sizeof(yaffs_Tnode))
-               tnodeSize = sizeof(yaffs_Tnode);
-       return tnodeSize;
-}
 
-static int yaffs_CreateTnodes(yaffs_Device *dev, int nTnodes)
+static yaffs_Tnode *yaffs_GetTnode(yaffs_Device *dev)
 {
-       int i;
-       int tnodeSize = yaffs_CalcTnodeSize(dev);
-       yaffs_Tnode *newTnodes;
-       __u8 *mem;
-       yaffs_Tnode *curr;
-       yaffs_Tnode *next;
-       yaffs_TnodeList *tnl;
-
-       if (nTnodes < 1)
-               return YAFFS_OK;
-
-
-       /* make these things */
-
-       newTnodes = YMALLOC(nTnodes * tnodeSize);
-       mem = (__u8 *)newTnodes;
-
-       if (!newTnodes) {
-               T(YAFFS_TRACE_ERROR,
-                       (TSTR("yaffs: Could not allocate Tnodes" TENDSTR)));
-               return YAFFS_FAIL;
-       }
-
-       /* Hook them into the free list */
-#if 0
-       for (i = 0; i < nTnodes - 1; i++) {
-               newTnodes[i].internal[0] = &newTnodes[i + 1];
-#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
-               newTnodes[i].internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
-#endif
-       }
-
-       newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes;
-#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
-       newTnodes[nTnodes - 1].internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
-#endif
-       dev->freeTnodes = newTnodes;
-#else
-       /* New hookup for wide tnodes */
-       for (i = 0; i < nTnodes - 1; i++) {
-               curr = (yaffs_Tnode *) &mem[i * tnodeSize];
-               next = (yaffs_Tnode *) &mem[(i+1) * tnodeSize];
-               curr->internal[0] = next;
+       yaffs_Tnode *tn = yaffs_AllocateRawTnode(dev);
+       if (tn){
+               memset(tn, 0, dev->tnodeSize);
+               dev->nTnodes++;
        }
 
-       curr = (yaffs_Tnode *) &mem[(nTnodes - 1) * tnodeSize];
-       curr->internal[0] = dev->freeTnodes;
-       dev->freeTnodes = (yaffs_Tnode *)mem;
-
-#endif
-
-
-       dev->nFreeTnodes += nTnodes;
-       dev->nTnodesCreated += nTnodes;
-
-       /* Now add this bunch of tnodes to a list for freeing up.
-        * NB If we can't add this to the management list it isn't fatal
-        * but it just means we can't free this bunch of tnodes later.
-        */
-
-       tnl = YMALLOC(sizeof(yaffs_TnodeList));
-       if (!tnl) {
-               T(YAFFS_TRACE_ERROR,
-                 (TSTR
-                  ("yaffs: Could not add tnodes to management list" TENDSTR)));
-                  return YAFFS_FAIL;
-       } else {
-               tnl->tnodes = newTnodes;
-               tnl->next = dev->allocatedTnodeList;
-               dev->allocatedTnodeList = tnl;
-       }
-
-       T(YAFFS_TRACE_ALLOCATE, (TSTR("yaffs: Tnodes added" TENDSTR)));
-
-       return YAFFS_OK;
-}
-
-/* GetTnode gets us a clean tnode. Tries to make allocate more if we run out */
-
-static yaffs_Tnode *yaffs_GetTnodeRaw(yaffs_Device *dev)
-{
-       yaffs_Tnode *tn = NULL;
-
-#ifdef CONFIG_YAFFS_VALGRIND_TEST
-       tn = YMALLOC(yaffs_CalcTnodeSize(dev));
-       if(tn)
-               dev->nTnodesCreated++;
-#else
-       /* If there are none left make more */
-       if (!dev->freeTnodes)
-               yaffs_CreateTnodes(dev, YAFFS_ALLOCATION_NTNODES);
-
-       if (dev->freeTnodes) {
-               tn = dev->freeTnodes;
-#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
-               if (tn->internal[YAFFS_NTNODES_INTERNAL] != (void *)1) {
-                       /* Hoosterman, this thing looks like it isn't in the list */
-                       T(YAFFS_TRACE_ALWAYS,
-                         (TSTR("yaffs: Tnode list bug 1" TENDSTR)));
-               }
-#endif
-               dev->freeTnodes = dev->freeTnodes->internal[0];
-               dev->nFreeTnodes--;
-       }
-#endif
        dev->nCheckpointBlocksRequired = 0; /* force recalculation*/
 
        return tn;
 }
 
-static yaffs_Tnode *yaffs_GetTnode(yaffs_Device *dev)
-{
-       yaffs_Tnode *tn = yaffs_GetTnodeRaw(dev);
-       int tnodeSize = yaffs_CalcTnodeSize(dev);
-
-       if (tn)
-               memset(tn, 0, tnodeSize);
-
-       return tn;
-}
-
 /* FreeTnode frees up a tnode and puts it back on the free list */
 static void yaffs_FreeTnode(yaffs_Device *dev, yaffs_Tnode *tn)
 {
-       if (tn) {
-#ifdef CONFIG_YAFFS_VALGRIND_TEST
-               YFREE(tn);
-               dev->nTnodesCreated--;
-#else
-#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
-               if (tn->internal[YAFFS_NTNODES_INTERNAL] != 0) {
-                       /* Hoosterman, this thing looks like it is already in the list */
-                       T(YAFFS_TRACE_ALWAYS,
-                         (TSTR("yaffs: Tnode list bug 2" TENDSTR)));
-               }
-               tn->internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
-#endif
-               tn->internal[0] = dev->freeTnodes;
-               dev->freeTnodes = tn;
-               dev->nFreeTnodes++;
-#endif
-       }
+       yaffs_FreeRawTnode(dev,tn);
+       dev->nTnodes--;
        dev->nCheckpointBlocksRequired = 0; /* force recalculation*/
 }
 
-static void yaffs_DeinitialiseTnodes(yaffs_Device *dev)
-{
-       /* Free the list of allocated tnodes */
-       yaffs_TnodeList *tmp;
-
-       while (dev->allocatedTnodeList) {
-               tmp = dev->allocatedTnodeList->next;
-
-               YFREE(dev->allocatedTnodeList->tnodes);
-               YFREE(dev->allocatedTnodeList);
-               dev->allocatedTnodeList = tmp;
-
-       }
-
-       dev->freeTnodes = NULL;
-       dev->nFreeTnodes = 0;
-       dev->nTnodesCreated = 0;
-}
-
-static void yaffs_InitialiseTnodes(yaffs_Device *dev)
+static void yaffs_DeinitialiseTnodesAndObjects(yaffs_Device *dev)
 {
-       dev->allocatedTnodeList = NULL;
-       dev->freeTnodes = NULL;
-       dev->nFreeTnodes = 0;
-       dev->nTnodesCreated = 0;
+       yaffs_DeinitialiseRawTnodesAndObjects(dev);
+       dev->nObjects = 0;
+       dev->nTnodes = 0;
 }
 
 
@@ -1694,9 +1565,10 @@ static int yaffs_FindChunkInGroup(yaffs_Device *dev, int theChunk,
        return -1;
 }
 
-
+#if 0
+/* Experimental code not being used yet. Might speed up file deletion */
 /* DeleteWorker scans backwards through the tnode tree and deletes all the
- * chunks and tnodes in the file
+ * chunks and tnodes in the file.
  * Returns 1 if the tree was deleted.
  * Returns 0 if it stopped early due to hitting the limit and the delete is incomplete.
  */
@@ -1788,16 +1660,21 @@ static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level,
 
 }
 
+#endif
+
 static void yaffs_SoftDeleteChunk(yaffs_Device *dev, int chunk)
 {
        yaffs_BlockInfo *theBlock;
+       unsigned blockNo;
 
        T(YAFFS_TRACE_DELETION, (TSTR("soft delete chunk %d" TENDSTR), chunk));
 
-       theBlock = yaffs_GetBlockInfo(dev, chunk / dev->param.nChunksPerBlock);
+       blockNo =  chunk / dev->param.nChunksPerBlock;
+       theBlock = yaffs_GetBlockInfo(dev, blockNo);
        if (theBlock) {
                theBlock->softDeletions++;
                dev->nFreeChunks++;
+               yaffs_UpdateOldestDirtySequence(dev, blockNo, theBlock);
        }
 }
 
@@ -1925,7 +1802,7 @@ static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device *dev, yaffs_Tnode *tn,
                                        hasData++;
                        }
                } else {
-                       int tnodeSize_u32 = yaffs_CalcTnodeSize(dev)/sizeof(__u32);
+                       int tnodeSize_u32 = dev->tnodeSize/sizeof(__u32);
                        __u32 *map = (__u32 *)tn;
 
                         for(i = 0; !hasData && i < tnodeSize_u32; i++){
@@ -1990,96 +1867,32 @@ static int yaffs_PruneFileStructure(yaffs_Device *dev,
 
 /*-------------------- End of File Structure functions.-------------------*/
 
-/* yaffs_CreateFreeObjects creates a bunch more objects and
- * adds them to the object free list.
- */
-static int yaffs_CreateFreeObjects(yaffs_Device *dev, int nObjects)
-{
-       int i;
-       yaffs_Object *newObjects;
-       yaffs_ObjectList *list;
-
-       if (nObjects < 1)
-               return YAFFS_OK;
-
-       /* make these things */
-       newObjects = YMALLOC(nObjects * sizeof(yaffs_Object));
-       list = YMALLOC(sizeof(yaffs_ObjectList));
-
-       if (!newObjects || !list) {
-               if (newObjects){
-                       YFREE(newObjects);
-                       newObjects = NULL;
-               }
-               if (list){
-                       YFREE(list);
-                       list = NULL;
-               }
-               T(YAFFS_TRACE_ALLOCATE,
-                 (TSTR("yaffs: Could not allocate more objects" TENDSTR)));
-               return YAFFS_FAIL;
-       }
-
-       /* Hook them into the free list */
-       for (i = 0; i < nObjects - 1; i++) {
-               newObjects[i].siblings.next =
-                               (struct ylist_head *)(&newObjects[i + 1]);
-       }
-
-       newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects;
-       dev->freeObjects = newObjects;
-       dev->nFreeObjects += nObjects;
-       dev->nObjectsCreated += nObjects;
-
-       /* Now add this bunch of Objects to a list for freeing up. */
-
-       list->objects = newObjects;
-       list->next = dev->allocatedObjectList;
-       dev->allocatedObjectList = list;
-
-       return YAFFS_OK;
-}
-
 
 /* AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out */
 static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device *dev)
 {
-       yaffs_Object *tn = NULL;
+       yaffs_Object *obj = yaffs_AllocateRawObject(dev);
 
-#ifdef CONFIG_YAFFS_VALGRIND_TEST
-       tn = YMALLOC(sizeof(yaffs_Object));
-       if(tn)
-               dev->nObjectsCreated++;
-#else
-       /* If there are none left make more */
-       if (!dev->freeObjects)
-               yaffs_CreateFreeObjects(dev, YAFFS_ALLOCATION_NOBJECTS);
+       if (obj) {
+               dev->nObjects++;
 
-       if (dev->freeObjects) {
-               tn = dev->freeObjects;
-               dev->freeObjects =
-                       (yaffs_Object *) (dev->freeObjects->siblings.next);
-               dev->nFreeObjects--;
-       }
-#endif
-       if (tn) {
                /* Now sweeten it up... */
 
-               memset(tn, 0, sizeof(yaffs_Object));
-               tn->beingCreated = 1;
+               memset(obj, 0, sizeof(yaffs_Object));
+               obj->beingCreated = 1;
 
-               tn->myDev = dev;
-               tn->hdrChunk = 0;
-               tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN;
-               YINIT_LIST_HEAD(&(tn->hardLinks));
-               YINIT_LIST_HEAD(&(tn->hashLink));
-               YINIT_LIST_HEAD(&tn->siblings);
+               obj->myDev = dev;
+               obj->hdrChunk = 0;
+               obj->variantType = YAFFS_OBJECT_TYPE_UNKNOWN;
+               YINIT_LIST_HEAD(&(obj->hardLinks));
+               YINIT_LIST_HEAD(&(obj->hashLink));
+               YINIT_LIST_HEAD(&obj->siblings);
 
 
                /* Now make the directory sane */
                if (dev->rootDir) {
-                       tn->parent = dev->rootDir;
-                       ylist_add(&(tn->siblings), &dev->rootDir->variant.directoryVariant.children);
+                       obj->parent = dev->rootDir;
+                       ylist_add(&(obj->siblings), &dev->rootDir->variant.directoryVariant.children);
                }
 
                /* Add it to the lost and found directory.
@@ -2087,14 +1900,14 @@ static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device *dev)
                 * check if lostNFound exists first
                 */
                if (dev->lostNFoundDir)
-                       yaffs_AddObjectToDirectory(dev->lostNFoundDir, tn);
+                       yaffs_AddObjectToDirectory(dev->lostNFoundDir, obj);
 
-               tn->beingCreated = 0;
+               obj->beingCreated = 0;
        }
 
        dev->nCheckpointBlocksRequired = 0; /* force recalculation*/
 
-       return tn;
+       return obj;
 }
 
 static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device *dev, int number,
@@ -2118,54 +1931,46 @@ static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device *dev, int number,
 
 }
 
-static void yaffs_UnhashObject(yaffs_Object *tn)
+static void yaffs_UnhashObject(yaffs_Object *obj)
 {
        int bucket;
-       yaffs_Device *dev = tn->myDev;
+       yaffs_Device *dev = obj->myDev;
 
        /* If it is still linked into the bucket list, free from the list */
-       if (!ylist_empty(&tn->hashLink)) {
-               ylist_del_init(&tn->hashLink);
-               bucket = yaffs_HashFunction(tn->objectId);
+       if (!ylist_empty(&obj->hashLink)) {
+               ylist_del_init(&obj->hashLink);
+               bucket = yaffs_HashFunction(obj->objectId);
                dev->objectBucket[bucket].count--;
        }
 }
 
 /*  FreeObject frees up a Object and puts it back on the free list */
-static void yaffs_FreeObject(yaffs_Object *tn)
+static void yaffs_FreeObject(yaffs_Object *obj)
 {
-       yaffs_Device *dev = tn->myDev;
+       yaffs_Device *dev = obj->myDev;
 
-       T(YAFFS_TRACE_OS, (TSTR("FreeObject %p inode %p"TENDSTR), tn, tn->myInode));
+       T(YAFFS_TRACE_OS, (TSTR("FreeObject %p inode %p"TENDSTR), obj, obj->myInode));
 
-       if (!tn)
+       if (!obj)
                YBUG();
-       if (tn->parent)
+       if (obj->parent)
                YBUG();
-       if (!ylist_empty(&tn->siblings))
+       if (!ylist_empty(&obj->siblings))
                YBUG();
 
 
-       if (tn->myInode) {
+       if (obj->myInode) {
                /* We're still hooked up to a cached inode.
                 * Don't delete now, but mark for later deletion
                 */
-               tn->deferedFree = 1;
+               obj->deferedFree = 1;
                return;
        }
 
-       yaffs_UnhashObject(tn);
-
-#ifdef CONFIG_YAFFS_VALGRIND_TEST
-       YFREE(tn);
-       dev->nObjectsCreated--;
-       tn = NULL;
-#else
-       /* Link into the free list. */
-       tn->siblings.next = (struct ylist_head *)(dev->freeObjects);
-       dev->freeObjects = tn;
-       dev->nFreeObjects++;
-#endif
+       yaffs_UnhashObject(obj);
+       
+       yaffs_FreeRawObject(dev,obj);
+       dev->nObjects--;
        dev->nCheckpointBlocksRequired = 0; /* force recalculation*/
 }
 
@@ -2176,33 +1981,14 @@ void yaffs_HandleDeferedFree(yaffs_Object *obj)
                yaffs_FreeObject(obj);
 }
 
-
-static void yaffs_DeinitialiseObjects(yaffs_Device *dev)
-{
-       /* Free the list of allocated Objects */
-
-       yaffs_ObjectList *tmp;
-
-       while (dev->allocatedObjectList) {
-               tmp = dev->allocatedObjectList->next;
-               YFREE(dev->allocatedObjectList->objects);
-               YFREE(dev->allocatedObjectList);
-
-               dev->allocatedObjectList = tmp;
-       }
-
-       dev->freeObjects = NULL;
-       dev->nFreeObjects = 0;
-       dev->nObjectsCreated = 0;
-}
-
-static void yaffs_InitialiseObjects(yaffs_Device *dev)
+static void yaffs_InitialiseTnodesAndObjects(yaffs_Device *dev)
 {
        int i;
 
-       dev->allocatedObjectList = NULL;
-       dev->freeObjects = NULL;
-       dev->nFreeObjects = 0;
+       dev->nObjects = 0;
+       dev->nTnodes = 0;
+
+       yaffs_InitialiseRawTnodesAndObjects(dev);
 
        for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) {
                YINIT_LIST_HEAD(&dev->objectBucket[i].list);
@@ -2352,6 +2138,8 @@ yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev, int number,
                case YAFFS_OBJECT_TYPE_DIRECTORY:
                        YINIT_LIST_HEAD(&theObject->variant.directoryVariant.
                                        children);
+                       YINIT_LIST_HEAD(&theObject->variant.directoryVariant.
+                                       dirty);
                        break;
                case YAFFS_OBJECT_TYPE_SYMLINK:
                case YAFFS_OBJECT_TYPE_HARDLINK:
@@ -2492,7 +2280,7 @@ static yaffs_Object *yaffs_MknodObject(yaffs_ObjectType type,
                        break;
                }
 
-               if (yaffs_UpdateObjectHeader(in, name, 0, 0, 0) < 0) {
+               if (yaffs_UpdateObjectHeader(in, name, 0, 0, 0, NULL) < 0) {
                        /* Could not create the object header, fail the creation */
                        yaffs_DeleteObject(in);
                        in = NULL;
@@ -2600,7 +2388,7 @@ static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir,
                        obj->unlinked = 1;
 
                /* If it is a deletion then we mark it as a shrink for gc purposes. */
-               if (yaffs_UpdateObjectHeader(obj, newName, 0, deleteOp, shadows) >= 0)
+               if (yaffs_UpdateObjectHeader(obj, newName, 0, deleteOp, shadows, NULL) >= 0)
                        return YAFFS_OK;
        }
 
@@ -2656,12 +2444,12 @@ int yaffs_RenameObject(yaffs_Object *oldDir, const YCHAR *oldName,
                         * Note we must disable gc otherwise it can mess up the shadowing.
                         *
                         */
-                       dev->isDoingGC=1;
+                       dev->gcDisable=1;
                        yaffs_ChangeObjectName(obj, newDir, newName, force,
                                                existingTarget->objectId);
                        existingTarget->isShadowed = 1;
                        yaffs_UnlinkObject(existingTarget);
-                       dev->isDoingGC=0;
+                       dev->gcDisable=0;
                }
 
                result = yaffs_ChangeObjectName(obj, newDir, newName, 1, 0);
@@ -2778,10 +2566,8 @@ static __u32 yaffs_FindRefreshBlock(yaffs_Device *dev)
         if(dev->refreshSkip > dev->param.refreshPeriod)
                 dev->refreshSkip = dev->param.refreshPeriod;
 
-       if(dev->refreshSkip > 0){
-               dev->refreshSkip--;
+       if(dev->refreshSkip > 0)
                return oldest;
-       }
 
        /*
         * Refresh skip is now zero.
@@ -2790,12 +2576,9 @@ static __u32 yaffs_FindRefreshBlock(yaffs_Device *dev)
         */
        dev->refreshSkip = dev->param.refreshPeriod;
        dev->refreshCount++;
-
+       bi = dev->blockInfo;
        for (b = dev->internalStartBlock; b <=dev->internalEndBlock; b++){
 
-               bi = yaffs_GetBlockInfo(dev, b);
-               
-
                if (bi->blockState == YAFFS_BLOCK_STATE_FULL){
 
                        if(oldest < 1 ||
@@ -2804,6 +2587,7 @@ static __u32 yaffs_FindRefreshBlock(yaffs_Device *dev)
                                 oldestSequence = bi->sequenceNumber;
                         }
                }
+               bi++;
        }
 
        if (oldest > 0) {
@@ -2815,107 +2599,6 @@ static __u32 yaffs_FindRefreshBlock(yaffs_Device *dev)
        return oldest;
 }
 
-/*
- * FindDiretiestBlock is used to select the dirtiest block (or close enough)
- * for garbage collection.
- */
-
-static int yaffs_FindBlockForGarbageCollection(yaffs_Device *dev,
-                                       int aggressive)
-{
-       int b = dev->currentDirtyChecker;
-
-       int i;
-       int iterations;
-       int dirtiest = -1;
-       int pagesInUse = 0;
-       int prioritised = 0;
-       yaffs_BlockInfo *bi;
-       int pendingPrioritisedExist = 0;
-
-       /* First let's see if we need to grab a prioritised block */
-       if (dev->hasPendingPrioritisedGCs) {
-               for (i = dev->internalStartBlock; i < dev->internalEndBlock && !prioritised; i++) {
-
-                       bi = yaffs_GetBlockInfo(dev, i);
-                       /* yaffs_VerifyBlock(dev,bi,i); */
-
-                       if (bi->gcPrioritise) {
-                               pendingPrioritisedExist = 1;
-                               if (bi->blockState == YAFFS_BLOCK_STATE_FULL &&
-                                  yaffs_BlockNotDisqualifiedFromGC(dev, bi)) {
-                                       pagesInUse = (bi->pagesInUse - bi->softDeletions);
-                                       dirtiest = i;
-                                       prioritised = 1;
-                                       aggressive = 1; /* Fool the non-aggressive skip logiv below */
-                               }
-                       }
-               }
-
-               if (!pendingPrioritisedExist) /* None found, so we can clear this */
-                       dev->hasPendingPrioritisedGCs = 0;
-       }
-
-       /* If we're doing aggressive GC then we are happy to take a less-dirty block, and
-        * search harder.
-        * else (we're doing a leasurely gc), then we only bother to do this if the
-        * block has only a few pages in use.
-        */
-
-       dev->nonAggressiveSkip--;
-
-       if (!aggressive && (dev->nonAggressiveSkip > 0))
-               return -1;
-
-       if (!prioritised)
-               pagesInUse =
-                       (aggressive) ? dev->param.nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1;
-
-       if (aggressive)
-               iterations =
-                   dev->internalEndBlock - dev->internalStartBlock + 1;
-       else {
-               iterations =
-                   dev->internalEndBlock - dev->internalStartBlock + 1;
-               iterations = iterations / 16;
-               if (iterations > 200)
-                       iterations = 200;
-       }
-
-       for (i = 0; i <= iterations && pagesInUse > 0 && !prioritised; i++) {
-               b++;
-               if (b < dev->internalStartBlock || b > dev->internalEndBlock)
-                       b = dev->internalStartBlock;
-
-               if (b < dev->internalStartBlock || b > dev->internalEndBlock) {
-                       T(YAFFS_TRACE_ERROR,
-                         (TSTR("**>> Block %d is not valid" TENDSTR), b));
-                       YBUG();
-               }
-
-               bi = yaffs_GetBlockInfo(dev, b);
-
-               if (bi->blockState == YAFFS_BLOCK_STATE_FULL &&
-                       (bi->pagesInUse - bi->softDeletions) < pagesInUse &&
-                               yaffs_BlockNotDisqualifiedFromGC(dev, bi)) {
-                       dirtiest = b;
-                       pagesInUse = (bi->pagesInUse - bi->softDeletions);
-               }
-       }
-
-       dev->currentDirtyChecker = b;
-
-       if (dirtiest > 0) {
-               T(YAFFS_TRACE_GC,
-                 (TSTR("GC Selected block %d with %d free, prioritised:%d" TENDSTR), dirtiest,
-                  dev->param.nChunksPerBlock - pagesInUse, prioritised));
-       }
-
-       if (dirtiest > 0)
-               dev->nonAggressiveSkip = 4;
-
-       return dirtiest;
-}
 
 static void yaffs_BlockBecameDirty(yaffs_Device *dev, int blockNo)
 {
@@ -2935,6 +2618,16 @@ static void yaffs_BlockBecameDirty(yaffs_Device *dev, int blockNo)
 
        bi->blockState = YAFFS_BLOCK_STATE_DIRTY;
 
+       /* If this is the block being garbage collected then stop gc'ing this block */
+       if(blockNo == dev->gcBlock)
+               dev->gcBlock = 0;
+
+       /* If this block is currently the best candidate for gc then drop as a candidate */
+       if(blockNo == dev->gcDirtiest){
+               dev->gcDirtiest = 0;
+               dev->gcPagesInUse = 0;
+       }
+
        if (!bi->needsRetiring) {
                yaffs_InvalidateCheckpoint(dev);
                erasedOk = yaffs_EraseBlockInNAND(dev, blockNo);
@@ -3032,22 +2725,28 @@ static int yaffs_FindBlockForAllocation(yaffs_Device *dev)
 
 
 
+static int yaffs_CheckpointRequired(yaffs_Device *dev)
+{
+       int nblocks = dev->internalEndBlock - dev->internalStartBlock + 1 ;
+       return dev->param.isYaffs2 &&
+               !dev->param.skipCheckpointWrite &&
+               (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS);
+}
 static int yaffs_CalcCheckpointBlocksRequired(yaffs_Device *dev)
 {
        if (!dev->nCheckpointBlocksRequired &&
-          dev->param.isYaffs2) {
+               yaffs_CheckpointRequired(dev)){
                /* Not a valid value so recalculate */
                int nBytes = 0;
                int nBlocks;
                int devBlocks = (dev->param.endBlock - dev->param.startBlock + 1);
-               int tnodeSize = yaffs_CalcTnodeSize(dev);
 
                nBytes += sizeof(yaffs_CheckpointValidity);
                nBytes += sizeof(yaffs_CheckpointDevice);
                nBytes += devBlocks * sizeof(yaffs_BlockInfo);
                nBytes += devBlocks * dev->chunkBitmapStride;
-               nBytes += (sizeof(yaffs_CheckpointObject) + sizeof(__u32)) * (dev->nObjectsCreated - dev->nFreeObjects);
-               nBytes += (tnodeSize + sizeof(__u32)) * (dev->nTnodesCreated - dev->nFreeTnodes);
+               nBytes += (sizeof(yaffs_CheckpointObject) + sizeof(__u32)) * (dev->nObjects);
+               nBytes += (dev->tnodeSize + sizeof(__u32)) * (dev->nTnodes);
                nBytes += sizeof(yaffs_CheckpointValidity);
                nBytes += sizeof(__u32); /* checksum*/
 
@@ -3065,7 +2764,7 @@ static int yaffs_CalcCheckpointBlocksRequired(yaffs_Device *dev)
  * Check if there's space to allocate...
  * Thinks.... do we need top make this ths same as yaffs_GetFreeChunks()?
  */
-static int yaffs_CheckSpaceForAllocation(yaffs_Device *dev)
+static int yaffs_CheckSpaceForAllocation(yaffs_Device *dev, int nChunks)
 {
        int reservedChunks;
        int reservedBlocks = dev->param.nReservedBlocks;
@@ -3082,7 +2781,7 @@ static int yaffs_CheckSpaceForAllocation(yaffs_Device *dev)
 
        reservedChunks = ((reservedBlocks + checkpointBlocks) * dev->param.nChunksPerBlock);
 
-       return (dev->nFreeChunks > reservedChunks);
+       return (dev->nFreeChunks > (reservedChunks + nChunks));
 }
 
 static int yaffs_AllocateChunk(yaffs_Device *dev, int useReserve,
@@ -3097,7 +2796,7 @@ static int yaffs_AllocateChunk(yaffs_Device *dev, int useReserve,
                dev->allocationPage = 0;
        }
 
-       if (!useReserve && !yaffs_CheckSpaceForAllocation(dev)) {
+       if (!useReserve && !yaffs_CheckSpaceForAllocation(dev, 1)) {
                /* Not enough space to allocate unless we're allowed to use the reserve. */
                return -1;
        }
@@ -3207,13 +2906,7 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block,
        
        bi->hasShrinkHeader = 0;        /* clear the flag so that the block can erase */
 
-       /* Take off the number of soft deleted entries because
-        * they're going to get really deleted during GC.
-        */
-       if(dev->gcChunk == 0) /* first time through for this block */
-               dev->nFreeChunks -= bi->softDeletions;
-
-       dev->isDoingGC = 1;
+       dev->gcDisable = 1;
 
        if (isCheckpointBlock ||
                        !yaffs_StillSomeChunkBits(dev, block)) {
@@ -3228,7 +2921,7 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block,
 
                yaffs_VerifyBlock(dev, bi, block);
 
-               maxCopies = (wholeBlock) ? dev->param.nChunksPerBlock : 10;
+               maxCopies = (wholeBlock) ? dev->param.nChunksPerBlock : 5;
                oldChunk = block * dev->param.nChunksPerBlock + dev->gcChunk;
 
                for (/* init already done */;
@@ -3292,6 +2985,13 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block,
                                         * No need to copy this, just forget about it and
                                         * fix up the object.
                                         */
+                                        
+                                       /* Free chunks already includes softdeleted chunks.
+                                        * How ever this chunk is going to soon be really deleted
+                                        * which will increment free chunks.
+                                        * We have to decrement free chunks so this works out properly.
+                                        */
+                                       dev->nFreeChunks--;
 
                                        object->nDataChunks--;
 
@@ -3334,14 +3034,20 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block,
 
                                                yaffs_ObjectHeader *oh;
                                                oh = (yaffs_ObjectHeader *)buffer;
+
                                                oh->isShrink = 0;
                                                tags.extraIsShrinkHeader = 0;
+
                                                oh->shadowsObject = 0;
                                                oh->inbandShadowsObject = 0;
-                                               if(object->variantType == YAFFS_OBJECT_TYPE_FILE)
-                                                       oh->fileSize = object->variant.fileVariant.fileSize;
                                                tags.extraShadows = 0;
 
+                                               /* Update file size */
+                                               if(object->variantType == YAFFS_OBJECT_TYPE_FILE){
+                                                       oh->fileSize = object->variant.fileVariant.fileSize;
+                                                       tags.extraFileLength = oh->fileSize;
+                                               }
+
                                                yaffs_VerifyObjectHeader(object, oh, &tags, 1);
                                                newChunk =
                                                    yaffs_WriteNewChunkWithTagsToNAND(dev,(__u8 *) oh, &tags, 1);
@@ -3406,8 +3112,14 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block,
 
 
 
-       /* If the gc completed then clear the current gcBlock so that we find another. */
-       if (bi->blockState != YAFFS_BLOCK_STATE_COLLECTING) {
+       if (bi->blockState == YAFFS_BLOCK_STATE_COLLECTING) {
+               /*
+                * The gc did not complete. Set block state back to FULL
+                * because checkpointing does not restore gc.
+                */
+               bi->blockState = YAFFS_BLOCK_STATE_FULL;
+       } else {
+               /* The gc completed. */
                chunksAfter = yaffs_GetErasedChunks(dev);
                if (chunksBefore >= chunksAfter) {
                        T(YAFFS_TRACE_GC,
@@ -3415,15 +3127,165 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block,
                           ("gc did not increase free chunks before %d after %d"
                            TENDSTR), chunksBefore, chunksAfter));
                }
-               dev->gcBlock = -1;
+               dev->gcBlock = 0;
                dev->gcChunk = 0;
        }
 
-       dev->isDoingGC = 0;
+       dev->gcDisable = 0;
 
        return retVal;
 }
 
+/*
+ * FindBlockForgarbageCollection is used to select the dirtiest block (or close enough)
+ * for garbage collection.
+ */
+
+static unsigned yaffs_FindBlockForGarbageCollection(yaffs_Device *dev,
+                                       int aggressive,
+                                       int background)
+{
+       int i;
+       int iterations;
+       unsigned selected = 0;
+       int prioritised = 0;
+       int prioritisedExists = 0;
+       yaffs_BlockInfo *bi;
+       int threshold;
+
+       /* First let's see if we need to grab a prioritised block */
+       if (dev->hasPendingPrioritisedGCs && !aggressive) {
+               dev->gcDirtiest = 0;
+               bi = dev->blockInfo;
+               for (i = dev->internalStartBlock;
+                       i <= dev->internalEndBlock && !selected;
+                       i++) {
+
+                       if (bi->gcPrioritise) {
+                               prioritisedExists = 1;
+                               if (bi->blockState == YAFFS_BLOCK_STATE_FULL &&
+                                  yaffs_BlockNotDisqualifiedFromGC(dev, bi)) {
+                                       selected = i;
+                                       prioritised = 1;
+                               }
+                       }
+                       bi++;
+               }
+
+               /*
+                * If there is a prioritised block and none was selected then
+                * this happened because there is at least one old dirty block gumming
+                * up the works. Let's gc the oldest dirty block.
+                */
+
+               if(prioritisedExists &&
+                       !selected &&
+                       dev->oldestDirtyBlock > 0)
+                       selected = dev->oldestDirtyBlock;
+
+               if (!prioritisedExists) /* None found, so we can clear this */
+                       dev->hasPendingPrioritisedGCs = 0;
+       }
+
+       /* If we're doing aggressive GC then we are happy to take a less-dirty block, and
+        * search harder.
+        * else (we're doing a leasurely gc), then we only bother to do this if the
+        * block has only a few pages in use.
+        */
+
+       if (!selected){
+               int pagesUsed;
+               int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
+               if (aggressive){
+                       threshold = dev->param.nChunksPerBlock;
+                       iterations = nBlocks;
+               } else {
+                       int maxThreshold = dev->param.nChunksPerBlock/2;
+                       threshold = background ?
+                               (dev->gcNotDone + 2) * 2 : 0;
+                       if(threshold <YAFFS_GC_PASSIVE_THRESHOLD)
+                               threshold = YAFFS_GC_PASSIVE_THRESHOLD;
+                       if(threshold > maxThreshold)
+                               threshold = maxThreshold;
+
+                       iterations = nBlocks / 16 + 1;
+                       if (iterations > 100)
+                               iterations = 100;
+               }
+
+               for (i = 0;
+                       i < iterations &&
+                       (dev->gcDirtiest < 1 ||
+                               dev->gcPagesInUse > YAFFS_GC_GOOD_ENOUGH);
+                       i++) {
+                       dev->gcBlockFinder++;
+                       if (dev->gcBlockFinder < dev->internalStartBlock ||
+                               dev->gcBlockFinder > dev->internalEndBlock)
+                               dev->gcBlockFinder = dev->internalStartBlock;
+
+                       bi = yaffs_GetBlockInfo(dev, dev->gcBlockFinder);
+
+                       pagesUsed = bi->pagesInUse - bi->softDeletions;
+
+                       if (bi->blockState == YAFFS_BLOCK_STATE_FULL &&
+                               pagesUsed < dev->param.nChunksPerBlock &&
+                               (dev->gcDirtiest < 1 || pagesUsed < dev->gcPagesInUse) &&
+                               yaffs_BlockNotDisqualifiedFromGC(dev, bi)) {
+                               dev->gcDirtiest = dev->gcBlockFinder;
+                               dev->gcPagesInUse = pagesUsed;
+                       }
+               }
+
+               if(dev->gcDirtiest > 0 && dev->gcPagesInUse <= threshold)
+                       selected = dev->gcDirtiest;
+       }
+
+       /*
+        * If nothing has been selected for a while, try selecting the oldest dirty
+        * because that's gumming up the works.
+        */
+
+       if(!selected && dev->param.isYaffs2 &&
+               dev->gcNotDone >= ( background ? 10 : 20)){
+               yaffs_FindOldestDirtySequence(dev);
+               if(dev->oldestDirtyBlock > 0) {
+                       selected = dev->oldestDirtyBlock;
+                       dev->gcDirtiest = selected;
+                       dev->oldestDirtyGCs++;
+                       bi = yaffs_GetBlockInfo(dev, selected);
+                       dev->gcPagesInUse =  bi->pagesInUse - bi->softDeletions;
+               } else
+                       dev->gcNotDone = 0;
+       }
+
+       if(selected){
+               T(YAFFS_TRACE_GC,
+                 (TSTR("GC Selected block %d with %d free, prioritised:%d" TENDSTR),
+                 selected,
+                 dev->param.nChunksPerBlock - dev->gcPagesInUse,
+                 prioritised));
+
+               if(background)
+                       dev->backgroundGCs++;
+               dev->gcDirtiest = 0;
+               dev->gcPagesInUse = 0;
+               dev->gcNotDone = 0;
+               if(dev->refreshSkip > 0)
+                       dev->refreshSkip--;
+       } else{
+               dev->gcNotDone++;
+               T(YAFFS_TRACE_GC,
+                 (TSTR("GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s" TENDSTR),
+                 dev->gcBlockFinder, dev->gcNotDone,
+                 threshold,
+                 dev->gcDirtiest, dev->gcPagesInUse,
+                 dev->oldestDirtyBlock,
+                 background ? " bg" : ""));
+       }
+
+       return selected;
+}
+
 /* New garbage collector
  * If we're very low on erased blocks then we do aggressive garbage collection
  * otherwise we do "leasurely" garbage collection.
@@ -3433,22 +3295,28 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block,
  * The idea is to help clear out space in a more spread-out manner.
  * Dunno if it really does anything useful.
  */
-static int yaffs_CheckGarbageCollection(yaffs_Device *dev)
+static int yaffs_CheckGarbageCollection(yaffs_Device *dev, int background)
 {
-       int block;
-       int aggressive;
+       int aggressive = 0;
        int gcOk = YAFFS_OK;
        int maxTries = 0;
 
+       int minErased;
+       int erasedChunks;
+
        int checkpointBlockAdjust;
 
-       if (dev->isDoingGC) {
+       if(dev->param.gcControl &&
+               (dev->param.gcControl(dev) & 1) == 0)
+               return YAFFS_OK;
+
+       if (dev->gcDisable) {
                /* Bail out so we don't get recursive gc */
                return YAFFS_OK;
        }
 
        /* This loop should pass the first time.
-        * We'll only see looping here if the erase of the collected block fails.
+        * We'll only see looping here if the collection does not increase space.
         */
 
        do {
@@ -3458,11 +3326,26 @@ static int yaffs_CheckGarbageCollection(yaffs_Device *dev)
                if (checkpointBlockAdjust < 0)
                        checkpointBlockAdjust = 0;
 
+               minErased  = dev->param.nReservedBlocks + checkpointBlockAdjust + 1;
+               erasedChunks = dev->nErasedBlocks * dev->param.nChunksPerBlock;
+
                /* If we need a block soon then do aggressive gc.*/
-               if (dev->nErasedBlocks < (dev->param.nReservedBlocks + checkpointBlockAdjust + 2))
+               if (dev->nErasedBlocks < minErased)
                        aggressive = 1;
-               else
-                       aggressive = 0;
+               else {
+                       if(dev->gcSkip > 20)
+                               dev->gcSkip = 20;
+                       if(erasedChunks < dev->nFreeChunks/2 ||
+                               dev->gcSkip < 1 ||
+                               background)
+                               aggressive = 0;
+                       else {
+                               dev->gcSkip--;
+                               break;
+                       }
+               }
+
+               dev->gcSkip = 5;
 
                 /* If we don't already have a block being gc'd then see if we should start another */
 
@@ -3471,38 +3354,51 @@ static int yaffs_CheckGarbageCollection(yaffs_Device *dev)
                        dev->gcChunk = 0;
                }
                if (dev->gcBlock < 1) {
-                       dev->gcBlock = yaffs_FindBlockForGarbageCollection(dev, aggressive);
+                       dev->gcBlock = yaffs_FindBlockForGarbageCollection(dev, aggressive, background);
                        dev->gcChunk = 0;
                }
 
-               block = dev->gcBlock;
-
-               if (block > 0) {
-                       dev->garbageCollections++;
+               if (dev->gcBlock > 0) {
+                       dev->allGCs++;
                        if (!aggressive)
-                               dev->passiveGarbageCollections++;
+                               dev->passiveGCs++;
 
                        T(YAFFS_TRACE_GC,
                          (TSTR
                           ("yaffs: GC erasedBlocks %d aggressive %d" TENDSTR),
                           dev->nErasedBlocks, aggressive));
 
-                       gcOk = yaffs_GarbageCollectBlock(dev, block, aggressive);
+                       gcOk = yaffs_GarbageCollectBlock(dev, dev->gcBlock, aggressive);
                }
 
-               if (dev->nErasedBlocks < (dev->param.nReservedBlocks) && block > 0) {
+               if (dev->nErasedBlocks < (dev->param.nReservedBlocks) && dev->gcBlock > 0) {
                        T(YAFFS_TRACE_GC,
                          (TSTR
                           ("yaffs: GC !!!no reclaim!!! erasedBlocks %d after try %d block %d"
-                           TENDSTR), dev->nErasedBlocks, maxTries, block));
+                           TENDSTR), dev->nErasedBlocks, maxTries, dev->gcBlock));
                }
        } while ((dev->nErasedBlocks < dev->param.nReservedBlocks) &&
-                (block > 0) &&
+                (dev->gcBlock > 0) &&
                 (maxTries < 2));
 
        return aggressive ? gcOk : YAFFS_OK;
 }
 
+/*
+ * yaffs_BackgroundGarbageCollect()
+ * Garbage collects. Intended to be called from a background thread.
+ * Returns non-zero if at least half the free chunks are erased.
+ */
+int yaffs_BackgroundGarbageCollect(yaffs_Device *dev, unsigned urgency)
+{
+       int erasedChunks = dev->nErasedBlocks * dev->param.nChunksPerBlock;
+
+       T(YAFFS_TRACE_BACKGROUND, (TSTR("Background gc %u" TENDSTR),urgency));
+
+       yaffs_CheckGarbageCollection(dev, 1);
+       return erasedChunks > dev->nFreeChunks/2;
+}
+
 /*-------------------------  TAGS --------------------------------*/
 
 static int yaffs_TagsMatch(const yaffs_ExtendedTags *tags, int objectId,
@@ -3806,7 +3702,7 @@ void yaffs_DeleteChunk(yaffs_Device *dev, int chunkId, int markNAND, int lyn)
 
        bi = yaffs_GetBlockInfo(dev, block);
        
-       yaffs_UpdateOldestDirtySequence(dev,bi);
+       yaffs_UpdateOldestDirtySequence(dev, block, bi);
 
        T(YAFFS_TRACE_DELETION,
          (TSTR("line %d delete of chunk %d" TENDSTR), lyn, chunkId));
@@ -3865,7 +3761,7 @@ static int yaffs_WriteChunkDataToObject(yaffs_Object *in, int chunkInInode,
 
        yaffs_Device *dev = in->myDev;
 
-       yaffs_CheckGarbageCollection(dev);
+       yaffs_CheckGarbageCollection(dev,0);
 
        /* Get the previous chunk at this location in the file if it exists.
         * If it does not exist then put a zero into the tree. This creates
@@ -3912,7 +3808,7 @@ static int yaffs_WriteChunkDataToObject(yaffs_Object *in, int chunkInInode,
  * If name is not NULL, then that new name is used.
  */
 int yaffs_UpdateObjectHeader(yaffs_Object *in, const YCHAR *name, int force,
-                            int isShrink, int shadows)
+                            int isShrink, int shadows, yaffs_XAttrMod *xmod)
 {
 
        yaffs_BlockInfo *bi;
@@ -3938,9 +3834,9 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in, const YCHAR *name, int force,
 
        if (!in->fake ||
                in == dev->rootDir || /* The rootDir should also be saved */
-               force) {
+               force  || xmod) {
 
-               yaffs_CheckGarbageCollection(dev);
+               yaffs_CheckGarbageCollection(dev,0);
                yaffs_CheckObjectDetailsLoaded(in);
 
                buffer = yaffs_GetTempBuffer(in->myDev, __LINE__);
@@ -3955,9 +3851,9 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in, const YCHAR *name, int force,
                        yaffs_VerifyObjectHeader(in, oh, &oldTags, 0);
 
                        memcpy(oldName, oh->name, sizeof(oh->name));
-               }
-
-               memset(buffer, 0xFF, dev->nDataBytesPerChunk);
+                       memset(buffer, 0xFF, sizeof(yaffs_ObjectHeader));
+               } else
+                       memset(buffer, 0xFF, dev->nDataBytesPerChunk);
 
                oh->type = in->variantType;
                oh->yst_mode = in->yst_mode;
@@ -4025,6 +3921,11 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in, const YCHAR *name, int force,
                        break;
                }
 
+               /* process any xattrib modifications */
+               if(xmod)
+                       yaffs_ApplyXMod(dev, (char *)buffer, xmod);
+
+
                /* Tags */
                yaffs_InitialiseTags(&newTags);
                in->serial++;
@@ -4546,7 +4447,6 @@ static int yaffs_CheckpointTnodeWorker(yaffs_Object *in, yaffs_Tnode *tn,
        int i;
        yaffs_Device *dev = in->myDev;
        int ok = 1;
-       int tnodeSize = yaffs_CalcTnodeSize(dev);
 
        if (tn) {
                if (level > 0) {
@@ -4563,7 +4463,7 @@ static int yaffs_CheckpointTnodeWorker(yaffs_Object *in, yaffs_Tnode *tn,
                        __u32 baseOffset = chunkOffset <<  YAFFS_TNODES_LEVEL0_BITS;
                        ok = (yaffs_CheckpointWrite(dev, &baseOffset, sizeof(baseOffset)) == sizeof(baseOffset));
                        if (ok)
-                               ok = (yaffs_CheckpointWrite(dev, tn, tnodeSize) == tnodeSize);
+                               ok = (yaffs_CheckpointWrite(dev, tn, dev->tnodeSize) == dev->tnodeSize);
                }
        }
 
@@ -4597,7 +4497,6 @@ static int yaffs_ReadCheckpointTnodes(yaffs_Object *obj)
        yaffs_FileStructure *fileStructPtr = &obj->variant.fileVariant;
        yaffs_Tnode *tn;
        int nread = 0;
-       int tnodeSize = yaffs_CalcTnodeSize(dev);
 
        ok = (yaffs_CheckpointRead(dev, &baseChunk, sizeof(baseChunk)) == sizeof(baseChunk));
 
@@ -4606,10 +4505,10 @@ static int yaffs_ReadCheckpointTnodes(yaffs_Object *obj)
                /* Read level 0 tnode */
 
 
-               tn = yaffs_GetTnodeRaw(dev);
-               if (tn)
-                       ok = (yaffs_CheckpointRead(dev, tn, tnodeSize) == tnodeSize);
-               else
+               tn = yaffs_GetTnode(dev);
+               if (tn){
+                       ok = (yaffs_CheckpointRead(dev, tn, dev->tnodeSize) == dev->tnodeSize);
+               else
                        ok = 0;
 
                if (tn && ok)
@@ -4759,7 +4658,7 @@ static int yaffs_WriteCheckpointData(yaffs_Device *dev)
 {
        int ok = 1;
 
-       if (dev->param.skipCheckpointWrite || !dev->param.isYaffs2) {
+       if (!yaffs_CheckpointRequired(dev)) {
                T(YAFFS_TRACE_CHECKPOINT, (TSTR("skipping checkpoint write" TENDSTR)));
                ok = 0;
        }
@@ -4850,9 +4749,9 @@ static void yaffs_InvalidateCheckpoint(yaffs_Device *dev)
                        dev->blocksInCheckpoint > 0) {
                dev->isCheckpointed = 0;
                yaffs_CheckpointInvalidateStream(dev);
-               if (dev->param.markSuperBlockDirty)
-                       dev->param.markSuperBlockDirty(dev);
        }
+       if (dev->param.markSuperBlockDirty)
+               dev->param.markSuperBlockDirty(dev);
 }
 
 
@@ -4995,7 +4894,7 @@ int yaffs_ReadDataFromFile(yaffs_Object *in, __u8 *buffer, loff_t offset,
        return nDone;
 }
 
-int yaffs_WriteDataToFile(yaffs_Object *in, const __u8 *buffer, loff_t offset,
+int yaffs_DoWriteDataToFile(yaffs_Object *in, const __u8 *buffer, loff_t offset,
                        int nBytes, int writeThrough)
 {
 
@@ -5015,8 +4914,6 @@ int yaffs_WriteDataToFile(yaffs_Object *in, const __u8 *buffer, loff_t offset,
        dev = in->myDev;
 
        while (n > 0 && chunkWritten >= 0) {
-               /* chunk = offset / dev->nDataBytesPerChunk + 1; */
-               /* start = offset % dev->nDataBytesPerChunk; */
                yaffs_AddrToChunk(dev, offset, &chunk, &start);
 
                if (chunk * dev->nDataBytesPerChunk + start != offset ||
@@ -5026,7 +4923,7 @@ int yaffs_WriteDataToFile(yaffs_Object *in, const __u8 *buffer, loff_t offset,
                           TENDSTR),
                           (int)offset, chunk, start));
                }
-               chunk++;
+               chunk++; /* File pos to chunk in file offset */
 
                /* OK now check for the curveball where the start and end are in
                 * the same chunk.
@@ -5072,19 +4969,17 @@ int yaffs_WriteDataToFile(yaffs_Object *in, const __u8 *buffer, loff_t offset,
                                cache = yaffs_FindChunkCache(in, chunk);
 
                                if (!cache
-                                   && yaffs_CheckSpaceForAllocation(in->
-                                                                    myDev)) {
-                                       cache = yaffs_GrabChunkCache(in->myDev);
+                                   && yaffs_CheckSpaceForAllocation(dev, 1)) {
+                                       cache = yaffs_GrabChunkCache(dev);
                                        cache->object = in;
                                        cache->chunkId = chunk;
                                        cache->dirty = 0;
                                        cache->locked = 0;
                                        yaffs_ReadChunkDataFromObject(in, chunk,
-                                                                     cache->
-                                                                     data);
+                                                                     cache->data);
                                } else if (cache &&
                                        !cache->dirty &&
-                                       !yaffs_CheckSpaceForAllocation(in->myDev)) {
+                                       !yaffs_CheckSpaceForAllocation(dev, 1)) {
                                        /* Drop the cache if it was a read cache item and
                                         * no space check has been made for it.
                                         */
@@ -5175,6 +5070,14 @@ int yaffs_WriteDataToFile(yaffs_Object *in, const __u8 *buffer, loff_t offset,
        return nDone;
 }
 
+int yaffs_WriteDataToFile(yaffs_Object *in, const __u8 *buffer, loff_t offset,
+                       int nBytes, int writeThrough)
+{
+       yaffs_HandleHole(in,offset);
+       return yaffs_DoWriteDataToFile(in,buffer,offset,nBytes,writeThrough);
+}
+
+
 
 /* ---------------------- File resizing stuff ------------------ */
 
@@ -5219,57 +5122,146 @@ static void yaffs_PruneResizedChunks(yaffs_Object *in, int newSize)
 
 }
 
-int yaffs_ResizeFile(yaffs_Object *in, loff_t newSize)
-{
 
-       int oldFileSize = in->variant.fileVariant.fileSize;
-       __u32 newSizeOfPartialChunk;
+static void yaffs_ResizeDown( yaffs_Object *obj, loff_t newSize)
+{
        int newFullChunks;
-
-       yaffs_Device *dev = in->myDev;
+       __u32 newSizeOfPartialChunk;
+       yaffs_Device *dev = obj->myDev;
 
        yaffs_AddrToChunk(dev, newSize, &newFullChunks, &newSizeOfPartialChunk);
 
-       yaffs_FlushFilesChunkCache(in);
-       yaffs_InvalidateWholeChunkCache(in);
+       yaffs_PruneResizedChunks(obj, newSize);
 
-       yaffs_CheckGarbageCollection(dev);
+       if (newSizeOfPartialChunk != 0) {
+               int lastChunk = 1 + newFullChunks;
+               __u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__);
 
-       if (in->variantType != YAFFS_OBJECT_TYPE_FILE)
+               /* Got to read and rewrite the last chunk with its new size and zero pad */
+               yaffs_ReadChunkDataFromObject(obj, lastChunk, localBuffer);
+               memset(localBuffer + newSizeOfPartialChunk, 0,
+                       dev->nDataBytesPerChunk - newSizeOfPartialChunk);
+
+               yaffs_WriteChunkDataToObject(obj, lastChunk, localBuffer,
+                                            newSizeOfPartialChunk, 1);
+
+               yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__);
+       }
+
+       obj->variant.fileVariant.fileSize = newSize;
+
+       yaffs_PruneFileStructure(dev, &obj->variant.fileVariant);
+}
+
+
+static int yaffs_HandleHole(yaffs_Object *obj, loff_t newSize)
+{
+       /* if newsSize > oldFileSize.
+        * We're going to be writing a hole.
+        * If the hole is small then write zeros otherwise write a start of hole marker.
+        */
+               
+
+       loff_t oldFileSize;
+       int increase;
+       int smallHole   ;
+       int result = YAFFS_OK;
+       yaffs_Device *dev = NULL;
+
+       __u8 *localBuffer = NULL;
+       
+       int smallIncreaseOk = 0;
+       
+       if(!obj)
                return YAFFS_FAIL;
 
-       if (newSize == oldFileSize)
+       if(obj->variantType != YAFFS_OBJECT_TYPE_FILE)
+               return YAFFS_FAIL;
+       
+       dev = obj->myDev;
+       
+       /* Bail out if not yaffs2 mode */
+       if(!dev->param.isYaffs2)
                return YAFFS_OK;
 
-       if (newSize < oldFileSize) {
+       oldFileSize = obj->variant.fileVariant.fileSize;
 
-               yaffs_PruneResizedChunks(in, newSize);
+       if (newSize <= oldFileSize)
+               return YAFFS_OK;
 
-               if (newSizeOfPartialChunk != 0) {
-                       int lastChunk = 1 + newFullChunks;
+       increase = newSize - oldFileSize;
 
-                       __u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__);
+       if(increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->nDataBytesPerChunk &&
+               yaffs_CheckSpaceForAllocation(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1))
+               smallHole = 1;
+       else
+               smallHole = 0;
 
-                       /* Got to read and rewrite the last chunk with its new size and zero pad */
-                       yaffs_ReadChunkDataFromObject(in, lastChunk,
-                                                     localBuffer);
+       if(smallHole)
+               localBuffer= yaffs_GetTempBuffer(dev, __LINE__);
+       
+       if(localBuffer){
+               /* fill hole with zero bytes */
+               int pos = oldFileSize;
+               int thisWrite;
+               int written;
+               memset(localBuffer,0,dev->nDataBytesPerChunk);
+               smallIncreaseOk = 1;
+
+               while(increase > 0 && smallIncreaseOk){
+                       thisWrite = increase;
+                       if(thisWrite > dev->nDataBytesPerChunk)
+                               thisWrite = dev->nDataBytesPerChunk;
+                       written = yaffs_DoWriteDataToFile(obj,localBuffer,pos,thisWrite,0);
+                       if(written == thisWrite){
+                               pos += thisWrite;
+                               increase -= thisWrite;
+                       } else
+                               smallIncreaseOk = 0;
+               }
 
-                       memset(localBuffer + newSizeOfPartialChunk, 0,
-                              dev->nDataBytesPerChunk - newSizeOfPartialChunk);
+               yaffs_ReleaseTempBuffer(dev,localBuffer,__LINE__);
 
-                       yaffs_WriteChunkDataToObject(in, lastChunk, localBuffer,
-                                                    newSizeOfPartialChunk, 1);
+               /* If we were out of space then reverse any chunks we've added */               
+               if(!smallIncreaseOk)
+                       yaffs_ResizeDown(obj, oldFileSize);
+       }
+       
+       if (!smallIncreaseOk &&
+               obj->parent &&
+               obj->parent->objectId != YAFFS_OBJECTID_UNLINKED &&
+               obj->parent->objectId != YAFFS_OBJECTID_DELETED){
+               /* Write a hole start header with the old file size */
+               yaffs_UpdateObjectHeader(obj, NULL, 0, 1, 0, NULL);
+       }
 
-                       yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__);
-               }
+       return result;
 
-               in->variant.fileVariant.fileSize = newSize;
+}
 
-               yaffs_PruneFileStructure(dev, &in->variant.fileVariant);
-       } else {
-               /* newsSize > oldFileSize */
+int yaffs_ResizeFile(yaffs_Object *in, loff_t newSize)
+{
+       yaffs_Device *dev = in->myDev;
+       int oldFileSize = in->variant.fileVariant.fileSize;
+
+       yaffs_FlushFilesChunkCache(in);
+       yaffs_InvalidateWholeChunkCache(in);
+
+       yaffs_CheckGarbageCollection(dev,0);
+
+       if (in->variantType != YAFFS_OBJECT_TYPE_FILE)
+               return YAFFS_FAIL;
+
+       if (newSize == oldFileSize)
+               return YAFFS_OK;
+               
+       if(newSize > oldFileSize){
+               yaffs_HandleHole(in,newSize);
                in->variant.fileVariant.fileSize = newSize;
-       }
+       } else {
+               /* newSize < oldFileSize */ 
+               yaffs_ResizeDown(in, newSize);
+       } 
 
        /* Write a new object header to reflect the resize.
         * show we've shrunk the file, if need be
@@ -5280,8 +5272,8 @@ int yaffs_ResizeFile(yaffs_Object *in, loff_t newSize)
            !in->isShadowed &&
            in->parent->objectId != YAFFS_OBJECTID_UNLINKED &&
            in->parent->objectId != YAFFS_OBJECTID_DELETED)
-               yaffs_UpdateObjectHeader(in, NULL, 0,
-                                        (newSize < oldFileSize) ? 1 : 0, 0);
+               yaffs_UpdateObjectHeader(in, NULL, 0, 0, 0, NULL);
+
 
        return YAFFS_OK;
 }
@@ -5324,7 +5316,7 @@ int yaffs_FlushFile(yaffs_Object *in, int updateTime, int dataSync)
 #endif
                        }
 
-                       retVal = (yaffs_UpdateObjectHeader(in, NULL, 0, 0, 0) >=
+                       retVal = (yaffs_UpdateObjectHeader(in, NULL, 0, 0, 0, NULL) >=
                                0) ? YAFFS_OK : YAFFS_FAIL;
                }
        } else {
@@ -5365,6 +5357,7 @@ static int yaffs_UnlinkFileIfNeeded(yaffs_Object *in)
 
        int retVal;
        int immediateDeletion = 0;
+       yaffs_Device *dev = in->myDev;
 
        if (!in->myInode)
                immediateDeletion = 1;
@@ -5378,7 +5371,7 @@ static int yaffs_UnlinkFileIfNeeded(yaffs_Object *in)
                   in->objectId));
                in->deleted = 1;
                in->myDev->nDeletedFiles++;
-               if (1 || in->myDev->param.isYaffs2)
+               if (dev->param.disableSoftDelete || dev->param.isYaffs2)
                        yaffs_ResizeFile(in, 0);
                yaffs_SoftDeleteFile(in);
        } else {
@@ -5394,9 +5387,11 @@ static int yaffs_UnlinkFileIfNeeded(yaffs_Object *in)
 int yaffs_DeleteFile(yaffs_Object *in)
 {
        int retVal = YAFFS_OK;
-       int deleted = in->deleted;
+       int deleted; /* Need to cache value on stack if in is freed */
+       yaffs_Device *dev = in->myDev;
 
-       yaffs_ResizeFile(in, 0);
+       if (dev->param.disableSoftDelete || dev->param.isYaffs2)
+               yaffs_ResizeFile(in, 0);
 
        if (in->nDataChunks > 0) {
                /* Use soft deletion if there is data in the file.
@@ -5405,6 +5400,8 @@ int yaffs_DeleteFile(yaffs_Object *in)
                if (!in->unlinked)
                        retVal = yaffs_UnlinkFileIfNeeded(in);
 
+               deleted = in->deleted;
+
                if (retVal == YAFFS_OK && in->unlinked && !in->deleted) {
                        in->deleted = 1;
                        deleted = 1;
@@ -5463,6 +5460,10 @@ int retVal = -1;
                retVal = yaffs_DeleteFile(obj);
                break;
        case YAFFS_OBJECT_TYPE_DIRECTORY:
+               if(!ylist_empty(&obj->variant.directoryVariant.dirty)){
+                       T(YAFFS_TRACE_BACKGROUND, (TSTR("Remove object %d from dirty directories" TENDSTR),obj->objectId));
+                       ylist_del_init(&obj->variant.directoryVariant.dirty);
+               }
                return yaffs_DeleteDirectory(obj);
                break;
        case YAFFS_OBJECT_TYPE_SYMLINK:
@@ -5537,6 +5538,7 @@ static int yaffs_UnlinkWorker(yaffs_Object *obj)
                        return yaffs_DeleteFile(obj);
                        break;
                case YAFFS_OBJECT_TYPE_DIRECTORY:
+                       ylist_del_init(&obj->variant.directoryVariant.dirty);
                        return yaffs_DeleteDirectory(obj);
                        break;
                case YAFFS_OBJECT_TYPE_SYMLINK:
@@ -5854,8 +5856,8 @@ static int yaffs_Scan(yaffs_Device *dev)
        dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER;
 
        /* Scan all the blocks to determine their state */
+       bi = dev->blockInfo;
        for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) {
-               bi = yaffs_GetBlockInfo(dev, blk);
                yaffs_ClearChunkBits(dev, blk);
                bi->pagesInUse = 0;
                bi->softDeletions = 0;
@@ -5881,6 +5883,7 @@ static int yaffs_Scan(yaffs_Device *dev)
                        dev->nErasedBlocks++;
                        dev->nFreeChunks += dev->param.nChunksPerBlock;
                }
+               bi++;
        }
 
        startIterator = dev->internalStartBlock;
@@ -6238,7 +6241,7 @@ static int yaffs_Scan(yaffs_Device *dev)
                        obj = yaffs_FindObjectByNumber(dev, fixer->objectId);
 
                        if (obj)
-                               yaffs_UpdateObjectHeader(obj, NULL, 1, 0, 0);
+                               yaffs_UpdateObjectHeader(obj, NULL, 1, 0, 0, NULL);
 
                        YFREE(fixer);
                }
@@ -6378,8 +6381,8 @@ static int yaffs_ScanBackwards(yaffs_Device *dev)
        chunkData = yaffs_GetTempBuffer(dev, __LINE__);
 
        /* Scan all the blocks to determine their state */
+       bi = dev->blockInfo;
        for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) {
-               bi = yaffs_GetBlockInfo(dev, blk);
                yaffs_ClearChunkBits(dev, blk);
                bi->pagesInUse = 0;
                bi->softDeletions = 0;
@@ -6432,6 +6435,7 @@ static int yaffs_ScanBackwards(yaffs_Device *dev)
 
                        }
                }
+               bi++;
        }
 
        T(YAFFS_TRACE_SCAN,
@@ -6545,7 +6549,7 @@ static int yaffs_ScanBackwards(yaffs_Device *dev)
                                                         * the current allocation block.
                                                         */
 
-                                                        T(YAFFS_TRACE_ALWAYS,
+                                                        T(YAFFS_TRACE_SCAN,
                                                         (TSTR("Partially written block %d detected" TENDSTR),
                                                         blk));
                                                }
@@ -7045,17 +7049,57 @@ static void yaffs_VerifyDirectory(yaffs_Object *directory)
  *   create dir/a : update dir's mtime/ctime
  *   rm dir/a:   update dir's mtime/ctime
  *   modify dir/a: don't update dir's mtimme/ctime
+ *
+ * This can be handled immediately or defered. Defering helps reduce the number
+ * of updates when many files in a directory are changed within a brief period.
+ *
+ * If the directory updating is defered then yaffs_UpdateDirtyDirecories must be
+ * called periodically.
  */
  
 static void yaffs_UpdateParent(yaffs_Object *obj)
 {
+       yaffs_Device *dev;
        if(!obj)
                return;
 
+       dev = obj->myDev;
        obj->dirty = 1;
        obj->yst_mtime = obj->yst_ctime = Y_CURRENT_TIME;
+       if(dev->param.deferDirectoryUpdate){
+               struct ylist_head *link = &obj->variant.directoryVariant.dirty; 
+       
+               if(ylist_empty(link)){
+                       ylist_add(link,&dev->dirtyDirectories);
+                       T(YAFFS_TRACE_BACKGROUND, (TSTR("Added object %d to dirty directories" TENDSTR),obj->objectId));
+               }
+
+       } else
+               yaffs_UpdateObjectHeader(obj, NULL, 0, 0, 0, NULL);
+}
+
+void yaffs_UpdateDirtyDirectories(yaffs_Device *dev)
+{
+       struct ylist_head *link;
+       yaffs_Object *obj;
+       yaffs_DirectoryStructure *dS;
+       yaffs_ObjectVariant *oV;
+
+       T(YAFFS_TRACE_BACKGROUND, (TSTR("Update dirty directories" TENDSTR)));
+
+       while(!ylist_empty(&dev->dirtyDirectories)){
+               link = dev->dirtyDirectories.next;
+               ylist_del_init(link);
+               
+               dS=ylist_entry(link,yaffs_DirectoryStructure,dirty);
+               oV = ylist_entry(dS,yaffs_ObjectVariant,directoryVariant);
+               obj = ylist_entry(oV,yaffs_Object,variant);
 
-       yaffs_UpdateObjectHeader(obj,NULL,0,0,0);
+               T(YAFFS_TRACE_BACKGROUND, (TSTR("Update directory %d" TENDSTR), obj->objectId));
+
+               if(obj->dirty)
+                       yaffs_UpdateObjectHeader(obj, NULL, 0, 0, 0, NULL);
+       }
 }
 
 static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj)
@@ -7385,7 +7429,7 @@ int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr)
        if (valid & ATTR_SIZE)
                yaffs_ResizeFile(obj, attr->ia_size);
 
-       yaffs_UpdateObjectHeader(obj, NULL, 1, 0, 0);
+       yaffs_UpdateObjectHeader(obj, NULL, 1, 0, 0, NULL);
 
        return YAFFS_OK;
 
@@ -7418,6 +7462,104 @@ int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr)
 
 #endif
 
+
+static int yaffs_DoXMod(yaffs_Object *obj, int set, const char *name, const void *value, int size, int flags)
+{
+       yaffs_XAttrMod xmod;
+
+       int result;
+
+       xmod.set = set;
+       xmod.name = name;
+       xmod.data = value;
+       xmod.size =  size;
+       xmod.flags = flags;
+       xmod.result = -ENOSPC;
+
+       result = yaffs_UpdateObjectHeader(obj, NULL, 0, 0, 0, &xmod);
+
+       if(result > 0)
+               return xmod.result;
+       else
+               return -ENOSPC;
+}
+
+static int yaffs_ApplyXMod(yaffs_Device *dev, char *buffer, yaffs_XAttrMod *xmod)
+{
+       int retval = 0;
+       int x_offs = sizeof(yaffs_ObjectHeader);
+       int x_size = dev->nDataBytesPerChunk - sizeof(yaffs_ObjectHeader);
+
+       char * x_buffer = buffer + x_offs;
+
+       if(xmod->set)
+               retval = nval_set(x_buffer, x_size, xmod->name, xmod->data, xmod->size, xmod->flags);
+       else
+               retval = nval_del(x_buffer, x_size, xmod->name);
+
+       xmod->result = retval;
+
+       return retval;
+}
+
+static int yaffs_DoXFetch(yaffs_Object *obj, const char *name, void *value, int size)
+{
+       char *buffer = NULL;
+       int result;
+       yaffs_ExtendedTags tags;
+       yaffs_Device *dev = obj->myDev;
+       int x_offs = sizeof(yaffs_ObjectHeader);
+       int x_size = dev->nDataBytesPerChunk - sizeof(yaffs_ObjectHeader);
+
+       __u8 * x_buffer;
+
+       int retval = 0;
+
+       if(obj->hdrChunk < 1)
+               return -ENOENT;
+
+       buffer = yaffs_GetTempBuffer(dev, __LINE__);
+       if(!buffer)
+               return -ENOMEM;
+
+       result = yaffs_ReadChunkWithTagsFromNAND(dev,obj->hdrChunk, buffer, &tags);
+
+       if(result != YAFFS_OK)
+               retval = -ENOENT;
+       else{
+               x_buffer =  buffer + x_offs;
+
+               if(name)
+                       retval = nval_get(x_buffer, x_size, name, value, size);
+               else
+                       retval = nval_list(x_buffer, x_size, value,size);
+       }
+       yaffs_ReleaseTempBuffer(dev,buffer,__LINE__);
+       return retval;
+}
+
+int yaffs_SetXAttribute(yaffs_Object *obj, const char *name, const void * value, int size, int flags)
+{
+       return yaffs_DoXMod(obj, 1, name, value, size, flags);
+}
+
+int yaffs_RemoveXAttribute(yaffs_Object *obj, const char *name)
+{
+       return yaffs_DoXMod(obj, 0, name, NULL, 0, 0);
+}
+
+int yaffs_GetXAttribute(yaffs_Object *obj, const char *name, void *value, int size)
+{
+       return yaffs_DoXFetch(obj, name, value, size);
+}
+
+int yaffs_ListXAttributes(yaffs_Object *obj, char *buffer, int size)
+{
+       return yaffs_DoXFetch(obj, NULL, buffer,size);
+}
+
+
+
 #if 0
 int yaffs_DumpObject(yaffs_Object *obj)
 {
@@ -7521,7 +7663,7 @@ int yaffs_GutsInitialise(yaffs_Device *dev)
        dev->chunkOffset = 0;
        dev->nFreeChunks = 0;
 
-       dev->gcBlock = -1;
+       dev->gcBlock = 0;
 
        if (dev->param.startBlock == 0) {
                dev->internalStartBlock = dev->param.startBlock + 1;
@@ -7633,6 +7775,9 @@ int yaffs_GutsInitialise(yaffs_Device *dev)
        else
                dev->chunkGroupBits = bits - dev->tnodeWidth;
 
+       dev->tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8;
+       if(dev->tnodeSize < sizeof(yaffs_Tnode))
+               dev->tnodeSize = sizeof(yaffs_Tnode);
 
        dev->chunkGroupSize = 1 << dev->chunkGroupBits;
 
@@ -7650,9 +7795,11 @@ int yaffs_GutsInitialise(yaffs_Device *dev)
        /* OK, we've finished verifying the device, lets continue with initialisation */
 
        /* More device initialisation */
-       dev->garbageCollections = 0;
-       dev->passiveGarbageCollections = 0;
-       dev->currentDirtyChecker = 0;
+       dev->allGCs = 0;
+       dev->passiveGCs = 0;
+       dev->oldestDirtyGCs = 0;
+       dev->backgroundGCs = 0;
+       dev->gcBlockFinder = 0;
        dev->bufferedBlock = -1;
        dev->doingBufferedBlockRewrite = 0;
        dev->nDeletedFiles = 0;
@@ -7664,9 +7811,11 @@ int yaffs_GutsInitialise(yaffs_Device *dev)
        dev->tagsEccUnfixed = 0;
        dev->nErasureFailures = 0;
        dev->nErasedBlocks = 0;
-       dev->isDoingGC = 0;
+       dev->gcDisable= 0;
        dev->hasPendingPrioritisedGCs = 1; /* Assume the worst for now, will get fixed on first GC */
+       YINIT_LIST_HEAD(&dev->dirtyDirectories);
        dev->oldestDirtySequence = 0;
+       dev->oldestDirtyBlock = 0;
 
        /* Initialise temporary buffers and caches. */
        if (!yaffs_InitialiseTempBuffers(dev))
@@ -7718,8 +7867,7 @@ int yaffs_GutsInitialise(yaffs_Device *dev)
        if (!init_failed && !yaffs_InitialiseBlocks(dev))
                init_failed = 1;
 
-       yaffs_InitialiseTnodes(dev);
-       yaffs_InitialiseObjects(dev);
+       yaffs_InitialiseTnodesAndObjects(dev);
 
        if (!init_failed && !yaffs_CreateInitialDirectories(dev))
                init_failed = 1;
@@ -7738,9 +7886,8 @@ int yaffs_GutsInitialise(yaffs_Device *dev)
                                 * and scan backwards.
                                 */
                                yaffs_DeinitialiseBlocks(dev);
-                               yaffs_DeinitialiseTnodes(dev);
-                               yaffs_DeinitialiseObjects(dev);
 
+                               yaffs_DeinitialiseTnodesAndObjects(dev);
 
                                dev->nErasedBlocks = 0;
                                dev->nFreeChunks = 0;
@@ -7753,8 +7900,7 @@ int yaffs_GutsInitialise(yaffs_Device *dev)
                                if (!init_failed && !yaffs_InitialiseBlocks(dev))
                                        init_failed = 1;
 
-                               yaffs_InitialiseTnodes(dev);
-                               yaffs_InitialiseObjects(dev);
+                               yaffs_InitialiseTnodesAndObjects(dev);
 
                                if (!init_failed && !yaffs_CreateInitialDirectories(dev))
                                        init_failed = 1;
@@ -7808,8 +7954,7 @@ void yaffs_Deinitialise(yaffs_Device *dev)
                int i;
 
                yaffs_DeinitialiseBlocks(dev);
-               yaffs_DeinitialiseTnodes(dev);
-               yaffs_DeinitialiseObjects(dev);
+               yaffs_DeinitialiseTnodesAndObjects(dev);
                if (dev->param.nShortOpCaches > 0 &&
                    dev->srCache) {
 
@@ -7837,15 +7982,13 @@ void yaffs_Deinitialise(yaffs_Device *dev)
 
 static int yaffs_CountFreeChunks(yaffs_Device *dev)
 {
-       int nFree;
+       int nFree=0;
        int b;
 
        yaffs_BlockInfo *blk;
 
-       for (nFree = 0, b = dev->internalStartBlock; b <= dev->internalEndBlock;
-                       b++) {
-               blk = yaffs_GetBlockInfo(dev, b);
-
+       blk = dev->blockInfo;
+       for (b = dev->internalStartBlock; b <= dev->internalEndBlock; b++) {
                switch (blk->blockState) {
                case YAFFS_BLOCK_STATE_EMPTY:
                case YAFFS_BLOCK_STATE_ALLOCATING:
@@ -7858,6 +8001,7 @@ static int yaffs_CountFreeChunks(yaffs_Device *dev)
                default:
                        break;
                }
+               blk++;
        }
 
        return nFree;