*** empty log message ***
[yaffs/.git] / yaffs_guts.c
index bb714e01bcc79b6bc5df98f490545b8556a7fbcb..47059771ef3727dd21f413947425f20a1a85ec63 100644 (file)
@@ -14,7 +14,7 @@
  */
  //yaffs_guts.c
 
-const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.16 2003-01-14 23:15:29 charles Exp $";
+const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.17 2003-01-17 04:19:08 charles Exp $";
 
 #include "yportenv.h"
 
@@ -22,9 +22,9 @@ const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.16 2003-01-14 23:15:29 c
 #include "yaffs_guts.h"
 
 
-#define YAFFS_GARBAGE_COLLECT_LOW_WATER 2
 
 
+#define YAFFS_PASSIVE_GC_CHUNKS 2
 
 // External functions for ECC on data
 void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);
@@ -195,7 +195,7 @@ yaffs_Object *yaffs_LostNFound(yaffs_Device *dev)
 
 static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare)
 {
-       if(chunkInNAND < dev->startBlock * YAFFS_CHUNKS_PER_BLOCK)
+       if(chunkInNAND < dev->startBlock * dev->nChunksPerBlock)
        {
                T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d is not valid" TENDSTR),chunkInNAND));
                return YAFFS_FAIL;
@@ -438,8 +438,9 @@ static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND)
 
        spare.blockStatus = 0;
        
-       yaffs_WriteChunkToNAND(dev, blockInNAND * YAFFS_CHUNKS_PER_BLOCK, NULL , &spare);
-       yaffs_WriteChunkToNAND(dev, blockInNAND * YAFFS_CHUNKS_PER_BLOCK + 1, NULL , &spare);
+       // TODO change this retirement marking for other NAND types
+       yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock, NULL , &spare);
+       yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock + 1, NULL , &spare);
        
        yaffs_GetBlockInfo(dev,blockInNAND)->blockState = YAFFS_BLOCK_STATE_DEAD;
        dev->nRetiredBlocks++;
