*/
const char *yaffs_guts_c_version =
- "$Id: yaffs_guts.c,v 1.113 2010-03-05 02:26:27 charles Exp $";
+ "$Id: yaffs_guts.c,v 1.116 2010-03-09 04:12:00 charles Exp $";
#include "yportenv.h"
#include "yaffs_trace.h"
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 = 0;
+
+ if(!dev->param.isYaffs2)
+ return 0;
+
+ /* Find the oldest dirty sequence number. */
+ 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;
+ }
+ 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) {
if (theBlock) {
theBlock->softDeletions++;
dev->nFreeChunks++;
+ yaffs_UpdateOldestDirtySequence(dev,theBlock);
}
}
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.
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) {
chunkId));
bi = yaffs_GetBlockInfo(dev, block);
+
+ yaffs_UpdateOldestDirtySequence(dev,bi);
T(YAFFS_TRACE_DELETION,
(TSTR("line %d delete of chunk %d" TENDSTR), lyn, chunkId));
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);
}
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:
* 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);
+}
- 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. */