Add test for reduced shrink headers
[yaffs2.git] / yaffs_guts.c
index ff6e9932a769f94185b20b799125b2ebe03bf8f1..7fc606d7c579639425b1365a68fec310569eade0 100644 (file)
@@ -12,7 +12,7 @@
  */
 
 const char *yaffs_guts_c_version =
-    "$Id: yaffs_guts.c,v 1.111 2010-03-02 02:29:21 charles Exp $";
+    "$Id: yaffs_guts.c,v 1.117 2010-03-11 02:44:43 charles Exp $";
 
 #include "yportenv.h"
 #include "yaffs_trace.h"
@@ -404,16 +404,19 @@ static int yaffs_CountChunkBits(yaffs_Device *dev, int blk)
 
 static int yaffs_SkipVerification(yaffs_Device *dev)
 {
+       dev=dev;
        return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL));
 }
 
 static int yaffs_SkipFullVerification(yaffs_Device *dev)
 {
+       dev=dev;
        return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_FULL));
 }
 
 static int yaffs_SkipNANDVerification(yaffs_Device *dev)
 {
+       dev=dev;
        return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_NAND));
 }
 
@@ -1044,6 +1047,80 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev,
        return chunk;
 }
 
+
+/*
+ * Oldest Dirty Sequence Number handling.
+ */
+/* yaffs_CalcOldestDirtySequence()
+ * yaffs_FindOldestDirtySequence()
+ * Calculate the oldest dirty sequence number if we don't know it.
+ */
+static int yaffs_CalcOldestDirtySequence(yaffs_Device *dev)
+{
+       int i;
+       __u32 seq;
+       yaffs_BlockInfo *b = 0;
+
+       if(!dev->param.isYaffs2)
+               return 0;
+
+       /* Find the oldest dirty sequence number. */
+       seq = dev->sequenceNumber;
+       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;
+       }
+       return seq;
+}
+
+
+static void yaffs_FindOldestDirtySequence(yaffs_Device *dev)
+{
+       if(dev->param.isYaffs2 && !dev->oldestDirtySequence)
+               dev->oldestDirtySequence = 
+                       yaffs_CalcOldestDirtySequence(dev);
+
+#if 0
+       if(!yaffs_SkipVerification(dev) &&
+               dev->oldestDirtySequence != yaffs_CalcOldestDirtySequence(dev))
+               YBUG();
+
+#endif
+}
+
+/*
+ * yaffs_ClearOldestDirtySequence()
+ * Called when a block is erased or marked bad. (ie. when its sequenceNumber
+ * becomes invalid). If the value matches the oldest then we clear 
+ * dev->oldestDirtySequence to force its recomputation.
+ */
+static void yaffs_ClearOldestDirtySequence(yaffs_Device *dev, yaffs_BlockInfo *bi)
+{
+
+       if(!dev->param.isYaffs2)
+               return;
+
+       if(!bi || bi->sequenceNumber == dev->oldestDirtySequence)
+               dev->oldestDirtySequence = 0;
+}
+
+/*
+ * yaffs_UpdateOldestDirtySequence()
+ * 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)
+{
+       if(dev->param.isYaffs2 && dev->oldestDirtySequence){
+               if(dev->oldestDirtySequence > bi->sequenceNumber)
+                       dev->oldestDirtySequence = bi->sequenceNumber;
+       }
+}
 /*
  * Block retiring for handling a broken block.
  */
@@ -1053,6 +1130,8 @@ static void yaffs_RetireBlock(yaffs_Device *dev, int blockInNAND)
        yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND);
 
        yaffs_InvalidateCheckpoint(dev);
+       
+       yaffs_ClearOldestDirtySequence(dev,bi);
 
        if (yaffs_MarkBlockBad(dev, blockInNAND) != YAFFS_OK) {
                if (yaffs_EraseBlockInNAND(dev, blockInNAND) != YAFFS_OK) {
@@ -1094,11 +1173,18 @@ static void yaffs_HandleWriteChunkOk(yaffs_Device *dev, int chunkInNAND,
                                const __u8 *data,
                                const yaffs_ExtendedTags *tags)
 {
+       dev=dev;
+       chunkInNAND=chunkInNAND;
+       data=data;
+       tags=tags;
 }
 
 static void yaffs_HandleUpdateChunk(yaffs_Device *dev, int chunkInNAND,
                                const yaffs_ExtendedTags *tags)
 {
+       dev=dev;
+       chunkInNAND=chunkInNAND;
+       tags=tags;
 }
 
 void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi)
@@ -1446,6 +1532,8 @@ static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev,
        int requiredTallness;
        int level = fStruct->topLevel;
 
+       dev=dev;
+
        /* Check sane level and chunk Id */
        if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL)
                return NULL;
@@ -1715,6 +1803,7 @@ static void yaffs_SoftDeleteChunk(yaffs_Device *dev, int chunk)
        if (theBlock) {
                theBlock->softDeletions++;
                dev->nFreeChunks++;
+               yaffs_UpdateOldestDirtySequence(dev,theBlock);
        }
 }
 
