X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs2.git;a=blobdiff_plain;f=yaffs_guts.c;h=feade2aff1044b208adf2647a924f374d79b52b4;hp=1b0a67f7e6cc78bb7a165ee12f0fefae2b90d32a;hb=a48028210bd3547399ecaed7b0138d6c43a1f547;hpb=30ab3ff5e311e8e2b488c3f62f30e7450dc6bee9 diff --git a/yaffs_guts.c b/yaffs_guts.c index 1b0a67f..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.108 2010-02-18 01:18:04 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, @@ -404,16 +406,19 @@ static int yaffs_CountChunkBits(yaffs_Device *dev, int blk) static int yaffs_SkipVerification(yaffs_Device *dev) { + dev=dev; return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); } static int yaffs_SkipFullVerification(yaffs_Device *dev) { + dev=dev; return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_FULL)); } static int yaffs_SkipNANDVerification(yaffs_Device *dev) { + dev=dev; return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_NAND)); } @@ -682,12 +687,6 @@ static void yaffs_VerifyFile(yaffs_Object *obj) actualTallness = obj->variant.fileVariant.topLevel; - if (requiredTallness > actualTallness) - T(YAFFS_TRACE_VERIFY, - (TSTR("Obj %d had tnode tallness %d, needs to be %d"TENDSTR), - obj->objectId, actualTallness, requiredTallness)); - - /* Check that the chunks in the tnode tree are all correct. * We do this by scanning through the tnode tree and * checking the tags for every chunk match. @@ -1050,6 +1049,81 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, return chunk; } + +/* + * Oldest Dirty Sequence Number handling. + */ + +/* yaffs_CalcOldestDirtySequence() + * yaffs_FindOldestDirtySequence() + * Calculate the oldest dirty sequence number if we don't know it. + */ +static int yaffs_CalcOldestDirtySequence(yaffs_Device *dev) +{ + int i; + __u32 seq; + 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++) { + if (b->blockState == YAFFS_BLOCK_STATE_FULL && + (b->pagesInUse - b->softDeletions) < dev->param.nChunksPerBlock && + b->sequenceNumber < seq) + seq = b->sequenceNumber; + b++; + } + return seq; +} + + +static void yaffs_FindOldestDirtySequence(yaffs_Device *dev) +{ + if(dev->param.isYaffs2 && !dev->oldestDirtySequence) + dev->oldestDirtySequence = + yaffs_CalcOldestDirtySequence(dev); + +#if 0 + if(!yaffs_SkipVerification(dev) && + dev->oldestDirtySequence != yaffs_CalcOldestDirtySequence(dev)) + YBUG(); + +#endif +} + +/* + * yaffs_ClearOldestDirtySequence() + * Called when a block is erased or marked bad. (ie. when its sequenceNumber + * becomes invalid). If the value matches the oldest then we clear + * dev->oldestDirtySequence to force its recomputation. + */ +static void yaffs_ClearOldestDirtySequence(yaffs_Device *dev, yaffs_BlockInfo *bi) +{ + + if(!dev->param.isYaffs2) + return; + + if(!bi || bi->sequenceNumber == dev->oldestDirtySequence) + dev->oldestDirtySequence = 0; +} + +/* + * yaffs_UpdateOldestDirtySequence() + * Update the oldest dirty sequence number whenever we dirty a block. + * Only do this if the oldestDirtySequence is actually being tracked. + */ +static void yaffs_UpdateOldestDirtySequence(yaffs_Device *dev, yaffs_BlockInfo *bi) +{ + if(dev->param.isYaffs2 && dev->oldestDirtySequence){ + if(dev->oldestDirtySequence > bi->sequenceNumber) + dev->oldestDirtySequence = bi->sequenceNumber; + } +} + /* * Block retiring for handling a broken block. */ @@ -1059,6 +1133,8 @@ static void yaffs_RetireBlock(yaffs_Device *dev, int blockInNAND) yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND); yaffs_InvalidateCheckpoint(dev); + + yaffs_ClearOldestDirtySequence(dev,bi); if (yaffs_MarkBlockBad(dev, blockInNAND) != YAFFS_OK) { if (yaffs_EraseBlockInNAND(dev, blockInNAND) != YAFFS_OK) { @@ -1100,11 +1176,18 @@ static void yaffs_HandleWriteChunkOk(yaffs_Device *dev, int chunkInNAND, const __u8 *data, const yaffs_ExtendedTags *tags) { + dev=dev; + chunkInNAND=chunkInNAND; + data=data; + tags=tags; } static void yaffs_HandleUpdateChunk(yaffs_Device *dev, int chunkInNAND, const yaffs_ExtendedTags *tags) { + dev=dev; + chunkInNAND=chunkInNAND; + tags=tags; } void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi) @@ -1452,6 +1535,8 @@ static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev, int requiredTallness; int level = fStruct->topLevel; + dev=dev; + /* Check sane level and chunk Id */ if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) return NULL; @@ -1721,6 +1806,7 @@ static void yaffs_SoftDeleteChunk(yaffs_Device *dev, int chunk) if (theBlock) { theBlock->softDeletions++; dev->nFreeChunks++; + yaffs_UpdateOldestDirtySequence(dev,theBlock); } } @@ -2135,33 +2221,21 @@ static void yaffs_InitialiseObjects(yaffs_Device *dev) static int yaffs_FindNiceObjectBucket(yaffs_Device *dev) { - static int x; int i; int l = 999; int lowest = 999999; - /* First let's see if we can find one that's empty. */ - - for (i = 0; i < 10 && lowest > 0; i++) { - x++; - x %= YAFFS_NOBJECT_BUCKETS; - if (dev->objectBucket[x].count < lowest) { - lowest = dev->objectBucket[x].count; - l = x; - } - - } - /* If we didn't find an empty list, then try - * looking a bit further for a short one + /* Search for the shortest list or one that + * isn't too long. */ - for (i = 0; i < 10 && lowest > 3; i++) { - x++; - x %= YAFFS_NOBJECT_BUCKETS; - if (dev->objectBucket[x].count < lowest) { - lowest = dev->objectBucket[x].count; - l = x; + for (i = 0; i < 10 && lowest > 4; i++) { + dev->bucketFinder++; + dev->bucketFinder %= YAFFS_NOBJECT_BUCKETS; + if (dev->objectBucket[dev->bucketFinder].count < lowest) { + lowest = dev->objectBucket[dev->bucketFinder].count; + l = dev->bucketFinder; } } @@ -2287,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: @@ -2671,9 +2747,6 @@ static void yaffs_DeinitialiseBlocks(yaffs_Device *dev) static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device *dev, yaffs_BlockInfo *bi) { - int i; - __u32 seq; - yaffs_BlockInfo *b; if (!dev->param.isYaffs2) return 1; /* disqualification only applies to yaffs2. */ @@ -2681,23 +2754,7 @@ static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device *dev, if (!bi->hasShrinkHeader) return 1; /* can gc */ - /* Find the oldest dirty sequence number if we don't know it and save it - * so we don't have to keep recomputing it. - */ - if (!dev->oldestDirtySequence) { - seq = dev->sequenceNumber; - - 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; - } - } - dev->oldestDirtySequence = seq; - } + yaffs_FindOldestDirtySequence(dev); /* Can't do gc of this block if there are any blocks older than this one that have * discarded pages. @@ -2705,7 +2762,70 @@ static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device *dev, return (bi->sequenceNumber <= dev->oldestDirtySequence); } -/* FindDiretiestBlock is used to select the dirtiest block (or close enough) +/* + * yaffs_FindRefreshBlock() + * periodically finds the oldest full block by sequence number for refreshing. + * Only for yaffs2. + */ +static __u32 yaffs_FindRefreshBlock(yaffs_Device *dev) +{ + __u32 b ; + + __u32 oldest = 0; + __u32 oldestSequence = 0; + + yaffs_BlockInfo *bi; + + /* + * If refresh period < 10 then refreshing is disabled. + */ + if(dev->param.refreshPeriod < 10 || + !dev->param.isYaffs2) + return oldest; + + /* + * Fix broken values. + */ + if(dev->refreshSkip > dev->param.refreshPeriod) + dev->refreshSkip = dev->param.refreshPeriod; + + if(dev->refreshSkip > 0){ + dev->refreshSkip--; + return oldest; + } + + /* + * Refresh skip is now zero. + * We'll do a refresh this time around.... + * Update the refresh skip and find the oldest block. + */ + dev->refreshSkip = dev->param.refreshPeriod; + dev->refreshCount++; + bi = dev->blockInfo; + for (b = dev->internalStartBlock; b <=dev->internalEndBlock; b++){ + + if (bi->blockState == YAFFS_BLOCK_STATE_FULL){ + + if(oldest < 1 || + bi->sequenceNumber < oldestSequence){ + oldest = b; + oldestSequence = bi->sequenceNumber; + } + } + bi++; + } + + if (oldest > 0) { + T(YAFFS_TRACE_GC, + (TSTR("GC refresh count %d selected block %d with sequenceNumber %d" TENDSTR), + dev->refreshCount, oldest, oldestSequence)); + } + + return oldest; +} + +/* + * FindDiretiestBlock is used to select the dirtiest block (or close enough) * for garbage collection. */ @@ -2724,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 && @@ -2739,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 */ @@ -2800,8 +2919,6 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device *dev, dev->param.nChunksPerBlock - pagesInUse, prioritised)); } - dev->oldestDirtySequence = 0; - if (dirtiest > 0) dev->nonAggressiveSkip = 4; @@ -2822,8 +2939,14 @@ static void yaffs_BlockBecameDirty(yaffs_Device *dev, int blockNo) (TSTR("yaffs_BlockBecameDirty block %d state %d %s"TENDSTR), blockNo, bi->blockState, (bi->needsRetiring) ? "needs retiring" : "")); + yaffs_ClearOldestDirtySequence(dev,bi); + 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); @@ -2851,6 +2974,7 @@ static void yaffs_BlockBecameDirty(yaffs_Device *dev, int blockNo) if (erasedOk) { /* Clean it up... */ bi->blockState = YAFFS_BLOCK_STATE_EMPTY; + bi->sequenceNumber = 0; dev->nErasedBlocks++; bi->pagesInUse = 0; bi->softDeletions = 0; @@ -2953,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; @@ -2970,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, @@ -2985,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; } @@ -3095,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 || @@ -3180,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--; @@ -3222,17 +3347,26 @@ 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; tags.extraShadows = 0; - yaffs_VerifyObjectHeader(object, oh, &tags, 1); - } + /* Update file size */ + if(object->variantType == YAFFS_OBJECT_TYPE_FILE){ + oh->fileSize = object->variant.fileVariant.fileSize; + tags.extraFileLength = oh->fileSize; + } - newChunk = - yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &tags, 1); + yaffs_VerifyObjectHeader(object, oh, &tags, 1); + newChunk = + yaffs_WriteNewChunkWithTagsToNAND(dev,(__u8 *) oh, &tags, 1); + } else + newChunk = + yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &tags, 1); if (newChunk < 0) { retVal = YAFFS_FAIL; @@ -3289,16 +3423,23 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block, yaffs_VerifyCollectedBlock(dev, bi, block); - chunksAfter = yaffs_GetErasedChunks(dev); - if (chunksBefore >= chunksAfter) { - T(YAFFS_TRACE_GC, - (TSTR - ("gc did not increase free chunks before %d after %d" - TENDSTR), chunksBefore, chunksAfter)); - } - /* 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, + (TSTR + ("gc did not increase free chunks before %d after %d" + TENDSTR), chunksBefore, chunksAfter)); + } dev->gcBlock = -1; dev->gcChunk = 0; } @@ -3326,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; @@ -3342,15 +3487,19 @@ static int yaffs_CheckGarbageCollection(yaffs_Device *dev) if (checkpointBlockAdjust < 0) checkpointBlockAdjust = 0; - if (dev->nErasedBlocks < (dev->param.nReservedBlocks + checkpointBlockAdjust + 2)) { - /* We need a block soon...*/ + /* If we need a block soon then do aggressive gc.*/ + if (dev->nErasedBlocks < (dev->param.nReservedBlocks + checkpointBlockAdjust + 2)) aggressive = 1; - } else { - /* We're in no hurry */ + else aggressive = 0; - } - if (dev->gcBlock <= 0) { + /* If we don't already have a block being gc'd then see if we should start another */ + + if (dev->gcBlock < 1 && !aggressive) { + dev->gcBlock = yaffs_FindRefreshBlock(dev); + dev->gcChunk = 0; + } + if (dev->gcBlock < 1) { dev->gcBlock = yaffs_FindBlockForGarbageCollection(dev, aggressive); dev->gcChunk = 0; } @@ -3685,6 +3834,8 @@ void yaffs_DeleteChunk(yaffs_Device *dev, int chunkId, int markNAND, int lyn) chunkId)); bi = yaffs_GetBlockInfo(dev, block); + + yaffs_UpdateOldestDirtySequence(dev,bi); T(YAFFS_TRACE_DELETION, (TSTR("line %d delete of chunk %d" TENDSTR), lyn, chunkId)); @@ -3750,9 +3901,9 @@ static int yaffs_WriteChunkDataToObject(yaffs_Object *in, int chunkInInode, * the tnode now, rather than later when it is harder to clean up. */ prevChunkId = yaffs_FindChunkInFile(in, chunkInInode, &prevTags); - if(prevChunkId <= 0 && - !yaffs_PutChunkIntoFile(in, chunkInInode, 0, 0)){ - } + if(prevChunkId < 1 && + !yaffs_PutChunkIntoFile(in, chunkInInode, 0, 0)) + return 0; /* Set up new tags */ yaffs_InitialiseTags(&newTags); @@ -3769,13 +3920,6 @@ static int yaffs_WriteChunkDataToObject(yaffs_Object *in, int chunkInInode, YBUG(); } - /* - * If there isn't already a chunk there then do a dummy - * insert to make sue we have the desired tnode structure. - */ - if(prevChunkId < 1 && - yaffs_PutChunkIntoFile(in, chunkInInode, 0, 0) != YAFFS_OK) - return -1; newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags, @@ -4266,7 +4410,6 @@ static void yaffs_DeviceToCheckpointDevice(yaffs_CheckpointDevice *cp, cp->nUnlinkedFiles = dev->nUnlinkedFiles; cp->nBackgroundDeletions = dev->nBackgroundDeletions; cp->sequenceNumber = dev->sequenceNumber; - cp->oldestDirtySequence = dev->oldestDirtySequence; } @@ -4282,7 +4425,6 @@ static void yaffs_CheckpointDeviceToDevice(yaffs_Device *dev, dev->nUnlinkedFiles = cp->nUnlinkedFiles; dev->nBackgroundDeletions = cp->nBackgroundDeletions; dev->sequenceNumber = cp->sequenceNumber; - dev->oldestDirtySequence = cp->oldestDirtySequence; } @@ -4737,9 +4879,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->context); } + if (dev->param.markSuperBlockDirty) + dev->param.markSuperBlockDirty(dev); } @@ -4882,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) { @@ -4959,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. */ @@ -5062,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 ------------------ */ @@ -5106,58 +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) - return YAFFS_FAIL; + /* 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); - if (newSize == oldFileSize) - return YAFFS_OK; + yaffs_WriteChunkDataToObject(obj, lastChunk, localBuffer, + newSizeOfPartialChunk, 1); - if (newSize < oldFileSize) { + yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__); + } - yaffs_PruneResizedChunks(in, newSize); + obj->variant.fileVariant.fileSize = newSize; - if (newSizeOfPartialChunk != 0) { - int lastChunk = 1 + newFullChunks; + yaffs_PruneFileStructure(dev, &obj->variant.fileVariant); +} - __u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__); - /* Got to read and rewrite the last chunk with its new size and zero pad */ - yaffs_ReadChunkDataFromObject(in, lastChunk, - localBuffer); +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. + */ + - memset(localBuffer + newSizeOfPartialChunk, 0, - dev->nDataBytesPerChunk - newSizeOfPartialChunk); + loff_t oldFileSize; + int increase; + int smallHole ; + int result = YAFFS_OK; + yaffs_Device *dev = NULL; - yaffs_WriteChunkDataToObject(in, lastChunk, localBuffer, - newSizeOfPartialChunk, 1); + __u8 *localBuffer = NULL; + + int smallIncreaseOk = 0; + + if(!obj) + return YAFFS_FAIL; - yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__); + 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; + + oldFileSize = obj->variant.fileVariant.fileSize; + + if (newSize <= oldFileSize) + return YAFFS_OK; + + increase = newSize - oldFileSize; + + if(increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->nDataBytesPerChunk && + yaffs_CheckSpaceForAllocation(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1)) + smallHole = 1; + else + smallHole = 0; + + 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; } - in->variant.fileVariant.fileSize = newSize; + yaffs_ReleaseTempBuffer(dev,localBuffer,__LINE__); - yaffs_PruneFileStructure(dev, &in->variant.fileVariant); - } else { - /* newsSize > oldFileSize */ - in->variant.fileVariant.fileSize = newSize; + /* 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); + } + + return result; + +} + +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 @@ -5168,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; } @@ -5253,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; @@ -5266,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 { @@ -5283,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. @@ -5351,6 +5590,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: @@ -5425,6 +5668,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: @@ -5742,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; @@ -5769,6 +6013,7 @@ static int yaffs_Scan(yaffs_Device *dev) dev->nErasedBlocks++; dev->nFreeChunks += dev->param.nChunksPerBlock; } + bi++; } startIterator = dev->internalStartBlock; @@ -6266,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; @@ -6320,6 +6565,7 @@ static int yaffs_ScanBackwards(yaffs_Device *dev) } } + bi++; } T(YAFFS_TRACE_SCAN, @@ -6472,35 +6718,26 @@ static int yaffs_ScanBackwards(yaffs_Device *dev) if (in && in->variantType == YAFFS_OBJECT_TYPE_FILE - && chunkBase < - in->variant.fileVariant.shrinkSize) { + && chunkBase < in->variant.fileVariant.shrinkSize) { /* This has not been invalidated by a resize */ - if (!yaffs_PutChunkIntoFile(in, tags.chunkId, - chunk, -1)) { + if (!yaffs_PutChunkIntoFile(in, tags.chunkId, chunk, -1)) { alloc_failed = 1; } /* File size is calculated by looking at the data chunks if we have not * seen an object header yet. Stop this practice once we find an object header. */ - endpos = - (tags.chunkId - - 1) * dev->nDataBytesPerChunk + - tags.byteCount; + endpos = chunkBase + tags.byteCount; if (!in->valid && /* have not got an object header yet */ - in->variant.fileVariant. - scannedFileSize < endpos) { - in->variant.fileVariant. - scannedFileSize = endpos; - in->variant.fileVariant. - fileSize = - in->variant.fileVariant. - scannedFileSize; + in->variant.fileVariant.scannedFileSize < endpos) { + in->variant.fileVariant.scannedFileSize = endpos; + in->variant.fileVariant.fileSize = endpos; } } else if (in) { - /* This chunk has been invalidated by a resize, so delete */ + /* This chunk has been invalidated by a resize, or a past file deletion + * so delete the chunk*/ yaffs_DeleteChunk(dev, chunk, 1, __LINE__); } @@ -6517,9 +6754,9 @@ static int yaffs_ScanBackwards(yaffs_Device *dev) in = NULL; if (tags.extraHeaderInfoAvailable) { - in = yaffs_FindOrCreateObjectByNumber - (dev, tags.objectId, - tags.extraObjectType); + in = yaffs_FindOrCreateObjectByNumber(dev, + tags.objectId, + tags.extraObjectType); if (!in) alloc_failed = 1; } @@ -6601,13 +6838,8 @@ static int yaffs_ScanBackwards(yaffs_Device *dev) isShrink = 1; } - if (isShrink && - in->variant.fileVariant. - shrinkSize > thisSize) { - in->variant.fileVariant. - shrinkSize = - thisSize; - } + if (isShrink && in->variant.fileVariant.shrinkSize > thisSize) + in->variant.fileVariant.shrinkSize = thisSize; if (isShrink) bi->hasShrinkHeader = 1; @@ -6780,14 +7012,12 @@ static int yaffs_ScanBackwards(yaffs_Device *dev) * than its current data extents. */ in->variant.fileVariant.fileSize = fileSize; - in->variant.fileVariant.scannedFileSize = - in->variant.fileVariant.fileSize; + in->variant.fileVariant.scannedFileSize = fileSize; } - if (isShrink && - in->variant.fileVariant.shrinkSize > fileSize) { + if (in->variant.fileVariant.shrinkSize > fileSize) in->variant.fileVariant.shrinkSize = fileSize; - } + break; case YAFFS_OBJECT_TYPE_HARDLINK: @@ -6949,17 +7179,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) @@ -7570,6 +7840,8 @@ 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. */ if (!yaffs_InitialiseTempBuffers(dev)) @@ -7652,7 +7924,6 @@ int yaffs_GutsInitialise(yaffs_Device *dev) dev->nDeletedFiles = 0; dev->nUnlinkedFiles = 0; dev->nBackgroundDeletions = 0; - dev->oldestDirtySequence = 0; if (!init_failed && !yaffs_InitialiseBlocks(dev)) init_failed = 1; @@ -7741,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: @@ -7762,6 +8031,7 @@ static int yaffs_CountFreeChunks(yaffs_Device *dev) default: break; } + blk++; } return nFree;