*/
const char *yaffs_guts_c_version =
- "$Id: yaffs_guts.c,v 1.34 2006-05-21 09:39:12 charles Exp $";
+ "$Id: yaffs_guts.c,v 1.37 2006-09-21 08:13:59 charles Exp $";
#include "yportenv.h"
/* Robustification (if it ever comes about...) */
static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND);
-static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND);
+static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND, int erasedOk);
static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND,
const __u8 * data,
const yaffs_ExtendedTags * tags);
/* Other local prototypes */
static int yaffs_UnlinkObject( yaffs_Object *obj);
+static int yaffs_ObjectHasCachedWriteData(yaffs_Object *obj);
static void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList);
loff_t yaffs_GetFileSize(yaffs_Object * obj);
-static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve);
+static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve, yaffs_BlockInfo **blockUsedPtr);
static void yaffs_VerifyFreeChunks(yaffs_Device * dev);
yaffs_ExtendedTags tags;
yaffs_ReadChunkWithTagsFromNAND(dev, chunkInNAND, data, &tags);
+
+ if(tags.eccResult > YAFFS_ECC_RESULT_NO_ERROR)
+ retval = YAFFS_FAIL;
+
if (!yaffs_CheckFF(data, dev->nBytesPerChunk) || tags.chunkUsed) {
T(YAFFS_TRACE_NANDACCESS,
{
int chunk;
- int writeOk = 1;
+ int writeOk = 0;
+ int erasedOk = 1;
int attempts = 0;
+ yaffs_BlockInfo *bi;
yaffs_InvalidateCheckpoint(dev);
do {
- chunk = yaffs_AllocateChunk(dev, useReserve);
+ chunk = yaffs_AllocateChunk(dev, useReserve,&bi);
if (chunk >= 0) {
-
- /* First check this chunk is erased... */
-#ifndef CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK
- writeOk = yaffs_CheckChunkErased(dev, chunk);
+ /* First check this chunk is erased, if it needs checking.
+ * The checking policy (unless forced always on) is as follows:
+ * Check the first page we try to write in a block.
+ * - If the check passes then we don't need to check any more.
+ * - If the check fails, we check again...
+ * If the block has been erased, we don't need to check.
+ *
+ * However, if the block has been prioritised for gc, then
+ * we think there might be something odd about this block
+ * and should continue doing erased checks.
+ *
+ * Rationale:
+ * We should only ever see chunks that have not been erased
+ * if there was a partially written chunk due to power loss
+ * This checking policy should catch that case with very
+ * few checks and thus save a lot of checks that are most likely not
+ * needed.
+ */
+
+#ifdef CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED
+ bi->skipErasedCheck = 1;
#endif
- if (!writeOk) {
+ if(!bi->skipErasedCheck){
+ erasedOk = yaffs_CheckChunkErased(dev, chunk);
+ if(erasedOk && !bi->gcPrioritise)
+ bi->skipErasedCheck = 1;
+ }
+
+ if (!erasedOk) {
T(YAFFS_TRACE_ERROR,
(TSTR
("**>> yaffs chunk %d was not erased"
yaffs_WriteChunkWithTagsToNAND(dev, chunk,
data, tags);
}
+
attempts++;
if (writeOk) {
* NB We do this at the end to prevent duplicates in the case of a write error.
* Todo
*/
- yaffs_HandleWriteChunkOk(dev, chunk, data,
- tags);
+ yaffs_HandleWriteChunkOk(dev, chunk, data, tags);
+
} else {
- yaffs_HandleWriteChunkError(dev, chunk);
+ /* The erased check or write failed */
+ yaffs_HandleWriteChunkError(dev, chunk, erasedOk);
}
}
{
}
-static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND)
+static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND, int erasedOk)
{
+
int blockInNAND = chunkInNAND / dev->nChunksPerBlock;
+ yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND);
+ bi->gcPrioritise = 1;
+
+ if(erasedOk) {
+ /* Was an actual write failure, so mark the block for retirement */
- /* Mark the block for retirement */
- yaffs_GetBlockInfo(dev, blockInNAND)->needsRetiring = 1;
+ bi->needsRetiring = 1;
+ }
+
/* Delete the chunk */
yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__);
}
int iterations;
int dirtiest = -1;
int pagesInUse;
+ int prioritised;
yaffs_BlockInfo *bi;
static int nonAggressiveSkip = 0;
}
}
- for (i = 0; i <= iterations && pagesInUse > 0; i++) {
+ for (i = 0, prioritised = 0; i <= iterations && pagesInUse > 0 && !prioritised; i++) {
b++;
if (b < dev->internalStartBlock || b > dev->internalEndBlock) {
b = dev->internalStartBlock;
#endif
if (bi->blockState == YAFFS_BLOCK_STATE_FULL &&
- (bi->pagesInUse - bi->softDeletions) < pagesInUse &&
+ (bi->gcPrioritise || (bi->pagesInUse - bi->softDeletions)) < pagesInUse &&
yaffs_BlockNotDisqualifiedFromGC(dev, bi)) {
dirtiest = b;
pagesInUse = (bi->pagesInUse - bi->softDeletions);
+ if(bi->gcPrioritise)
+ prioritised = 1; /* Trick it into selecting this one */
}
}
if (dirtiest > 0) {
T(YAFFS_TRACE_GC,
- (TSTR("GC Selected block %d with %d free" TENDSTR), dirtiest,
- dev->nChunksPerBlock - pagesInUse));
+ (TSTR("GC Selected block %d with %d free, prioritised:%d" TENDSTR), dirtiest,
+ dev->nChunksPerBlock - pagesInUse,prioritised));
}
dev->oldestDirtySequence = 0;
bi->pagesInUse = 0;
bi->softDeletions = 0;
bi->hasShrinkHeader = 0;
+ bi->skipErasedCheck = 1; /* This is clean, so no need to check */
+ bi->gcPrioritise = 0;
yaffs_ClearChunkBits(dev, blockNo);
T(YAFFS_TRACE_ERASE,
return (dev->nFreeChunks > reservedChunks);
}
-static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve)
+static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve, yaffs_BlockInfo **blockUsedPtr)
{
int retVal;
yaffs_BlockInfo *bi;
dev->allocationBlock = -1;
}
+ if(blockUsedPtr)
+ *blockUsedPtr = bi;
+
return retVal;
}
__LINE__);
}
- in->dirty = 0;
+ if(!yaffs_ObjectHasCachedWriteData(in))
+ in->dirty = 0;
/* If this was a shrink, then mark the block that the chunk lives on */
if (isShrink) {
* need a very intelligent search.
*/
+static int yaffs_ObjectHasCachedWriteData(yaffs_Object *obj)
+{
+ yaffs_Device *dev = obj->myDev;
+ int i;
+ yaffs_ChunkCache *cache;
+ int nCaches = obj->myDev->nShortOpCaches;
+
+ if(nCaches > 0){
+ for(i = 0; i < nCaches; i++){
+ if (dev->srCache[i].object == obj &&
+ dev->srCache[i].dirty)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
static void yaffs_FlushFilesChunkCache(yaffs_Object * obj)
{
yaffs_Device *dev = obj->myDev;
if(ok && cp.objectId == ~0)
done = 1;
else if(ok){
- /* printf("Read object %d type %d\n",cp.objectId,cp.variantType); */
+ 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);
if(obj) {
yaffs_CheckpointObjectToObject(obj,&cp);
yaffs_BlockIndex *blockIndex = NULL;
+ if (dev->isYaffs2) {
+ T(YAFFS_TRACE_SCAN,
+ (TSTR("yaffs_Scan is not for YAFFS2!" TENDSTR)));
+ return YAFFS_FAIL;
+ }
+
+ //TODO Throw all the yaffs2 stuuf out of yaffs_Scan since it is only for yaffs1 format.
+
T(YAFFS_TRACE_SCAN,
(TSTR("yaffs_Scan starts intstartblk %d intendblk %d..." TENDSTR),
dev->internalStartBlock, dev->internalEndBlock));
int fileSize;
int isShrink;
+ int foundChunksInBlock;
int equivalentObjectId;
yaffs_BlockIndex *blockIndex = NULL;
+ int altBlockIndex = 0;
if (!dev->isYaffs2) {
T(YAFFS_TRACE_SCAN,
("yaffs_ScanBackwards starts intstartblk %d intendblk %d..."
TENDSTR), dev->internalStartBlock, dev->internalEndBlock));
- chunkData = yaffs_GetTempBuffer(dev, __LINE__);
dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER;
blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex));
+
+ if(!blockIndex) {
+ blockIndex = YMALLOC_ALT(nBlocks * sizeof(yaffs_BlockIndex));
+ altBlockIndex = 1;
+ }
+
+ if(!blockIndex) {
+ T(YAFFS_TRACE_SCAN,
+ (TSTR("yaffs_Scan() could not allocate block index!" TENDSTR)));
+ return YAFFS_FAIL;
+ }
+
+ chunkData = yaffs_GetTempBuffer(dev, __LINE__);
/* Scan all the blocks to determine their state */
for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) {
deleted = 0;
/* For each chunk in each block that needs scanning.... */
+ foundChunksInBlock = 0;
for (c = dev->nChunksPerBlock - 1; c >= 0 &&
(state == YAFFS_BLOCK_STATE_NEEDS_SCANNING ||
state == YAFFS_BLOCK_STATE_ALLOCATING); c--) {
/* Let's have a good look at this chunk... */
if (!tags.chunkUsed) {
- // An unassigned chunk in the block
- // This means that either the block is empty or
- // this is the one being allocated from
+ /* An unassigned chunk in the block.
+ * If there are used chunks after this one, then
+ * it is a chunk that was skipped due to failing the erased
+ * check. Just skip it so that it can be deleted.
+ * But, more typically, We get here when this is an unallocated
+ * chunk and his means that either the block is empty or
+ * this is the one being allocated from
+ */
- if (c == 0) {
+ if(foundChunksInBlock)
+ {
+ /* This is a chunk that was skipped due to failing the erased check */
+
+ } else if (c == 0) {
/* We're looking at the first chunk in the block so the block is unused */
state = YAFFS_BLOCK_STATE_EMPTY;
dev->nErasedBlocks++;
} else if (tags.chunkId > 0) {
/* chunkId > 0 so it is a data chunk... */
unsigned int endpos;
-
__u32 chunkBase =
(tags.chunkId - 1) * dev->nBytesPerChunk;
+
+ foundChunksInBlock = 1;
+
yaffs_SetChunkBit(dev, blk, c);
bi->pagesInUse++;
/* chunkId == 0, so it is an ObjectHeader.
* Thus, we read in the object header and make the object
*/
+ foundChunksInBlock = 1;
+
yaffs_SetChunkBit(dev, blk, c);
bi->pagesInUse++;
}
- if (blockIndex) {
+ if (altBlockIndex)
+ YFREE_ALT(blockIndex);
+ else
YFREE(blockIndex);
- }
+
/* Ok, we've done all the scanning.
* Fix up the hard link chains.
* We should now have scanned all the objects, now it's time to add these