Better retirement and erasure checking.
authorcharles <charles>
Thu, 21 Sep 2006 08:13:59 +0000 (08:13 +0000)
committercharles <charles>
Thu, 21 Sep 2006 08:13:59 +0000 (08:13 +0000)
Kconfig
direct/Makefile
direct/yaffs_fileem2k.c
yaffs_guts.c
yaffs_guts.h
yaffs_mtdif2.c
yaffs_nand.c
yaffs_packedtags2.c

diff --git a/Kconfig b/Kconfig
index f314f60..9cc372e 100644 (file)
--- a/Kconfig
+++ b/Kconfig
@@ -105,15 +105,21 @@ config YAFFS_DISABLE_WIDE_TNODES
 
          If unsure, say N.
 
-config YAFFS_DISABLE_CHUNK_ERASED_CHECK
-       bool "Turn off debug chunk erase check"
+config YAFFS_ALWAYS_CHECK_CHUNK_ERASED
+       bool "Force chunk erase check"
        depends on YAFFS_FS
-       default y
+       default n
        help
-         Enabling this turns off the test that chunks are erased in flash
-         before writing to them.  This is safe, since the write verification
-         will fail.  Suggest enabling the test (ie. say N)
-         during development to help debug things.
+          Normally YAFFS only checks chunks before writing until an erased
+         chunk is found. This helps to detect any partially written chunks
+         that might have happened due to power loss.
+
+         Enabling this forces on the test that chunks are erased in flash
+         before writing to them. This takes more time but is potentially a 
+         bit more secure.
+         Suggest setting Y during development and ironing out driver issues
+         etc. Suggest setting to N if you want faster writing.                  
 
          If unsure, say Y.
 
index ff9ac16..47a25a9 100644 (file)
@@ -10,9 +10,9 @@
 #
 # NB Warning this Makefile does not include header dependencies.
 #
-# $Id: Makefile,v 1.10 2006-05-21 09:39:12 charles Exp $
+# $Id: Makefile,v 1.11 2006-09-21 08:13:59 charles Exp $
 
-EXTRA_COMPILE_FLAGS = -DYAFFS_IGNORE_TAGS_ECC
+#EXTRA_COMPILE_FLAGS = -DYAFFS_IGNORE_TAGS_ECC
 
 CFLAGS =    -Wall -DCONFIG_YAFFS_DIRECT -DCONFIG_YAFFS_SHORT_NAMES_IN_RAM -DCONFIG_YAFFS_YAFFS2 -g $(EXTRA_COMPILE_FLAGS)
 #CFLAGS+=   -Wshadow -Wpointer-arith -Wwrite-strings -Wstrict-prototypes -Wmissing-declarations
index 4c5e200..7e867f6 100644 (file)
@@ -15,7 +15,7 @@
 // This provides a YAFFS nand emulation on a file for emulating 2kB pages.
 // THis is only intended as test code to test persistence etc.
 
-const char *yaffs_flashif_c_version = "$Id: yaffs_fileem2k.c,v 1.4 2005-07-18 23:12:00 charles Exp $";
+const char *yaffs_flashif_c_version = "$Id: yaffs_fileem2k.c,v 1.5 2006-09-21 08:13:59 charles Exp $";
 
 
 #include "yportenv.h"
@@ -55,6 +55,8 @@ typedef struct
 
 static yflash_Device filedisk;
 
+int yaffs_testPartialWrite = 0;
+
 static int  CheckInit(void)
 {
        static int initialised = 0;
@@ -138,6 +140,11 @@ int yflash_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND,const __u8
                lseek(filedisk.handle,pos,SEEK_SET);
                written = write(filedisk.handle,data,dev->nBytesPerChunk);
                
+               if(yaffs_testPartialWrite){
+                       close(filedisk.handle);
+                       exit(1);
+               }
+               
                if(written != dev->nBytesPerChunk) return YAFFS_FAIL;
        }
        
index 6b42a9c..db47608 100644 (file)
@@ -13,7 +13,7 @@
  */
 
 const char *yaffs_guts_c_version =
