From a8e9cfa2968a5f16ff9ae578cc1d3202ebef8725 Mon Sep 17 00:00:00 2001 From: charles Date: Thu, 21 Sep 2006 08:13:59 +0000 Subject: [PATCH] Better retirement and erasure checking. --- Kconfig | 20 ++++--- direct/Makefile | 4 +- direct/yaffs_fileem2k.c | 9 +++- yaffs_guts.c | 113 +++++++++++++++++++++++++++++++--------- yaffs_guts.h | 6 ++- yaffs_mtdif2.c | 5 +- yaffs_nand.c | 22 +++++--- yaffs_packedtags2.c | 18 ++++++- 8 files changed, 151 insertions(+), 46 deletions(-) diff --git a/Kconfig b/Kconfig index f314f60..9cc372e 100644 --- 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. diff --git a/direct/Makefile b/direct/Makefile index ff9ac16..47a25a9 100644 --- a/direct/Makefile +++ b/direct/Makefile @@ -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 diff --git a/direct/yaffs_fileem2k.c b/direct/yaffs_fileem2k.c index 4c5e200..7e867f6 100644 --- a/direct/yaffs_fileem2k.c +++ b/direct/yaffs_fileem2k.c @@ -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; } diff --git a/yaffs_guts.c b/yaffs_guts.c index 6b42a9c..db47608 100644 --- a/yaffs_guts.c +++ b/yaffs_guts.c @@ -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++; diff --git a/yaffs_guts.h b/yaffs_guts.h index de0627e..e66efcf 100644 --- a/yaffs_guts.h +++ b/yaffs_guts.h @@ -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 */ diff --git a/yaffs_mtdif2.c b/yaffs_mtdif2.c index d3a6552..1b917be 100644 --- a/yaffs_mtdif2.c +++ b/yaffs_mtdif2.c @@ -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; diff --git a/yaffs_nand.c b/yaffs_nand.c index cd39d51..989e8d9 100644 --- a/yaffs_nand.c +++ b/yaffs_nand.c @@ -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, diff --git a/yaffs_packedtags2.c b/yaffs_packedtags2.c index 5cc68af..69d78b2 100644 --- a/yaffs_packedtags2.c +++ b/yaffs_packedtags2.c @@ -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; -- 2.30.2