*/
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.119 2010-03-12 02:48:34 charles Exp $";
#include "yportenv.h"
#include "yaffs_trace.h"
#define YAFFS_PASSIVE_GC_CHUNKS 2
+#define YAFFS_SMALL_HOLE_THRESHOLD 3
#include "yaffs_ecc.h"
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,
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));
}
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.
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.
*/
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) {
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)
int requiredTallness;
int level = fStruct->topLevel;
+ dev=dev;
+
/* Check sane level and chunk Id */
if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL)
return NULL;
if (theBlock) {
theBlock->softDeletions++;
dev->nFreeChunks++;
+ yaffs_UpdateOldestDirtySequence(dev,theBlock);
}
}
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;
}
}
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:
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. */
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.
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.
*/
/* 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 &&
aggressive = 1; /* Fool the non-aggressive skip logiv below */
}
}
+ bi++;
}
if (!pendingPrioritisedExist) /* None found, so we can clear this */
dev->param.nChunksPerBlock - pagesInUse, prioritised));
}
- dev->oldestDirtySequence = 0;
-
if (dirtiest > 0)
dev->nonAggressiveSkip = 4;
(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 (!bi->needsRetiring) {
if (erasedOk) {
/* Clean it up... */
bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
+ bi->sequenceNumber = 0;
dev->nErasedBlocks++;
bi->pagesInUse = 0;
bi->softDeletions = 0;
* 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;
reservedChunks = ((reservedBlocks + checkpointBlocks) * dev->param.nChunksPerBlock);
- return (dev->nFreeChunks > reservedChunks);
+ return (dev->nFreeChunks > (reservedChunks + nChunks));
}
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;
}
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;
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) {
+ 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;
}
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;
}
chunkId));
bi = yaffs_GetBlockInfo(dev, block);
+
+ yaffs_UpdateOldestDirtySequence(dev,bi);
T(YAFFS_TRACE_DELETION,
(TSTR("line %d delete of chunk %d" TENDSTR), lyn, chunkId));
* 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);
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,
cp->nUnlinkedFiles = dev->nUnlinkedFiles;
cp->nBackgroundDeletions = dev->nBackgroundDeletions;
cp->sequenceNumber = dev->sequenceNumber;
- cp->oldestDirtySequence = dev->oldestDirtySequence;
}
dev->nUnlinkedFiles = cp->nUnlinkedFiles;
dev->nBackgroundDeletions = cp->nBackgroundDeletions;
dev->sequenceNumber = cp->sequenceNumber;
- dev->oldestDirtySequence = cp->oldestDirtySequence;
}
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);
}
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)
{
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.
*/
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 ------------------ */
}
-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);
+
+ yaffs_WriteChunkDataToObject(obj, lastChunk, localBuffer,
+ newSizeOfPartialChunk, 1);
+
+ yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__);
+ }
+
+ obj->variant.fileVariant.fileSize = newSize;
+
+ yaffs_PruneFileStructure(dev, &obj->variant.fileVariant);
+}
- if (newSize == oldFileSize)
- return YAFFS_OK;
- if (newSize < oldFileSize) {
+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;
- yaffs_PruneResizedChunks(in, newSize);
+ __u8 *localBuffer = NULL;
+
+ int smallIncreaseOk = 0;
+
+ if(!obj)
+ return YAFFS_FAIL;
- if (newSizeOfPartialChunk != 0) {
- int lastChunk = 1 + newFullChunks;
+ 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;
- __u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__);
+ oldFileSize = obj->variant.fileVariant.fileSize;
- /* Got to read and rewrite the last chunk with its new size and zero pad */
- yaffs_ReadChunkDataFromObject(in, lastChunk,
- localBuffer);
+ if (newSize <= oldFileSize)
+ return YAFFS_OK;
- memset(localBuffer + newSizeOfPartialChunk, 0,
- dev->nDataBytesPerChunk - newSizeOfPartialChunk);
+ increase = newSize - oldFileSize;
- yaffs_WriteChunkDataToObject(in, lastChunk, localBuffer,
- newSizeOfPartialChunk, 1);
+ if(increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->nDataBytesPerChunk &&
+ yaffs_CheckSpaceForAllocation(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1))
+ smallHole = 1;
+ else
+ smallHole = 0;
- yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__);
+ 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
!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;
}
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:
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:
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;
dev->nErasedBlocks++;
dev->nFreeChunks += dev->param.nChunksPerBlock;
}
+ bi++;
}
startIterator = dev->internalStartBlock;
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;
}
}
+ bi++;
}
T(YAFFS_TRACE_SCAN,
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__);
}
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;
}
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;
* 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:
* 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)
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))
dev->nDeletedFiles = 0;
dev->nUnlinkedFiles = 0;
dev->nBackgroundDeletions = 0;
- dev->oldestDirtySequence = 0;
if (!init_failed && !yaffs_InitialiseBlocks(dev))
init_failed = 1;
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:
default:
break;
}
+ blk++;
}
return nFree;