/*
- * YAFFS: Yet another FFS. A NAND-flash specific file system.
+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
*
- * Copyright (C) 2002 Aleph One Ltd.
+ * Copyright (C) 2002-2007 Aleph One Ltd.
* for Toby Churchill Ltd and Brightstar Engineering
*
* Created by Charles Manning <charles@aleph1.co.uk>
*/
const char *yaffs_guts_c_version =
- "$Id: yaffs_guts.c,v 1.39 2006-10-03 10:13:03 charles Exp $";
+ "$Id: yaffs_guts.c,v 1.46 2007-02-12 16:55:26 wookey Exp $";
#include "yportenv.h"
static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND)
{
+ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND);
yaffs_InvalidateCheckpoint(dev);
yaffs_MarkBlockBad(dev, blockInNAND);
- yaffs_GetBlockInfo(dev, blockInNAND)->blockState =
- YAFFS_BLOCK_STATE_DEAD;
+ bi->blockState = YAFFS_BLOCK_STATE_DEAD;
+ bi->gcPrioritise = 0;
+ bi->needsRetiring = 0;
dev->nRetiredBlocks++;
}
{
}
+void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi)
+{
+ if(!bi->gcPrioritise){
+ bi->gcPrioritise = 1;
+ dev->hasPendingPrioritisedGCs = 1;
+ bi->chunkErrorStrikes ++;
+
+ if(bi->chunkErrorStrikes > 3){
+ bi->needsRetiring = 1; /* Too many stikes, so retire this */
+ T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: Block struck out" TENDSTR)));
+
+ }
+
+ }
+}
+
+static void yaffs_ReportOddballBlocks(yaffs_Device *dev)
+{
+ int i;
+
+ for(i = dev->internalStartBlock; i <= dev->internalEndBlock && (yaffs_traceMask & YAFFS_TRACE_BAD_BLOCKS); i++){
+ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i);
+ if(bi->needsRetiring || bi->gcPrioritise)
+ T(YAFFS_TRACE_BAD_BLOCKS,(TSTR("yaffs block %d%s%s" TENDSTR),
+ i,
+ bi->needsRetiring ? " needs retiring" : "",
+ bi->gcPrioritise ? " gc prioritised" : ""));
+
+ }
+}
+
static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND, int erasedOk)
{
int blockInNAND = chunkInNAND / dev->nChunksPerBlock;
yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND);
+
+ yaffs_HandleChunkError(dev,bi);
- bi->gcPrioritise = 1;
- dev->hasPendingPrioritisedGCs = 1;
- if(erasedOk) {
+ if(erasedOk ) {
/* Was an actual write failure, so mark the block for retirement */
-
bi->needsRetiring = 1;
+ T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
+ (TSTR("**>> Block %d needs retiring" TENDSTR), blockInNAND));
+
+
}
/* Delete the chunk */
dev->blockInfoAlt = 0;
/* Set up dynamic blockinfo stuff. */
- dev->chunkBitmapStride = (dev->nChunksPerBlock + 7) / 8; // round up bytes
+ dev->chunkBitmapStride = (dev->nChunksPerBlock + 7) / 8; /* round up bytes */
dev->chunkBits = YMALLOC(dev->chunkBitmapStride * nBlocks);
if(!dev->chunkBits){
dev->chunkBits = YMALLOC_ALT(dev->chunkBitmapStride * nBlocks);
*/
return (bi->sequenceNumber <= dev->oldestDirtySequence);
- return 1;
-
}
/* FindDiretiestBlock is used to select the dirtiest block (or close enough)
int prioritised=0;
yaffs_BlockInfo *bi;
static int nonAggressiveSkip = 0;
+ int pendingPrioritisedExist = 0;
/* First let's see if we need to grab a prioritised block */
if(dev->hasPendingPrioritisedGCs){
for(i = dev->internalStartBlock; i < dev->internalEndBlock && !prioritised; i++){
bi = yaffs_GetBlockInfo(dev, i);
- if(bi->blockState == YAFFS_BLOCK_STATE_FULL &&
- bi->gcPrioritise &&
- yaffs_BlockNotDisqualifiedFromGC(dev, bi)){
- pagesInUse = (bi->pagesInUse - bi->softDeletions);
- dirtiest = b;
- prioritised = 1;
- aggressive = 1; /* Fool the non-aggressive skip logiv below */
+ if(bi->gcPrioritise) {
+ pendingPrioritisedExist = 1;
+ if(bi->blockState == YAFFS_BLOCK_STATE_FULL &&
+ yaffs_BlockNotDisqualifiedFromGC(dev, bi)){
+ pagesInUse = (bi->pagesInUse - bi->softDeletions);
+ dirtiest = i;
+ prioritised = 1;
+ aggressive = 1; /* Fool the non-aggressive skip logiv below */
+ }
}
}
- if(dirtiest < 0) /* None found, so we can clear this */
+
+ if(!pendingPrioritisedExist) /* None found, so we can clear this */
dev->hasPendingPrioritisedGCs = 0;
}
/* If the block is still healthy erase it and mark as clean.
* If the block has had a data failure, then retire it.
*/
+
+ T(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE,
+ (TSTR("yaffs_BlockBecameDirty block %d state %d %s"TENDSTR),
+ blockNo, bi->blockState, (bi->needsRetiring) ? "needs retiring" : ""));
+
bi->blockState = YAFFS_BLOCK_STATE_DIRTY;
if (!bi->needsRetiring) {
yaffs_CheckpointValidity cp;
cp.structType = sizeof(cp);
cp.magic = YAFFS_MAGIC;
- cp.version = 1;
+ cp.version = YAFFS_CHECKPOINT_VERSION;
cp.head = (head) ? 1 : 0;
return (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp))?
if(ok)
ok = (cp.structType == sizeof(cp)) &&
(cp.magic == YAFFS_MAGIC) &&
- (cp.version == 1) &&
+ (cp.version == YAFFS_CHECKPOINT_VERSION) &&
(cp.head == ((head) ? 1 : 0));
return ok ? 1 : 0;
}
if (!obj->deferedFree) {
yaffs_ObjectToCheckpointObject(&cp,obj);
cp.structType = sizeof(cp);
- /* printf("Write out object %d type %d\n",obj->objectId,obj->variantType); */
+
+ T(YAFFS_TRACE_CHECKPOINT,(
+ TSTR("Checkpoint write object %d parent %d type %d chunk %d obj addr %x" TENDSTR),
+ cp.objectId,cp.parentId,cp.variantType,cp.chunkId,(unsigned) obj));
+
ok = (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp));
if(ok && obj->variantType == YAFFS_OBJECT_TYPE_FILE){
if(ok && cp.objectId == ~0)
done = 1;
else if(ok){
- T(YAFFS_TRACE_CHECKPOINT,(TSTR("Read object %d parent %d type %d" TENDSTR),
- cp.objectId,cp.parentId,cp.variantType));
obj = yaffs_FindOrCreateObjectByNumber(dev,cp.objectId, cp.variantType);
+ T(YAFFS_TRACE_CHECKPOINT,(TSTR("Checkpoint read object %d parent %d type %d chunk %d obj addr %x" TENDSTR),
+ cp.objectId,cp.parentId,cp.variantType,cp.chunkId,(unsigned) obj));
if(obj) {
yaffs_CheckpointObjectToObject(obj,&cp);
if(obj->variantType == YAFFS_OBJECT_TYPE_FILE) {
int yaffs_CheckpointSave(yaffs_Device *dev)
{
+ yaffs_ReportOddballBlocks(dev);
T(YAFFS_TRACE_CHECKPOINT,(TSTR("save entry: isCheckpointed %d"TENDSTR),dev->isCheckpointed));
if(!dev->isCheckpointed)
T(YAFFS_TRACE_CHECKPOINT,(TSTR("restore exit: isCheckpointed %d"TENDSTR),dev->isCheckpointed));
+ yaffs_ReportOddballBlocks(dev);
+
return retval;
}
yaffs_ExtendedTags tags;
int result;
+#if 0
+ T(YAFFS_TRACE_SCAN,(TSTR("details for object %d %s loaded" TENDSTR),
+ in->objectId,
+ in->lazyLoaded ? "not yet" : "already"));
+#endif
+
if(in->lazyLoaded){
in->lazyLoaded = 0;
chunkData = yaffs_GetTempBuffer(dev, __LINE__);
state = YAFFS_BLOCK_STATE_EMPTY;
dev->nErasedBlocks++;
} else {
- /* this is the block being allocated from */
- if (state ==
- YAFFS_BLOCK_STATE_NEEDS_SCANNING) {
- T(YAFFS_TRACE_SCAN,
- (TSTR
- (" Allocating from %d %d"
- TENDSTR), blk, c));
+ if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING ||
+ state == YAFFS_BLOCK_STATE_ALLOCATING) {
+ if(dev->sequenceNumber == bi->sequenceNumber) {
+ /* this is the block being allocated from */
+
+ T(YAFFS_TRACE_SCAN,
+ (TSTR
+ (" Allocating from %d %d"
+ TENDSTR), blk, c));
+
+ state = YAFFS_BLOCK_STATE_ALLOCATING;
+ dev->allocationBlock = blk;
+ dev->allocationPage = c;
+ dev->allocationBlockFinder = blk;
+ }
+ else {
+ /* This is a partially written block that is not
+ * the current allocation block. This block must have
+ * had a write failure, so set up for retirement.
+ */
+
+ bi->needsRetiring = 1;
+ bi->gcPrioritise = 1;
+
+ T(YAFFS_TRACE_ALWAYS,
+ (TSTR("Partially written block %d being set for retirement" TENDSTR),
+ blk));
+ }
+
}
- state = YAFFS_BLOCK_STATE_ALLOCATING;
- dev->allocationBlock = blk;
- dev->allocationPage = c;
- dev->allocationBlockFinder = blk;
- /* Set it to here to encourage the allocator to
- * go forth from here.
- */
- /* Yaffs2 sanity check:
- * This should be the one with the highest sequence number
- */
- if (dev->isYaffs2
- && (dev->sequenceNumber !=
- bi->sequenceNumber)) {
- T(YAFFS_TRACE_ALWAYS,
- (TSTR
- ("yaffs: Allocation block %d was not highest sequence "
- "id: block seq = %d, dev seq = %d"
- TENDSTR), blk,
- bi->sequenceNumber,
- dev->sequenceNumber));
- }
}
dev->nFreeChunks++;
+
} else if (tags.chunkId > 0) {
/* chunkId > 0 so it is a data chunk... */
unsigned int endpos;