Change checkpoint to calculate number of blocks required instead of using a hardcoded...
[yaffs2.git] / yaffs_guts.c
index c81b1e7e35ba5aeb671da38397a10965606f0183..cad48e69c5295409442d5b2f9869f59a18f7bc32 100644 (file)
@@ -12,7 +12,7 @@
  */
 
 const char *yaffs_guts_c_version =
-    "$Id: yaffs_guts.c,v 1.49 2007-05-15 20:07:40 charles Exp $";
+    "$Id: yaffs_guts.c,v 1.53 2007-12-03 03:21:48 charles Exp $";
 
 #include "yportenv.h"
 
@@ -920,86 +920,88 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev,
                                             yaffs_ExtendedTags * tags,
                                             int useReserve)
 {
+       int attempts = 0;
+       int writeOk = 0;
        int chunk;
 
-       int writeOk = 0;
-       int erasedOk = 1;
-       int attempts = 0;
-       yaffs_BlockInfo *bi;
-       
        yaffs_InvalidateCheckpoint(dev);
 
        do {
-               chunk = yaffs_AllocateChunk(dev, useReserve,&bi);
-
-               if (chunk >= 0) {
-                       /* First check this chunk is erased, if it needs checking.
-                        * The checking policy (unless forced always on) is as follows:
-                        * Check the first page we try to write in a block.
-                        * - If the check passes then we don't need to check any more.
-                        * - If the check fails, we check again...
-                        * If the block has been erased, we don't need to check.
-                        *
-                        * However, if the block has been prioritised for gc, then
-                        * we think there might be something odd about this block
-                        * and stop using it.
-                        *
-                        * Rationale:
-                        * We should only ever see chunks that have not been erased
-                        * if there was a partially written chunk due to power loss
-                        * This checking policy should catch that case with very
-                        * few checks and thus save a lot of checks that are most likely not
-                        * needed.
-                        */
-                        
-                        if(bi->gcPrioritise){
-                                       yaffs_DeleteChunk(dev, chunk, 1, __LINE__);
-                       } else {
-#ifdef CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED
+               yaffs_BlockInfo *bi = 0;
+               int erasedOk = 0;
 
-                               bi->skipErasedCheck = 0;
+               chunk = yaffs_AllocateChunk(dev, useReserve, &bi);
+               if (chunk < 0) {
+                       /* no space */
+                       break;
+               }
 
-#endif
-                               if(!bi->skipErasedCheck){
-                                       erasedOk = yaffs_CheckChunkErased(dev, chunk);
-                                       if(erasedOk && !bi->gcPrioritise)
-                                               bi->skipErasedCheck = 1;
-                               }
+               /* First check this chunk is erased, if it needs
+                * checking.  The checking policy (unless forced
+                * always on) is as follows:
+                *
+                * Check the first page we try to write in a block.
+                * If the check passes then we don't need to check any
+                * more.        If the check fails, we check again...
+                * If the block has been erased, we don't need to check.
+                *
+                * However, if the block has been prioritised for gc,
+                * then we think there might be something odd about
+                * this block and stop using it.
+                *
+                * Rationale: We should only ever see chunks that have
+                * not been erased if there was a partially written
+                * chunk due to power loss.  This checking policy should
+                * catch that case with very few checks and thus save a
+                * lot of checks that are most likely not needed.
+                */
+               if (bi->gcPrioritise) {
+                       yaffs_DeleteChunk(dev, chunk, 1, __LINE__);
+                       /* try another chunk */
+                       continue;
+               }
 
-                               if (!erasedOk) {
-                                       T(YAFFS_TRACE_ERROR,
-                                         (TSTR
-                                          ("**>> yaffs chunk %d was not erased"
-                                           TENDSTR), chunk));
-                               } else {
-                                       writeOk =
-                                           yaffs_WriteChunkWithTagsToNAND(dev, chunk,
-                                                                          data, tags);
-                               }
-                       
-                               attempts++;
+               /* let's give it a try */
+               attempts++;
 
-                               if (writeOk) {
-                                       /*
-                                        *  Copy the data into the robustification buffer.
-                                        *  NB We do this at the end to prevent duplicates in the case of a write error.
-                                        *  Todo
-                                        */
-                                       yaffs_HandleWriteChunkOk(dev, chunk, data, tags);
-                               
-                               } else {
-                                       /* The erased check or write failed */
-                                       yaffs_HandleWriteChunkError(dev, chunk, erasedOk);
-                               }
+#ifdef CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED
+               bi->skipErasedCheck = 0;
+#endif
+               if (!bi->skipErasedCheck) {
+                       erasedOk = yaffs_CheckChunkErased(dev, chunk);
+                       if (erasedOk != YAFFS_OK) {
+                               T(YAFFS_TRACE_ERROR,
+                               (TSTR ("**>> yaffs chunk %d was not erased"
+                               TENDSTR), chunk));
+
+                               /* try another chunk */
+                               continue;
                        }
+                       bi->skipErasedCheck = 1;
                }
 
-       } while (chunk >= 0 && !writeOk);
+               writeOk = yaffs_WriteChunkWithTagsToNAND(dev, chunk,
+                               data, tags);
+               if (writeOk != YAFFS_OK) {
+                       yaffs_HandleWriteChunkError(dev, chunk, erasedOk);
+                       /* try another chunk */
+                       continue;
+               }
+
+               /* Copy the data into the robustification buffer */
+               yaffs_HandleWriteChunkOk(dev, chunk, data, tags);
+
+       } while (writeOk != YAFFS_OK && 
+               (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts));
+       
+       if(!writeOk)
+               chunk = -1;
 
        if (attempts > 1) {
                T(YAFFS_TRACE_ERROR,
-                 (TSTR("**>> yaffs write required %d attempts" TENDSTR),
-                  attempts));
+                       (TSTR("**>> yaffs write required %d attempts" TENDSTR),
+                       attempts));
+
                dev->nRetriedWrites += (attempts - 1);
        }
 
@@ -1144,6 +1146,10 @@ static int yaffs_CreateTnodes(yaffs_Device * dev, int nTnodes)
         * Must be a multiple of 32-bits  */
        tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8;
 
+       if(tnodeSize < sizeof(yaffs_Tnode))
+               tnodeSize = sizeof(yaffs_Tnode);
+               
+
        /* make these things */
 
        newTnodes = YMALLOC(nTnodes * tnodeSize);
@@ -1234,15 +1240,21 @@ static yaffs_Tnode *yaffs_GetTnodeRaw(yaffs_Device * dev)
                dev->nFreeTnodes--;
        }
 
