X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs2.git;a=blobdiff_plain;f=yaffs_guts.c;h=feade2aff1044b208adf2647a924f374d79b52b4;hp=1ad630b8a9f4e3c26cc3527717cea0fc9c109bdb;hb=a48028210bd3547399ecaed7b0138d6c43a1f547;hpb=adf8ff155a242969024595f8750f191be6b8acaf diff --git a/yaffs_guts.c b/yaffs_guts.c index 1ad630b..feade2a 100644 --- a/yaffs_guts.c +++ b/yaffs_guts.c @@ -12,7 +12,7 @@ */ const char *yaffs_guts_c_version = - "$Id: yaffs_guts.c,v 1.116 2010-03-09 04:12:00 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; } @@ -2798,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 || @@ -2812,6 +2812,7 @@ static __u32 yaffs_FindRefreshBlock(yaffs_Device *dev) oldestSequence = bi->sequenceNumber; } } + bi++; } if (oldest > 0) { @@ -2843,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 && @@ -2858,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 */ @@ -2943,6 +2943,10 @@ static void yaffs_BlockBecameDirty(yaffs_Device *dev, int blockNo) bi->blockState = YAFFS_BLOCK_STATE_DIRTY; + /* If this is the block being garbage collected then stop gc'ing this block */ + if(blockNo == dev->gcBlock) + dev->gcBlock = -1; + if (!bi->needsRetiring) { yaffs_InvalidateCheckpoint(dev); erasedOk = yaffs_EraseBlockInNAND(dev, blockNo); @@ -3073,7 +3077,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; @@ -3090,7 +3094,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, @@ -3105,7 +3109,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; } @@ -3215,12 +3219,6 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block, bi->hasShrinkHeader = 0; /* clear the flag so that the block can erase */ - /* Take off the number of soft deleted entries because - * they're going to get really deleted during GC. - */ - if(dev->gcChunk == 0) /* first time through for this block */ - dev->nFreeChunks -= bi->softDeletions; - dev->isDoingGC = 1; if (isCheckpointBlock || @@ -3300,6 +3298,13 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block, * No need to copy this, just forget about it and * fix up the object. */ + + /* Free chunks already includes softdeleted chunks. + * How ever this chunk is going to soon be really deleted + * which will increment free chunks. + * We have to decrement free chunks so this works out properly. + */ + dev->nFreeChunks--; object->nDataChunks--; @@ -3342,14 +3347,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); @@ -3414,8 +3425,14 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block, - /* If the gc completed then clear the current gcBlock so that we find another. */ - if (bi->blockState != YAFFS_BLOCK_STATE_COLLECTING) { + if (bi->blockState == YAFFS_BLOCK_STATE_COLLECTING) { + /* + * The gc did not complete. Set block state back to FULL + * because checkpointing does not restore gc. + */ + bi->blockState = YAFFS_BLOCK_STATE_FULL; + } else { + /* The gc completed. */ chunksAfter = yaffs_GetErasedChunks(dev); if (chunksBefore >= chunksAfter) { T(YAFFS_TRACE_GC, @@ -3450,6 +3467,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; @@ -5003,7 +5024,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) { @@ -5080,19 +5101,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. */ @@ -5183,6 +5202,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 ------------------ */ @@ -5227,57 +5254,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 @@ -5288,8 +5404,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; } @@ -5373,6 +5489,7 @@ static int yaffs_UnlinkFileIfNeeded(yaffs_Object *in) int retVal; int immediateDeletion = 0; + yaffs_Device *dev = in->myDev; if (!in->myInode) immediateDeletion = 1; @@ -5386,7 +5503,7 @@ static int yaffs_UnlinkFileIfNeeded(yaffs_Object *in) in->objectId)); in->deleted = 1; in->myDev->nDeletedFiles++; - if (1 || in->myDev->param.isYaffs2) + if (dev->param.disableSoftDelete || dev->param.isYaffs2) yaffs_ResizeFile(in, 0); yaffs_SoftDeleteFile(in); } else { @@ -5403,8 +5520,10 @@ int yaffs_DeleteFile(yaffs_Object *in) { int retVal = YAFFS_OK; int deleted = in->deleted; + yaffs_Device *dev = in->myDev; - yaffs_ResizeFile(in, 0); + if (dev->param.disableSoftDelete || dev->param.isYaffs2) + yaffs_ResizeFile(in, 0); if (in->nDataChunks > 0) { /* Use soft deletion if there is data in the file. @@ -5867,8 +5986,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; @@ -5894,6 +6013,7 @@ static int yaffs_Scan(yaffs_Device *dev) dev->nErasedBlocks++; dev->nFreeChunks += dev->param.nChunksPerBlock; } + bi++; } startIterator = dev->internalStartBlock; @@ -6391,8 +6511,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; @@ -6445,6 +6565,7 @@ static int yaffs_ScanBackwards(yaffs_Device *dev) } } + bi++; } T(YAFFS_TRACE_SCAN, @@ -7891,15 +8012,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: @@ -7912,6 +8031,7 @@ static int yaffs_CountFreeChunks(yaffs_Device *dev) default: break; } + blk++; } return nFree;