@@ -463,7 +464,7 @@ static int yaffs_RewriteBufferedBlock(yaffs_Device *dev)
 
 static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND)
 {
-       int blockInNAND = chunkInNAND/YAFFS_CHUNKS_PER_BLOCK;
+       int blockInNAND = chunkInNAND/dev->nChunksPerBlock;
 
        // Mark the block for retirement
        yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1;
@@ -490,7 +491,7 @@ static void yaffs_HandleUpdateChunk(yaffs_Device *dev,int chunkInNAND, const yaf
 
 static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND)
 {
-       int blockInNAND = chunkInNAND/YAFFS_CHUNKS_PER_BLOCK;
+       int blockInNAND = chunkInNAND/dev->nChunksPerBlock;
 
        // Mark the block for retirement
        yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1;
@@ -1150,7 +1151,7 @@ static int yaffs_SoftDeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level
                        {
                                        
                                        theChunk =  (tn->level0[i] << in->myDev->chunkGroupBits);
-                                       theBlock =      yaffs_GetBlockInfo(in->myDev,  theChunk/ YAFFS_CHUNKS_PER_BLOCK);
+                                       theBlock =      yaffs_GetBlockInfo(in->myDev,  theChunk/in->myDev->nChunksPerBlock);
                                        if(theBlock)
                                        {
                                                theBlock->softDeletions++;
@@ -1541,7 +1542,7 @@ static int yaffs_CreateNewObjectNumber(yaffs_Device *dev)
                        list_for_each(i,&dev->objectBucket[bucket].list)
                        {
                                // If there is already one in the list
-                               if(list_entry(i, yaffs_Object,hashLink)->objectId == n)
+                               if(i && list_entry(i, yaffs_Object,hashLink)->objectId == n)
                                {
                                        found = 0;
                                }
@@ -1579,10 +1580,13 @@ yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,__u32 number)
        list_for_each(i,&dev->objectBucket[bucket].list)
        {
                // Look if it is in the list
-               in = list_entry(i, yaffs_Object,hashLink);
-               if(in->objectId == number)
+               if(i)
                {
-                       return in;
+                       in = list_entry(i, yaffs_Object,hashLink);
+                       if(in->objectId == number)
+                       {
+                               return in;
+                       }
                }
        }
        
@@ -1987,17 +1991,35 @@ static void yaffs_DeinitialiseBlocks(yaffs_Device *dev)
 // FindDiretiestBlock is used to select the dirtiest block (or close enough)
 // for garbage collection.
 
-static int yaffs_FindDirtiestBlock(yaffs_Device *dev)
+static int yaffs_FindDirtiestBlock(yaffs_Device *dev,int aggressive)
 {
 
        int b = dev->currentDirtyChecker;
        
        int i;
+       int iterations;
        int dirtiest = -1;
-       int pagesInUse = dev->nChunksPerBlock
+       int pagesInUse; 
        yaffs_BlockInfo *bi;
+
+       // If we're doing aggressive GC then we are happy to take a less-dirty block, and
+       // search further.
+       
+       pagesInUse = (aggressive)? dev->nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1;
+       if(aggressive)
+       {
+               iterations = dev->endBlock - dev->startBlock + 1;
+       }
+       else
+       {
+               iterations = iterations / 16;
+               if(iterations > 200)
+               {
+                       iterations = 200;
+               }
+       }
        
-       for(i = dev->startBlock; i <= dev->endBlock && pagesInUse > 2 ; i++)
+       for(i = 0; i <= iterations && pagesInUse > 0 ; i++)
        {
                b++;
                if ( b < dev->startBlock || b > dev->endBlock)
@@ -2128,7 +2150,7 @@ static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve)
        {
                bi = yaffs_GetBlockInfo(dev,dev->allocationBlock);
                
-               retVal = (dev->allocationBlock * YAFFS_CHUNKS_PER_BLOCK) + 
+               retVal = (dev->allocationBlock * dev->nChunksPerBlock) + 
                                  dev->allocationPage;
                bi->pagesInUse++;
                yaffs_SetChunkBit(dev,dev->allocationBlock,dev->allocationPage);
@@ -2138,7 +2160,7 @@ static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve)
                dev->nFreeChunks--;
                
                // If the block is full set the state to full
-               if(dev->allocationPage >= YAFFS_CHUNKS_PER_BLOCK)
+               if(dev->allocationPage >= dev->nChunksPerBlock)
                {
                        bi->blockState = YAFFS_BLOCK_STATE_FULL;
                        dev->allocationBlock = -1;
@@ -2157,13 +2179,13 @@ static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve)
 // number of erased blocks.
 // The cache is allowed to use reserved blocks.
 
-int yaffs_CheckSpaceForChunkCache(yaffs_Device *dev)
+static int yaffs_CheckSpaceForChunkCache(yaffs_Device *dev)
 {
        return (dev->nErasedBlocks >= YAFFS_RESERVED_BLOCKS);
 }
 
 
-int  yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
+static int  yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
 {
        int oldChunk;
        int newChunk;
@@ -2173,7 +2195,7 @@ int  yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
        
        yaffs_Spare spare;
        yaffs_Tags  tags;
-               __u8  buffer[YAFFS_BYTES_PER_CHUNK];
+       __u8  buffer[YAFFS_BYTES_PER_CHUNK];
        
 //     yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,block);
        
@@ -2181,7 +2203,7 @@ int  yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
 
        //T(("Collecting block %d n %d bits %x\n",block, bi->pagesInUse, bi->pageBits));        
        
-       for(chunkInBlock = 0,oldChunk = block * YAFFS_CHUNKS_PER_BLOCK
+       for(chunkInBlock = 0,oldChunk = block * dev->nChunksPerBlock
            chunkInBlock < dev->nChunksPerBlock && yaffs_StillSomeChunkBits(dev,block);
            chunkInBlock++, oldChunk++ )
        {
@@ -2281,10 +2303,13 @@ static yaffs_Object *yaffs_FindDeletedUnlinkedFile(yaffs_Device *dev)
        //Scan the unlinked files looking for one to delete
        list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)
        {
-               l = list_entry(i, yaffs_Object,siblings);
-               if(l->deleted)
+               if(i)
                {
-                       return l;                       
+                       l = list_entry(i, yaffs_Object,siblings);
+                       if(l->deleted)
+                       {
+                               return l;                       
+                       }
                }
        }       
        return NULL;
@@ -2328,33 +2353,27 @@ static void yaffs_DoUnlinkedFileDeletion(yaffs_Device *dev)
 }
 
 
-
+#if 0
+#define YAFFS_GARBAGE_COLLECT_LOW_WATER 2
 static int yaffs_CheckGarbageCollection(yaffs_Device *dev)
 {
        int block;
+       int aggressive=0;
        
        //yaffs_DoUnlinkedFileDeletion(dev);
        
        if(dev->nErasedBlocks <= (YAFFS_RESERVED_BLOCKS + YAFFS_GARBAGE_COLLECT_LOW_WATER))
        {
-               dev->garbageCollectionRequired = 1;
-       }       
+               aggressive = 1;
+       }               
        
-       if(dev->garbageCollectionRequired)
+       if(aggressive)
        {
-               dev->garbageCollections++;
-               dev->garbageCollectionRequired = 0;
-               if(dev->blockSelectedForGC >= 0)
-               {
-                       block = dev->blockSelectedForGC;
-               }
-               else
-               {
-                       block = yaffs_FindDirtiestBlock(dev);
-               }
+               block = yaffs_FindDirtiestBlock(dev,aggressive);
                
                if(block >= 0)
                {
+                       dev->garbageCollections++;
                        return yaffs_GarbageCollectBlock(dev,block);
                }       
                else
@@ -2365,6 +2384,46 @@ static int yaffs_CheckGarbageCollection(yaffs_Device *dev)
 
        return YAFFS_OK;
 }
+#endif
+
+// New garbage collector
+// If we're very low on erased blocks then we do aggressive garbage collection
+// otherwise we do "passive" garbage collection.
+// Aggressive gc looks further (whole array) and will accept dirtier blocks.
+// Passive gc only inspects smaller areas and will only accept cleaner blocks.
+//
+// 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)
+{
+       int block;
+       int aggressive=0;
+       
+       //yaffs_DoUnlinkedFileDeletion(dev);
+       
+       if(dev->nErasedBlocks <= (YAFFS_RESERVED_BLOCKS + 1))
+       {
+               aggressive = 1;
+       }               
+       
+       block = yaffs_FindDirtiestBlock(dev,aggressive);
+       
+       if(block >= 0)
+       {
+               dev->garbageCollections++;
+               if(!aggressive)
+               {
+                       dev->passiveGarbageCollections++;
+               }
+
+               T(YAFFS_TRACE_GC,(TSTR("yaffs: GC erasedBlocks %d aggressive %d" TENDSTR),dev->nErasedBlocks,aggressive));
+
+               return yaffs_GarbageCollectBlock(dev,block);
+       }       
+
+       return aggressive ? YAFFS_FAIL : YAFFS_OK;
+}
 
 
 //////////////////////////// TAGS ///////////////////////////////////////
@@ -2623,7 +2682,7 @@ static int yaffs_CheckFileSanity(yaffs_Object *in)
        
        objId = in->objectId;
        fSize  = in->variant.fileVariant.fileSize;
-       nChunks = (fSize + YAFFS_BYTES_PER_CHUNK -1)/YAFFS_BYTES_PER_CHUNK;
+       nChunks = (fSize + in->myDev->nBytesPerChunk -1)/in->myDev->nBytesPerChunk;
        
        for(chunk = 1; chunk <= nChunks; chunk++)
        {
@@ -2774,8 +2833,8 @@ static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId,int markNAND)
        if(chunkId <= 0) return;        
        
        dev->nDeletions++;
-       block = chunkId / YAFFS_CHUNKS_PER_BLOCK;
-       page = chunkId % YAFFS_CHUNKS_PER_BLOCK;
+       block = chunkId / dev->nChunksPerBlock;
+       page = chunkId % dev->nChunksPerBlock;
        
        if(markNAND)
        {
@@ -3605,7 +3664,7 @@ int yaffs_FlushFile(yaffs_Object *in, int updateTime)
 #endif
                }
 
-               retVal = yaffs_UpdateObjectHeader(in,NULL,0);
+               retVal = (yaffs_UpdateObjectHeader(in,NULL,0) >= 0)? YAFFS_OK : YAFFS_FAIL;
        }
        else
        {
@@ -3862,7 +3921,7 @@ static int yaffs_IsBlockBad(yaffs_Device *dev, int blk)
 {
        yaffs_Spare spare;
        
-       yaffs_ReadChunkFromNAND(dev,blk * YAFFS_CHUNKS_PER_BLOCK,NULL,&spare,1);
+       yaffs_ReadChunkFromNAND(dev,blk * dev->nChunksPerBlock,NULL,&spare,1);
 #if 1
        if(yaffs_CountBits(spare.blockStatus) < 7)
        {
@@ -3874,7 +3933,7 @@ static int yaffs_IsBlockBad(yaffs_Device *dev, int blk)
                return 1;
        }
 #endif
-       yaffs_ReadChunkFromNAND(dev,blk * YAFFS_CHUNKS_PER_BLOCK + 1,NULL,&spare,1);
+       yaffs_ReadChunkFromNAND(dev,blk * dev->nChunksPerBlock + 1,NULL,&spare,1);
 
 #if 1
        if(yaffs_CountBits(spare.blockStatus) < 7)
@@ -3933,11 +3992,11 @@ static int yaffs_Scan(yaffs_Device *dev)
                
                // Read each chunk in the block.
                
-               for(c = 0; c < YAFFS_CHUNKS_PER_BLOCK && 
+               for(c = 0; c < dev->nChunksPerBlock && 
                                   state == YAFFS_BLOCK_STATE_SCANNING; c++)
                {
                        // Read the spare area and decide what to do
-                       chunk = blk * YAFFS_CHUNKS_PER_BLOCK + c;
+                       chunk = blk * dev->nChunksPerBlock + c;
                        
                        yaffs_ReadChunkFromNAND(dev,chunk,NULL,&spare,1);
 
@@ -3973,7 +4032,7 @@ static int yaffs_Scan(yaffs_Device *dev)
                                        dev->allocationPage = c;
                                }
 
-                               dev->nFreeChunks += (YAFFS_CHUNKS_PER_BLOCK - c);
+                               dev->nFreeChunks += (dev->nChunksPerBlock - c);
                        }
                        else if(tags.chunkId > 0)
                        {
@@ -4191,14 +4250,19 @@ static int yaffs_Scan(yaffs_Device *dev)
        
        {
                struct list_head *i;    
+               struct list_head *n;
+                       
                yaffs_Object *l;
                // Soft delete all the unlinked files
-               list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)
+               list_for_each_safe(i,n,&dev->unlinkedDir->variant.directoryVariant.children)
                {
-                       l = list_entry(i, yaffs_Object,siblings);
-                       if(l->deleted)
+                       if(i)
                        {
-                               yaffs_SoftDeleteFile(l);                
+                               l = list_entry(i, yaffs_Object,siblings);
+                               if(l->deleted)
+                               {
+                                       yaffs_SoftDeleteFile(l);                
+                               }
                        }
                }       
        }
@@ -4256,26 +4320,28 @@ yaffs_Object *yaffs_FindObjectByName(yaffs_Object *directory,const char *name)
        
        list_for_each(i,&directory->variant.directoryVariant.children)
        {
-               l = list_entry(i, yaffs_Object,siblings);
-               
-               // Special case for lost-n-found
-               if(l->objectId == YAFFS_OBJECTID_LOSTNFOUND)
+               if(i)
                {
-                       if(yaffs_strcmp(name,YAFFS_LOSTNFOUND_NAME) == 0)
+                       l = list_entry(i, yaffs_Object,siblings);
+               
+                       // Special case for lost-n-found
+                       if(l->objectId == YAFFS_OBJECTID_LOSTNFOUND)
                        {
-                               return l;
+                               if(yaffs_strcmp(name,YAFFS_LOSTNFOUND_NAME) == 0)
+                               {
+                                       return l;
+                               }
                        }
-               }
-               else if(yaffs_SumCompare(l->sum, sum))
-               {
-                       // Do a real check
-                       yaffs_GetObjectName(l,buffer,YAFFS_MAX_NAME_LENGTH);
-                       if(yaffs_strcmp(name,buffer) == 0)
+                       else if(yaffs_SumCompare(l->sum, sum))
                        {
-                               return l;
-                       }
-                       
+                               // Do a real check
+                               yaffs_GetObjectName(l,buffer,YAFFS_MAX_NAME_LENGTH);
+                               if(yaffs_strcmp(name,buffer) == 0)
+                               {
+                                       return l;
+                               }
                        
+                       }                       
                }
        }
        
@@ -4291,10 +4357,13 @@ int yaffs_ApplyToDirectoryChildren(yaffs_Object *theDir,int (*fn)(yaffs_Object *
        
        list_for_each(i,&theDir->variant.directoryVariant.children)
        {
-               l = list_entry(i, yaffs_Object,siblings);
-               if(!fn(l))
+               if(i)
                {
-                       return YAFFS_FAIL;
+                       l = list_entry(i, yaffs_Object,siblings);
+                       if(l && !fn(l))
+                       {
+                               return YAFFS_FAIL;
+                       }
                }
        }
        
@@ -4562,7 +4631,7 @@ int yaffs_GutsInitialise(yaffs_Device *dev)
        // Calculate chunkGroupBits. 
        // We need to find the next power of 2 > than endBlock
        
-       x = YAFFS_CHUNKS_PER_BLOCK * (dev->endBlock+1);
+       x = dev->nChunksPerBlock * (dev->endBlock+1);
        
        for(bits = extraBits = 0; x > 1; bits++)
        {
@@ -4592,12 +4661,11 @@ int yaffs_GutsInitialise(yaffs_Device *dev)
        
        
        // More device initialisation
-       dev->garbageCollectionRequired  = 0;
        dev->garbageCollections = 0;
+       dev->passiveGarbageCollections = 0;
        dev->currentDirtyChecker = 0;
        dev->bufferedBlock = -1;
        dev->doingBufferedBlockRewrite = 0;
-       dev->blockSelectedForGC = -1;
        dev->nDeletedFiles = 0;
        dev->nBackgroundDeletions=0;
        dev->nUnlinkedFiles = 0;
@@ -4682,7 +4750,7 @@ void yaffs_Deinitialise(yaffs_Device *dev)
 
 int  yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
 {
-       int nFree = dev->nFreeChunks - (YAFFS_CHUNKS_PER_BLOCK * YAFFS_RESERVED_BLOCKS);
+       int nFree = dev->nFreeChunks - (dev->nChunksPerBlock * YAFFS_RESERVED_BLOCKS);
        
        struct list_head *i;    
        yaffs_Object *l;
@@ -4740,11 +4808,14 @@ int  yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
        // To the free chunks add the chunks that are in the deleted unlinked files.
        list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)
        {
-               l = list_entry(i, yaffs_Object,siblings);
-               if(l->deleted)
+               if(i)
                {
-                       pending++;
-                       pending += l->nDataChunks;
+                       l = list_entry(i, yaffs_Object,siblings);
+                       if(l->deleted)
+                       {
+                               pending++;
+                               pending += l->nDataChunks;
+                       }
                }
        }