+       dev->nCheckpointBlocksRequired = 0; /* force recalculation*/
+
        return tn;
 }
 
 static yaffs_Tnode *yaffs_GetTnode(yaffs_Device * dev)
 {
        yaffs_Tnode *tn = yaffs_GetTnodeRaw(dev);
+       int tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8;
+
+       if(tnodeSize < sizeof(yaffs_Tnode))
+               tnodeSize = sizeof(yaffs_Tnode);
        
        if(tn)
-               memset(tn, 0, (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8);
+               memset(tn, 0, tnodeSize);
 
        return tn;      
 }
@@ -1263,6 +1275,8 @@ static void yaffs_FreeTnode(yaffs_Device * dev, yaffs_Tnode * tn)
                dev->freeTnodes = tn;
                dev->nFreeTnodes++;
        }
+       dev->nCheckpointBlocksRequired = 0; /* force recalculation*/
+       
 }
 
 static void yaffs_DeinitialiseTnodes(yaffs_Device * dev)
@@ -1907,6 +1921,8 @@ static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device * dev)
                        yaffs_AddObjectToDirectory(dev->lostNFoundDir, tn);
                }
        }
+       
+       dev->nCheckpointBlocksRequired = 0; /* force recalculation*/
 
        return tn;
 }
@@ -1968,6 +1984,9 @@ static void yaffs_FreeObject(yaffs_Object * tn)
        tn->siblings.next = (struct list_head *)(dev->freeObjects);
        dev->freeObjects = tn;
        dev->nFreeObjects++;
+
+       dev->nCheckpointBlocksRequired = 0; /* force recalculation*/
+
 }
 
 #ifdef __KERNEL__
@@ -2591,7 +2610,6 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev,
        int pagesInUse = 0;
        int prioritised=0;
        yaffs_BlockInfo *bi;
-       static int nonAggressiveSkip = 0;
        int pendingPrioritisedExist = 0;
        
        /* First let's see if we need to grab a prioritised block */
@@ -2623,9 +2641,9 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev,
         * block has only a few pages in use.
         */
 
-       nonAggressiveSkip--;
+       dev->nonAggressiveSkip--;
 
-       if (!aggressive && (nonAggressiveSkip > 0)) {
+       if (!aggressive && (dev->nonAggressiveSkip > 0)) {
                return -1;
        }
 
@@ -2686,7 +2704,7 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev,
        dev->oldestDirtySequence = 0;
 
        if (dirtiest > 0) {
-               nonAggressiveSkip = 4;
+               dev->nonAggressiveSkip = 4;
        }
 
        return dirtiest;
@@ -2803,6 +2821,40 @@ static int yaffs_FindBlockForAllocation(yaffs_Device * dev)
 }
 
 
+
+static int yaffs_CalcCheckpointBlocksRequired(yaffs_Device *dev)
+{
+       if(!dev->nCheckpointBlocksRequired){
+               /* Not a valid value so recalculate */
+               int nBytes = 0;
+               int nBlocks;
+               int devBlocks = (dev->endBlock - dev->startBlock + 1);
+               int tnodeSize;
+
+               tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8;
+
+               if(tnodeSize < sizeof(yaffs_Tnode))
+                       tnodeSize = sizeof(yaffs_Tnode);
+               
+               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_CheckpointValidity);
+               nBytes += sizeof(__u32); /* checksum*/
+       
+               /* Round up and add 2 blocks to allow for some bad blocks, so add 3 */
+       
+               nBlocks = (nBytes/(dev->nDataBytesPerChunk * dev->nChunksPerBlock)) + 3;
+       
+               dev->nCheckpointBlocksRequired = nBlocks;
+       }
+
+       return dev->nCheckpointBlocksRequired;
+}
+
 // 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)
