X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs2.git;a=blobdiff_plain;f=yaffs_guts.c;h=df66ec89860585636ffbfdaf0c80e7b90186493c;hp=e5b1eaf185dd410014dde4b69bf04aceb68585c7;hb=dc9cc82325224ce9231506acaad26f8d032cdcf4;hpb=6faeadf2b9efdb9f403b2cced9317578fd984e3a diff --git a/yaffs_guts.c b/yaffs_guts.c index e5b1eaf..df66ec8 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.115 2010-03-07 23:43:34 charles Exp $"; + "$Id: yaffs_guts.c,v 1.119 2010-03-12 02:48: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); @@ -4856,9 +4864,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 +5009,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 +5086,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 +5187,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 +5239,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 +5389,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 +5572,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 +5650,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 +5968,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 +5995,7 @@ static int yaffs_Scan(yaffs_Device *dev) dev->nErasedBlocks++; dev->nFreeChunks += dev->param.nChunksPerBlock; } + bi++; } startIterator = dev->internalStartBlock; @@ -6384,8 +6493,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 +6547,7 @@ static int yaffs_ScanBackwards(yaffs_Device *dev) } } + bi++; } T(YAFFS_TRACE_SCAN, @@ -7051,17 +7161,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)); + } - yaffs_UpdateObjectHeader(obj,NULL,0,0,0); + } 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; + + 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 +7822,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 +7994,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 +8013,7 @@ static int yaffs_CountFreeChunks(yaffs_Device *dev) default: break; } + blk++; } return nFree;