@@ -2269,6 +2358,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:
@@ -2653,9 +2744,6 @@ static void yaffs_DeinitialiseBlocks(yaffs_Device *dev)
 static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device *dev,
                                        yaffs_BlockInfo *bi)
 {
-       int i;
-       __u32 seq;
-       yaffs_BlockInfo *b;
 
        if (!dev->param.isYaffs2)
                return 1;       /* disqualification only applies to yaffs2. */
@@ -2663,23 +2751,7 @@ static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device *dev,
        if (!bi->hasShrinkHeader)
                return 1;       /* can gc */
 
-       /* Find the oldest dirty sequence number if we don't know it and save it
-        * so we don't have to keep recomputing it.
-        */
-       if (!dev->oldestDirtySequence) {
-               seq = dev->sequenceNumber;
-
-               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;
-                       }
-               }
-               dev->oldestDirtySequence = seq;
-       }
+       yaffs_FindOldestDirtySequence(dev);
 
        /* Can't do gc of this block if there are any blocks older than this one that have
         * discarded pages.
@@ -2847,8 +2919,6 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device *dev,
                   dev->param.nChunksPerBlock - pagesInUse, prioritised));
        }
 
-       dev->oldestDirtySequence = 0;
-
        if (dirtiest > 0)
                dev->nonAggressiveSkip = 4;
 
@@ -2869,6 +2939,8 @@ static void yaffs_BlockBecameDirty(yaffs_Device *dev, int blockNo)
                (TSTR("yaffs_BlockBecameDirty block %d state %d %s"TENDSTR),
                blockNo, bi->blockState, (bi->needsRetiring) ? "needs retiring" : ""));
 
+       yaffs_ClearOldestDirtySequence(dev,bi);
+
        bi->blockState = YAFFS_BLOCK_STATE_DIRTY;
 
        if (!bi->needsRetiring) {
@@ -3270,14 +3342,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);
@@ -3741,6 +3819,8 @@ void yaffs_DeleteChunk(yaffs_Device *dev, int chunkId, int markNAND, int lyn)
                         chunkId));
 
        bi = yaffs_GetBlockInfo(dev, block);
+       
+       yaffs_UpdateOldestDirtySequence(dev,bi);
 
        T(YAFFS_TRACE_DELETION,
          (TSTR("line %d delete of chunk %d" TENDSTR), lyn, chunkId));
@@ -4315,7 +4395,6 @@ static void yaffs_DeviceToCheckpointDevice(yaffs_CheckpointDevice *cp,
        cp->nUnlinkedFiles = dev->nUnlinkedFiles;
        cp->nBackgroundDeletions = dev->nBackgroundDeletions;
        cp->sequenceNumber = dev->sequenceNumber;
-       cp->oldestDirtySequence = dev->oldestDirtySequence;
 
 }
 
@@ -4331,7 +4410,6 @@ static void yaffs_CheckpointDeviceToDevice(yaffs_Device *dev,
        dev->nUnlinkedFiles = cp->nUnlinkedFiles;
        dev->nBackgroundDeletions = cp->nBackgroundDeletions;
        dev->sequenceNumber = cp->sequenceNumber;
-       dev->oldestDirtySequence = cp->oldestDirtySequence;
 }
 
 
@@ -4786,9 +4864,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);
 }
 
 
@@ -5207,7 +5285,6 @@ int yaffs_ResizeFile(yaffs_Object *in, loff_t newSize)
                in->variant.fileVariant.fileSize = newSize;
        }
 
-
        /* Write a new object header to reflect the resize.
         * show we've shrunk the file, if need be
         * Do this only if the file is not in the deleted directories
@@ -5400,6 +5477,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:
@@ -5474,6 +5555,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:
@@ -6982,17 +7064,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));
+               }
 
-       yaffs_UpdateObjectHeader(obj,NULL,0,0,0);
+       } else
+               yaffs_UpdateObjectHeader(obj,NULL,0,0,0);
+}
+
+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);
+
+               T(YAFFS_TRACE_BACKGROUND, (TSTR("Update directory %d" TENDSTR), obj->objectId));
+
+               if(obj->dirty)
+                       yaffs_UpdateObjectHeader(obj,NULL,0,0,0);
+       }
 }
 
 static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj)
@@ -7603,6 +7725,8 @@ int yaffs_GutsInitialise(yaffs_Device *dev)
        dev->nErasedBlocks = 0;
        dev->isDoingGC = 0;
        dev->hasPendingPrioritisedGCs = 1; /* Assume the worst for now, will get fixed on first GC */
+       YINIT_LIST_HEAD(&dev->dirtyDirectories);
+       dev->oldestDirtySequence = 0;
 
        /* Initialise temporary buffers and caches. */
        if (!yaffs_InitialiseTempBuffers(dev))
@@ -7685,7 +7809,6 @@ int yaffs_GutsInitialise(yaffs_Device *dev)
                                dev->nDeletedFiles = 0;
                                dev->nUnlinkedFiles = 0;
                                dev->nBackgroundDeletions = 0;
-                               dev->oldestDirtySequence = 0;
 
                                if (!init_failed && !yaffs_InitialiseBlocks(dev))
                                        init_failed = 1;