@@ -2811,7 +2863,7 @@ static int yaffs_CheckSpaceForAllocation(yaffs_Device * dev)
        int reservedBlocks = dev->nReservedBlocks;
        int checkpointBlocks;
        
-       checkpointBlocks =  dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint;
+       checkpointBlocks =  yaffs_CalcCheckpointBlocksRequired(dev) - dev->blocksInCheckpoint;
        if(checkpointBlocks < 0)
                checkpointBlocks = 0;
        
@@ -3144,7 +3196,7 @@ static int yaffs_CheckGarbageCollection(yaffs_Device * dev)
        do {
                maxTries++;
                
-               checkpointBlockAdjust = (dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint);
+               checkpointBlockAdjust = yaffs_CalcCheckpointBlocksRequired(dev) - dev->blocksInCheckpoint;
                if(checkpointBlockAdjust < 0)
                        checkpointBlockAdjust = 0;
 
@@ -4222,7 +4274,11 @@ static int yaffs_CheckpointTnodeWorker(yaffs_Object * in, yaffs_Tnode * tn,
        int i;
        yaffs_Device *dev = in->myDev;
        int ok = 1;
-       int nTnodeBytes = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8;
+       int tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8;
+
+       if(tnodeSize < sizeof(yaffs_Tnode))
+               tnodeSize = sizeof(yaffs_Tnode);
+       
 
        if (tn) {
                if (level > 0) {
@@ -4240,7 +4296,7 @@ static int yaffs_CheckpointTnodeWorker(yaffs_Object * in, yaffs_Tnode * tn,
                        /* printf("write tnode at %d\n",baseOffset); */
                        ok = (yaffs_CheckpointWrite(dev,&baseOffset,sizeof(baseOffset)) == sizeof(baseOffset));
                        if(ok)
-                               ok = (yaffs_CheckpointWrite(dev,tn,nTnodeBytes) == nTnodeBytes);
+                               ok = (yaffs_CheckpointWrite(dev,tn,tnodeSize) == tnodeSize);
                }
        }
 
@@ -4274,7 +4330,11 @@ static int yaffs_ReadCheckpointTnodes(yaffs_Object *obj)
        yaffs_FileStructure *fileStructPtr = &obj->variant.fileVariant;
        yaffs_Tnode *tn;
        int nread = 0;
-       
+       int tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8;
+
+       if(tnodeSize < sizeof(yaffs_Tnode))
+               tnodeSize = sizeof(yaffs_Tnode);
+
        ok = (yaffs_CheckpointRead(dev,&baseChunk,sizeof(baseChunk)) == sizeof(baseChunk));
        
        while(ok && (~baseChunk)){
@@ -4285,8 +4345,7 @@ static int yaffs_ReadCheckpointTnodes(yaffs_Object *obj)
                /* printf("read  tnode at %d\n",baseChunk); */
                tn = yaffs_GetTnodeRaw(dev);
                if(tn)
-                       ok = (yaffs_CheckpointRead(dev,tn,(dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8) ==
-                             (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8);
+                       ok = (yaffs_CheckpointRead(dev,tn,tnodeSize) == tnodeSize);
                else
                        ok = 0;
                        
@@ -5007,7 +5066,7 @@ int yaffs_ResizeFile(yaffs_Object * in, loff_t newSize)
                                         (newSize < oldFileSize) ? 1 : 0, 0);
        }
 
-       return newSize;
+       return YAFFS_OK;
 }
 
 loff_t yaffs_GetFileSize(yaffs_Object * obj)
@@ -6057,7 +6116,7 @@ static int yaffs_ScanBackwards(yaffs_Device * dev)
 #ifndef CONFIG_YAFFS_USE_OWN_SORT
        {
                /* Use qsort now. */
-               qsort(blockIndex, nBlocksToScan, sizeof(yaffs_BlockIndex), ybicmp);
+               yaffs_qsort(blockIndex, nBlocksToScan, sizeof(yaffs_BlockIndex), ybicmp);
        }
 #else
        {
@@ -7414,7 +7473,7 @@ int yaffs_GetNumberOfFreeChunks(yaffs_Device * dev)
        nFree -= ((dev->nReservedBlocks + 1) * dev->nChunksPerBlock);
        
        /* Now we figure out how much to reserve for the checkpoint and report that... */
-       blocksForCheckpoint = dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint;
+       blocksForCheckpoint = yaffs_CalcCheckpointBlocksRequired(dev) - dev->blocksInCheckpoint;
        if(blocksForCheckpoint < 0)
                blocksForCheckpoint = 0;