Dynamically update the oldest dirty sequence number so that it does not have to be...
[yaffs2.git] / yaffs_guts.c
index 1b0a67f7e6cc78bb7a165ee12f0fefae2b90d32a..60227dffee59c50da79f5b13754e2dde20e73c26 100644 (file)
@@ -12,7 +12,7 @@
  */
 
 const char *yaffs_guts_c_version =
-    "$Id: yaffs_guts.c,v 1.108 2010-02-18 01:18:04 charles Exp $";
+    "$Id: yaffs_guts.c,v 1.114 2010-03-07 22:07:03 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));
 }
 
@@ -682,12 +685,6 @@ static void yaffs_VerifyFile(yaffs_Object *obj)
 
        actualTallness = obj->variant.fileVariant.topLevel;
 
-       if (requiredTallness > actualTallness)
-               T(YAFFS_TRACE_VERIFY,
-               (TSTR("Obj %d had tnode tallness %d, needs to be %d"TENDSTR),
-                obj->objectId, actualTallness, requiredTallness));
-
-
        /* Check that the chunks in the tnode tree are all correct.
         * We do this by scanning through the tnode tree and
         * checking the tags for every chunk match.
@@ -1050,6 +1047,75 @@ 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;
+
+       
+       /* 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->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(!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->oldestDirtySequence){
+               if(dev->oldestDirtySequence > bi->sequenceNumber)
+                       dev->oldestDirtySequence = bi->sequenceNumber;
+       }
+}
 /*
  * Block retiring for handling a broken block.
  */
@@ -1059,6 +1125,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) {
@@ -1100,11 +1168,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)
@@ -1452,6 +1527,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;
@@ -2135,33 +2212,21 @@ static void yaffs_InitialiseObjects(yaffs_Device *dev)
 
 static int yaffs_FindNiceObjectBucket(yaffs_Device *dev)
 {
-       static int x;
        int i;
        int l = 999;
        int lowest = 999999;
 
-       /* First let's see if we can find one that's empty. */
-
-       for (i = 0; i < 10 && lowest > 0; i++) {
-               x++;
-               x %= YAFFS_NOBJECT_BUCKETS;
-               if (dev->objectBucket[x].count < lowest) {
-                       lowest = dev->objectBucket[x].count;
-                       l = x;
-               }
 
-       }
-
-       /* If we didn't find an empty list, then try
-        * looking a bit further for a short one
+       /* Search for the shortest list or one that
+        * isn't too long.
         */
 
-       for (i = 0; i < 10 && lowest > 3; i++) {
-               x++;
-               x %= YAFFS_NOBJECT_BUCKETS;
-               if (dev->objectBucket[x].count < lowest) {
-                       lowest = dev->objectBucket[x].count;
-                       l = x;
+       for (i = 0; i < 10 && lowest > 4; i++) {
+               dev->bucketFinder++;
+               dev->bucketFinder %= YAFFS_NOBJECT_BUCKETS;
+               if (dev->objectBucket[dev->bucketFinder].count < lowest) {
+                       lowest = dev->objectBucket[dev->bucketFinder].count;
+                       l = dev->bucketFinder;
                }
 
        }
@@ -2671,9 +2736,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. */
@@ -2681,23 +2743,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.
@@ -2705,7 +2751,72 @@ static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device *dev,
        return (bi->sequenceNumber <= dev->oldestDirtySequence);
 }
 
-/* FindDiretiestBlock is used to select the dirtiest block (or close enough)
+/*
+ * yaffs_FindRefreshBlock()
+ * periodically finds the oldest full block by sequence number for refreshing.
+ * Only for yaffs2.
+ */
+static __u32 yaffs_FindRefreshBlock(yaffs_Device *dev)
+{
+       __u32 b ;
+
+       __u32 oldest = 0;
+       __u32 oldestSequence = 0;
+
+       yaffs_BlockInfo *bi;
+
+       /*
+        * If refresh period < 10 then refreshing is disabled.
+        */
+       if(dev->param.refreshPeriod < 10 ||
+               !dev->param.isYaffs2)
+               return oldest;
+
+        /*
+         * Fix broken values.
+         */
+        if(dev->refreshSkip > dev->param.refreshPeriod)
+                dev->refreshSkip = dev->param.refreshPeriod;
+
+       if(dev->refreshSkip > 0){
+               dev->refreshSkip--;
+               return oldest;
+       }
+
+       /*
+        * Refresh skip is now zero.
+        * We'll do a refresh this time around....
+        * Update the refresh skip and find the oldest block.
+        */
+       dev->refreshSkip = dev->param.refreshPeriod;
+       dev->refreshCount++;
+
+       for (b = dev->internalStartBlock; b <=dev->internalEndBlock; b++){
+
+               bi = yaffs_GetBlockInfo(dev, b);
+               
+
+               if (bi->blockState == YAFFS_BLOCK_STATE_FULL){
+
+                       if(oldest < 1 ||
+                                bi->sequenceNumber < oldestSequence){
+                                oldest = b;
+                                oldestSequence = bi->sequenceNumber;
+                        }
+               }
+       }
+
+       if (oldest > 0) {
+               T(YAFFS_TRACE_GC,
+                 (TSTR("GC refresh count %d selected block %d with sequenceNumber %d" TENDSTR),
+                  dev->refreshCount, oldest, oldestSequence));
+       }
+
+       return oldest;
+}
+
+/*
+ * FindDiretiestBlock is used to select the dirtiest block (or close enough)
  * for garbage collection.
  */
 
@@ -2800,8 +2911,6 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device *dev,
                   dev->param.nChunksPerBlock - pagesInUse, prioritised));
        }
 