-    "$Id: yaffs_guts.c,v 1.36 2006-09-05 23:23:34 charles Exp $";
+    "$Id: yaffs_guts.c,v 1.37 2006-09-21 08:13:59 charles Exp $";
 
 #include "yportenv.h"
 
@@ -44,7 +44,7 @@ void yfsd_UnlockYAFFS(BOOL fsLockOnly);
 
 /* 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);
@@ -93,7 +93,7 @@ static int yaffs_TagsMatch(const yaffs_ExtendedTags * tags, int objectId,
 
 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);
 
@@ -299,6 +299,10 @@ static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *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,
@@ -319,21 +323,46 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev,
 {
        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"
@@ -343,6 +372,7 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev,
                                    yaffs_WriteChunkWithTagsToNAND(dev, chunk,
                                                                   data, tags);
                        }
+                       
                        attempts++;
 
                        if (writeOk) {
@@ -351,10 +381,11 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev,
                                 *  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);
                        }
                }
 
@@ -403,12 +434,19 @@ static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND,
 {
 }
 
-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__);
 }
@@ -1895,6 +1933,7 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev,
        int iterations;
        int dirtiest = -1;
        int pagesInUse;
+       int prioritised;
        yaffs_BlockInfo *bi;
        static int nonAggressiveSkip = 0;
 
@@ -1925,7 +1964,7 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev,
                }
        }
 
-       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;
@@ -1948,10 +1987,12 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev,
 #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 */
                }
        }
 
@@ -1959,8 +2000,8 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev,
 
        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;
@@ -2013,6 +2054,8 @@ static void yaffs_BlockBecameDirty(yaffs_Device * dev, int blockNo)
                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,
@@ -2092,7 +2135,7 @@ static int yaffs_CheckSpaceForAllocation(yaffs_Device * dev)
        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;
@@ -2133,6 +2176,9 @@ static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve)
                        dev->allocationBlock = -1;
                }
 
+               if(blockUsedPtr)
+                       *blockUsedPtr = bi;
+                       
                return retVal;
        }
        
@@ -5028,6 +5074,7 @@ static int yaffs_ScanBackwards(yaffs_Device * dev)
        
        int fileSize;
        int isShrink;
+       int foundChunksInBlock;
        int equivalentObjectId;
        
 
