Add garbage collection control
[yaffs2.git] / yaffs_guts.c
index e5b1eaf185dd410014dde4b69bf04aceb68585c7..d8ca648d2f47b63f551519fb0ea9ac4efbb9f3bd 100644 (file)
@@ -12,7 +12,7 @@
  */
 
 const char *yaffs_guts_c_version =
-    "$Id: yaffs_guts.c,v 1.115 2010-03-07 23:43:34 charles Exp $";
+    "$Id: yaffs_guts.c,v 1.120 2010-03-15 23:10:34 charles Exp $";
 
 #include "yportenv.h"
 #include "yaffs_trace.h"
@@ -35,6 +35,7 @@ const char *yaffs_guts_c_version =
 
 
 #define YAFFS_PASSIVE_GC_CHUNKS 2
+#define YAFFS_SMALL_HOLE_THRESHOLD 3
 
 #include "yaffs_ecc.h"
 
@@ -114,6 +115,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,
@@ -1060,19 +1062,20 @@ static int yaffs_CalcOldestDirtySequence(yaffs_Device *dev)
 {
        int i;
        __u32 seq;
-       yaffs_BlockInfo *b = 0;
+       yaffs_BlockInfo *b;
 
        if(!dev->param.isYaffs2)
                return 0;
 
        /* Find the oldest dirty sequence number. */
        seq = dev->sequenceNumber;
+       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++;
        }
        return seq;
 }
@@ -2358,6 +2361,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:
@@ -2796,12 +2801,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 ||
@@ -2810,6 +2812,7 @@ static __u32 yaffs_FindRefreshBlock(yaffs_Device *dev)
                                 oldestSequence = bi->sequenceNumber;
                         }
                }
+               bi++;
        }
 
        if (oldest > 0) {
@@ -2841,11 +2844,9 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device *dev,
 
        /* First let's see if we need to grab a prioritised block */
        if (dev->hasPendingPrioritisedGCs) {
+               bi = dev->blockInfo;
                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 &&
@@ -2856,6 +2857,7 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device *dev,
                                        aggressive = 1; /* Fool the non-aggressive skip logiv below */
                                }
                        }
+                       bi++;
                }
 
                if (!pendingPrioritisedExist) /* None found, so we can clear this */
@@ -3071,7 +3073,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;
@@ -3088,7 +3090,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,
@@ -3103,7 +3105,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;
        }
@@ -3340,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);
@@ -3448,6 +3456,10 @@ static int yaffs_CheckGarbageCollection(yaffs_Device *dev)
 
        int checkpointBlockAdjust;
 
+       if(dev->param.gcControl &&
+               (dev->param.gcControl(dev) & 1) == 0)
+               return YAFFS_OK;
+
        if (dev->isDoingGC) {
                /* Bail out so we don't get recursive gc */
                return YAFFS_OK;
@@ -4856,9 +4868,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);
 }
 
 
@@ -5001,7 +5013,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)
 {
 
@@ -5078,19 +5090,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.
                                         */
@@ -5181,6 +5191,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 ------------------ */
 
@@ -5225,57 +5243,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);
+       }
 
-                       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);
+
+       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
@@ -5286,8 +5393,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);
+
 
        return YAFFS_OK;
 }
@@ -5469,6 +5576,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:
@@ -5543,6 +5654,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:
@@ -5860,8 +5972,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;
@@ -5887,6 +5999,7 @@ static int yaffs_Scan(yaffs_Device *dev)
                        dev->nErasedBlocks++;
                        dev->nFreeChunks += dev->param.nChunksPerBlock;
                }
+               bi++;
        }
 
        startIterator = dev->internalStartBlock;
@@ -6384,8 +6497,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;
@@ -6438,6 +6551,7 @@ static int yaffs_ScanBackwards(yaffs_Device *dev)
 
                        }
                }
+               bi++;
        }
 
        T(YAFFS_TRACE_SCAN,
@@ -7051,17 +7165,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);
+}
+
+void yaffs_UpdateDirtyDirectories(yaffs_Device *dev)
+{
+       struct ylist_head *link;
+       yaffs_Object *obj;
+       yaffs_DirectoryStructure *dS;
+       yaffs_ObjectVariant *oV;
 
-       yaffs_UpdateObjectHeader(obj,NULL,0,0,0);
+       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)
@@ -7672,6 +7826,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 */
+       YINIT_LIST_HEAD(&dev->dirtyDirectories);
        dev->oldestDirtySequence = 0;
 
        /* Initialise temporary buffers and caches. */
@@ -7843,15 +7998,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:
@@ -7864,6 +8017,7 @@ static int yaffs_CountFreeChunks(yaffs_Device *dev)
                default:
                        break;
                }
+               blk++;
        }
 
        return nFree;