-       dev->oldestDirtySequence = 0;
-
        if (dirtiest > 0)
                dev->nonAggressiveSkip = 4;
 
@@ -2822,6 +2931,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) {
@@ -2851,6 +2962,7 @@ static void yaffs_BlockBecameDirty(yaffs_Device *dev, int blockNo)
        if (erasedOk) {
                /* Clean it up... */
                bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
+               bi->sequenceNumber = 0;
                dev->nErasedBlocks++;
                bi->pagesInUse = 0;
                bi->softDeletions = 0;
@@ -3226,13 +3338,16 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block,
                                                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;
 
                                                yaffs_VerifyObjectHeader(object, oh, &tags, 1);
-                                       }
-
-                                       newChunk =
-                                           yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &tags, 1);
+                                               newChunk =
+                                                   yaffs_WriteNewChunkWithTagsToNAND(dev,(__u8 *) oh, &tags, 1);
+                                       } else
+                                               newChunk =
+                                                   yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &tags, 1);
 
                                        if (newChunk < 0) {
                                                retVal = YAFFS_FAIL;
@@ -3289,16 +3404,17 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block,
 
        yaffs_VerifyCollectedBlock(dev, bi, block);
 
-       chunksAfter = yaffs_GetErasedChunks(dev);
-       if (chunksBefore >= chunksAfter) {
-               T(YAFFS_TRACE_GC,
-                 (TSTR
-                  ("gc did not increase free chunks before %d after %d"
-                   TENDSTR), chunksBefore, chunksAfter));
-       }
+
 
        /* If the gc completed then clear the current gcBlock so that we find another. */
        if (bi->blockState != YAFFS_BLOCK_STATE_COLLECTING) {
+               chunksAfter = yaffs_GetErasedChunks(dev);
+               if (chunksBefore >= chunksAfter) {
+                       T(YAFFS_TRACE_GC,
+                         (TSTR
+                          ("gc did not increase free chunks before %d after %d"
+                           TENDSTR), chunksBefore, chunksAfter));
+               }
                dev->gcBlock = -1;
                dev->gcChunk = 0;
        }
@@ -3342,15 +3458,19 @@ static int yaffs_CheckGarbageCollection(yaffs_Device *dev)
                if (checkpointBlockAdjust < 0)
                        checkpointBlockAdjust = 0;
 
-               if (dev->nErasedBlocks < (dev->param.nReservedBlocks + checkpointBlockAdjust + 2)) {
-                       /* We need a block soon...*/
+               /* If we need a block soon then do aggressive gc.*/
+               if (dev->nErasedBlocks < (dev->param.nReservedBlocks + checkpointBlockAdjust + 2))
                        aggressive = 1;
-               } else {
-                       /* We're in no hurry */
+               else
                        aggressive = 0;
-               }
 
-               if (dev->gcBlock <= 0) {
+                /* If we don't already have a block being gc'd then see if we should start another */
+
+               if (dev->gcBlock < 1 && !aggressive) {
+                       dev->gcBlock = yaffs_FindRefreshBlock(dev);
+                       dev->gcChunk = 0;
+               }
+               if (dev->gcBlock < 1) {
                        dev->gcBlock = yaffs_FindBlockForGarbageCollection(dev, aggressive);
                        dev->gcChunk = 0;
                }
@@ -3685,6 +3805,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));
@@ -3750,9 +3872,9 @@ static int yaffs_WriteChunkDataToObject(yaffs_Object *in, int chunkInInode,
         * the tnode now, rather than later when it is harder to clean up.
         */
        prevChunkId = yaffs_FindChunkInFile(in, chunkInInode, &prevTags);
-       if(prevChunkId <= 0 &&
-               !yaffs_PutChunkIntoFile(in, chunkInInode, 0, 0)){
-       }
+       if(prevChunkId < 1 &&
+               !yaffs_PutChunkIntoFile(in, chunkInInode, 0, 0))
+               return 0;
 
        /* Set up new tags */
        yaffs_InitialiseTags(&newTags);
@@ -3769,13 +3891,6 @@ static int yaffs_WriteChunkDataToObject(yaffs_Object *in, int chunkInInode,
                YBUG();
        }
        
-       /*
-        * If there isn't already a chunk there then do a dummy
-        * insert to make sue we have the desired tnode structure.
-        */
-       if(prevChunkId < 1 &&
-               yaffs_PutChunkIntoFile(in, chunkInInode, 0, 0) != YAFFS_OK)
-               return -1;
                
        newChunkId =
            yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags,
@@ -4266,7 +4381,6 @@ static void yaffs_DeviceToCheckpointDevice(yaffs_CheckpointDevice *cp,
        cp->nUnlinkedFiles = dev->nUnlinkedFiles;
        cp->nBackgroundDeletions = dev->nBackgroundDeletions;
        cp->sequenceNumber = dev->sequenceNumber;
-       cp->oldestDirtySequence = dev->oldestDirtySequence;
 
 }
 
@@ -4282,7 +4396,6 @@ static void yaffs_CheckpointDeviceToDevice(yaffs_Device *dev,
        dev->nUnlinkedFiles = cp->nUnlinkedFiles;
        dev->nBackgroundDeletions = cp->nBackgroundDeletions;
        dev->sequenceNumber = cp->sequenceNumber;
-       dev->oldestDirtySequence = cp->oldestDirtySequence;
 }
 
 
@@ -4738,7 +4851,7 @@ static void yaffs_InvalidateCheckpoint(yaffs_Device *dev)
                dev->isCheckpointed = 0;
                yaffs_CheckpointInvalidateStream(dev);
                if (dev->param.markSuperBlockDirty)
-                       dev->param.markSuperBlockDirty(dev->context);
+                       dev->param.markSuperBlockDirty(dev);
        }
 }
 