@@ -5177,6 +5224,7 @@ static int yaffs_ScanBackwards(yaffs_Device * dev)
                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--) {
@@ -5191,11 +5239,20 @@ static int yaffs_ScanBackwards(yaffs_Device * dev)
                        /* 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++;
@@ -5236,9 +5293,11 @@ static int yaffs_ScanBackwards(yaffs_Device * dev)
                        } 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++;
@@ -5282,6 +5341,8 @@ static int yaffs_ScanBackwards(yaffs_Device * dev)
                                /* 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++;
 
index de0627e..e66efcf 100644 (file)
@@ -14,7 +14,7 @@
  *
  * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
  *
- * $Id: yaffs_guts.h,v 1.22 2006-05-17 09:31:06 charles Exp $
+ * $Id: yaffs_guts.h,v 1.23 2006-09-21 08:13:59 charles Exp $
  */
 
 #ifndef __YAFFS_GUTS_H__
@@ -273,6 +273,10 @@ typedef struct {
        yaffs_BlockState blockState:4;  /* One of the above block states */
        __u32 needsRetiring:1;  /* Data has failed on this block, need to get valid data off */
                                /* and retire the block. */
+       __u32 skipErasedCheck: 1; /* If this is set we can skip the erased check on this block */
+       __u32 gcPrioritise: 1;  /* An ECC check or bank check has failed on this block. 
+                                  It should be prioritised for GC */
+
 #ifdef CONFIG_YAFFS_YAFFS2
        __u32 hasShrinkHeader:1; /* This block has at least one shrink object header */
        __u32 sequenceNumber;    /* block sequence number for yaffs2 */
index d3a6552..1b917be 100644 (file)
@@ -16,7 +16,7 @@
 /* mtd interface for YAFFS2 */
 
 const char *yaffs_mtdif2_c_version =
-    "$Id: yaffs_mtdif2.c,v 1.11 2006-04-25 00:41:43 wookey Exp $";
+    "$Id: yaffs_mtdif2.c,v 1.12 2006-09-21 08:13:59 charles Exp $";
 
 #include "yportenv.h"
 
@@ -120,6 +120,9 @@ int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
 
        if (tags)
                yaffs_UnpackTags2(tags, &pt);
+       
+       if(tags && retval == -EBADMSG && tags->eccResult == YAFFS_ECC_RESULT_NO_ERROR)
+               tags->eccResult = YAFFS_ECC_RESULT_UNFIXED;
 
        if (retval == 0)
                return YAFFS_OK;
index cd39d51..989e8d9 100644 (file)
@@ -13,7 +13,7 @@
  */
  
 const char *yaffs_nand_c_version =
-    "$Id: yaffs_nand.c,v 1.1 2006-05-08 10:13:34 charles Exp $";
+    "$Id: yaffs_nand.c,v 1.2 2006-09-21 08:13:59 charles Exp $";
 
 #include "yaffs_nand.h"
 #include "yaffs_tagscompat.h"
@@ -24,16 +24,26 @@ int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
                                           __u8 * buffer,
                                           yaffs_ExtendedTags * tags)
 {
-       chunkInNAND -= dev->chunkOffset;
+       int result;
+       
+       int realignedChunkInNAND = chunkInNAND - dev->chunkOffset;
 
        if (dev->readChunkWithTagsFromNAND)
-               return dev->readChunkWithTagsFromNAND(dev, chunkInNAND, buffer,
+               result = dev->readChunkWithTagsFromNAND(dev, realignedChunkInNAND, buffer,
                                                      tags);
        else
-               return yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(dev,
-                                                                       chunkInNAND,
+               result = yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(dev,
+                                                                       realignedChunkInNAND,
                                                                        buffer,
-                                                                       tags);
+                                                                       tags);  
+       if(tags && 
+          tags->eccResult > YAFFS_ECC_RESULT_NO_ERROR){
+       
+               yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, chunkInNAND/dev->nChunksPerBlock);
+               bi->gcPrioritise = 1;
+       }
+                                                               
+       return result;
 }
 
 int yaffs_WriteChunkWithTagsToNAND(yaffs_Device * dev,
index 5cc68af..69d78b2 100644 (file)
@@ -114,20 +114,34 @@ void yaffs_UnpackTags2(yaffs_ExtendedTags * t, yaffs_PackedTags2 * pt)
                /* Page is in use */
 #ifdef YAFFS_IGNORE_TAGS_ECC
                {
-                       t->eccResult = 0;
+                       t->eccResult = YAFFS_ECC_RESULT_NO_ERROR;
                }
 #else
                {
                        yaffs_ECCOther ecc;
+                       int result;
                        yaffs_ECCCalculateOther((unsigned char *)&pt->t,
                                                sizeof
                                                (yaffs_PackedTags2TagsPart),
                                                &ecc);
-                       t->eccResult =
+                       result =
                            yaffs_ECCCorrectOther((unsigned char *)&pt->t,
                                                  sizeof
                                                  (yaffs_PackedTags2TagsPart),
                                                  &pt->ecc, &ecc);
+                       switch(result){
+                               case 0: 
+                                       t->eccResult = YAFFS_ECC_RESULT_NO_ERROR; 
+                                       break;
+                               case 1: 
+                                       t->eccResult = YAFFS_ECC_RESULT_FIXED;
+                                       break;
+                               case -1:
+                                       t->eccResult = YAFFS_ECC_RESULT_UNFIXED;
+                                       break;
+                               default:
+                                       t->eccResult = YAFFS_ECC_RESULT_UNKNOWN;
+                       }
                }
 #endif
                t->blockBad = 0;