@@ -5158,7 +5271,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
@@ -6472,35 +6584,26 @@ static int yaffs_ScanBackwards(yaffs_Device *dev)
 
                                if (in &&
                                    in->variantType == YAFFS_OBJECT_TYPE_FILE
-                                   && chunkBase <
-                                   in->variant.fileVariant.shrinkSize) {
+                                   && chunkBase < in->variant.fileVariant.shrinkSize) {
                                        /* This has not been invalidated by a resize */
-                                       if (!yaffs_PutChunkIntoFile(in, tags.chunkId,
-                                                              chunk, -1)) {
+                                       if (!yaffs_PutChunkIntoFile(in, tags.chunkId, chunk, -1)) {
                                                alloc_failed = 1;
                                        }
 
                                        /* File size is calculated by looking at the data chunks if we have not
                                         * seen an object header yet. Stop this practice once we find an object header.
                                         */
-                                       endpos =
-                                           (tags.chunkId -
-                                            1) * dev->nDataBytesPerChunk +
-                                           tags.byteCount;
+                                       endpos = chunkBase + tags.byteCount;
 
                                        if (!in->valid &&       /* have not got an object header yet */
-                                           in->variant.fileVariant.
-                                           scannedFileSize < endpos) {
-                                               in->variant.fileVariant.
-                                                   scannedFileSize = endpos;
-                                               in->variant.fileVariant.
-                                                   fileSize =
-                                                   in->variant.fileVariant.
-                                                   scannedFileSize;
+                                           in->variant.fileVariant.scannedFileSize < endpos) {
+                                               in->variant.fileVariant.scannedFileSize = endpos;
+                                               in->variant.fileVariant.fileSize = endpos;
                                        }
 
                                } else if (in) {
-                                       /* This chunk has been invalidated by a resize, so delete */
+                                       /* This chunk has been invalidated by a resize, or a past file deletion
+                                        * so delete the chunk*/
                                        yaffs_DeleteChunk(dev, chunk, 1, __LINE__);
 
                                }
@@ -6517,9 +6620,9 @@ static int yaffs_ScanBackwards(yaffs_Device *dev)
                                in = NULL;
 
                                if (tags.extraHeaderInfoAvailable) {
-                                       in = yaffs_FindOrCreateObjectByNumber
-                                           (dev, tags.objectId,
-                                            tags.extraObjectType);
+                                       in = yaffs_FindOrCreateObjectByNumber(dev,
+                                               tags.objectId,
+                                               tags.extraObjectType);
                                        if (!in)
                                                alloc_failed = 1;
                                }
@@ -6601,13 +6704,8 @@ static int yaffs_ScanBackwards(yaffs_Device *dev)
                                                        isShrink = 1;
                                                }
 
-                                               if (isShrink &&
-                                                   in->variant.fileVariant.
-                                                   shrinkSize > thisSize) {
-                                                       in->variant.fileVariant.
-                                                           shrinkSize =
-                                                           thisSize;
-                                               }
+                                               if (isShrink && in->variant.fileVariant.shrinkSize > thisSize)
+                                                       in->variant.fileVariant.shrinkSize = thisSize;
 
                                                if (isShrink)
                                                        bi->hasShrinkHeader = 1;
@@ -6780,14 +6878,12 @@ static int yaffs_ScanBackwards(yaffs_Device *dev)
                                                         * than its current data extents.
                                                         */
                                                        in->variant.fileVariant.fileSize = fileSize;
-                                                       in->variant.fileVariant.scannedFileSize =
-                                                           in->variant.fileVariant.fileSize;
+                                                       in->variant.fileVariant.scannedFileSize = fileSize;
                                                }
 
-                                               if (isShrink &&
-                                                   in->variant.fileVariant.shrinkSize > fileSize) {
+                                               if (in->variant.fileVariant.shrinkSize > fileSize)
                                                        in->variant.fileVariant.shrinkSize = fileSize;
-                                               }
+                               
 
                                                break;
                                        case YAFFS_OBJECT_TYPE_HARDLINK:
@@ -7570,6 +7666,7 @@ 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 */
+       dev->oldestDirtySequence = 0;
 
        /* Initialise temporary buffers and caches. */
        if (!yaffs_InitialiseTempBuffers(dev))
@@ -7652,7 +7749,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;