From b4a902328423692f296c9118bf6bfaa057ccc72c Mon Sep 17 00:00:00 2001 From: charles Date: Thu, 25 Feb 2010 22:41:46 +0000 Subject: [PATCH] Add block refreshing feature --- Kconfig | 9 +++ direct/yaffscfg2k.c | 1 + moduleconfig.h | 10 +-- yaffs_fs.c | 42 ++++++----- yaffs_guts.c | 166 +++++++++++++++++++++++++++++--------------- yaffs_guts.h | 12 ++-- 6 files changed, 159 insertions(+), 81 deletions(-) diff --git a/Kconfig b/Kconfig index 4a8b0c8..073f541 100644 --- a/Kconfig +++ b/Kconfig @@ -175,3 +175,12 @@ config YAFFS_EMPTY_LOST_AND_FOUND If this is enabled then the contents of lost and found is automatically dumped at mount. +config YAFFS_DISABLE_BLOCK_REFRESHING + boot "Disable yaffs2 block refreshing" + depends on YAFFS_FS + default n + help + If this is set, then block refreshing is disabled. + Block refreshing infrequently refreshes the oldest block in + a yaffs2 file system. This mechanism helps to refresh flash to + mitigate against data loss. This is particularly useful for MLC. diff --git a/direct/yaffscfg2k.c b/direct/yaffscfg2k.c index 7731ade..3c0341c 100644 --- a/direct/yaffscfg2k.c +++ b/direct/yaffscfg2k.c @@ -182,6 +182,7 @@ int yaffs_StartUp(void) flashDev.param.isYaffs2 = 1; flashDev.param.useNANDECC=1; flashDev.param.wideTnodesDisabled=0; + flashDev.param.refreshPeriod = 10000; flashDev.param.nShortOpCaches = 10; // Use caches flashDev.context = (void *) 2; // Used to identify the device in fstat. flashDev.param.writeChunkWithTagsToNAND = yflash2_WriteChunkWithTagsToNAND; diff --git a/moduleconfig.h b/moduleconfig.h index 6468907..f58b8e2 100644 --- a/moduleconfig.h +++ b/moduleconfig.h @@ -46,15 +46,17 @@ /* Meaning: At mount automatically empty all files from lost and found. */ /* This is done to fix an old problem where rmdir was not checking for an */ /* empty directory. This can also be achieved with a mount option. */ -/* #define CONFIG_YAFFS_EMPTY_LOST_AND_FOUND */ +#define CONFIG_YAFFS_EMPTY_LOST_AND_FOUND /* Default: Selected */ /* Meaning: Cache short names, taking more RAM, but faster look-ups */ #define CONFIG_YAFFS_SHORT_NAMES_IN_RAM -/* Default: 10 */ -/* Meaning: set the count of blocks to reserve for checkpointing */ -#define CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS 10 +/* Default: Unselected */ +/* Meaning: Select to disable block refreshing. */ +/* Block Refreshing periodically rewrites the oldest block. */ +/* #define CONFIG_DISABLE_BLOCK_REFRESHING */ + /* Older-style on-NAND data format has a "pageStatus" byte to record diff --git a/yaffs_fs.c b/yaffs_fs.c index 0d219f6..8b11f40 100644 --- a/yaffs_fs.c +++ b/yaffs_fs.c @@ -32,7 +32,7 @@ */ const char *yaffs_fs_c_version = - "$Id: yaffs_fs.c,v 1.95 2010-02-19 01:19:12 charles Exp $"; + "$Id: yaffs_fs.c,v 1.96 2010-02-25 22:41:46 charles Exp $"; extern const char *yaffs_guts_c_version; #include @@ -2301,6 +2301,13 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, #ifdef CONFIG_YAFFS_EMPTY_LOST_AND_FOUND param->emptyLostAndFound = 1; #endif + +#ifdef CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING + param->refreshPeriod = 0; +#else + param->refreshPeriod = 10000; +#endif + if(options.empty_lost_and_found_overridden) param->emptyLostAndFound = options.empty_lost_and_found; @@ -2514,6 +2521,22 @@ static char *yaffs_dump_dev_part0(char *buf, yaffs_Device * dev) buf += sprintf(buf, "startBlock......... %d\n", dev->param.startBlock); buf += sprintf(buf, "endBlock........... %d\n", dev->param.endBlock); buf += sprintf(buf, "totalBytesPerChunk. %d\n", dev->param.totalBytesPerChunk); + buf += sprintf(buf, "useNANDECC......... %d\n", dev->param.useNANDECC); + buf += sprintf(buf, "noTagsECC.......... %d\n", dev->param.noTagsECC); + buf += sprintf(buf, "isYaffs2........... %d\n", dev->param.isYaffs2); + buf += sprintf(buf, "inbandTags......... %d\n", dev->param.inbandTags); + buf += sprintf(buf, "emptyLostAndFound.. %d\n", dev->param.emptyLostAndFound); + buf += sprintf(buf, "disableLazyLoad.... %d\n", dev->param.disableLazyLoad); + buf += sprintf(buf, "refreshPeriod...... %d\n", dev->param.refreshPeriod); + + buf += sprintf(buf, "\n"); + + return buf; +} + + +static char *yaffs_dump_dev_part1(char *buf, yaffs_Device * dev) +{ buf += sprintf(buf, "nDataBytesPerChunk. %d\n", dev->nDataBytesPerChunk); buf += sprintf(buf, "chunkGroupBits..... %d\n", dev->chunkGroupBits); buf += sprintf(buf, "chunkGroupSize..... %d\n", dev->chunkGroupSize); @@ -2530,8 +2553,7 @@ static char *yaffs_dump_dev_part0(char *buf, yaffs_Device * dev) buf += sprintf(buf, "nBlockErasures..... %d\n", dev->nBlockErasures); buf += sprintf(buf, "nGCCopies.......... %d\n", dev->nGCCopies); buf += sprintf(buf, "garbageCollections. %d\n", dev->garbageCollections); - buf += sprintf(buf, "passiveGCs......... %d\n", - dev->passiveGarbageCollections); + buf += sprintf(buf, "passiveGCs......... %d\n", dev->passiveGarbageCollections); buf += sprintf(buf, "nRetriedWrites..... %d\n", dev->nRetriedWrites); buf += sprintf(buf, "nShortOpCaches..... %d\n", dev->param.nShortOpCaches); buf += sprintf(buf, "nRetireBlocks...... %d\n", dev->nRetiredBlocks); @@ -2542,25 +2564,13 @@ static char *yaffs_dump_dev_part0(char *buf, yaffs_Device * dev) buf += sprintf(buf, "cacheHits.......... %d\n", dev->cacheHits); buf += sprintf(buf, "nDeletedFiles...... %d\n", dev->nDeletedFiles); buf += sprintf(buf, "nUnlinkedFiles..... %d\n", dev->nUnlinkedFiles); + buf += sprintf(buf, "refreshCount....... %d\n", dev->refreshCount); buf += sprintf(buf, "nBackgroudDeletions %d\n", dev->nBackgroundDeletions); return buf; } - -static char *yaffs_dump_dev_part1(char *buf, yaffs_Device * dev) -{ - buf += sprintf(buf, "useNANDECC......... %d\n", dev->param.useNANDECC); - buf += sprintf(buf, "noTagsECC.......... %d\n", dev->param.noTagsECC); - buf += sprintf(buf, "isYaffs2........... %d\n", dev->param.isYaffs2); - buf += sprintf(buf, "inbandTags......... %d\n", dev->param.inbandTags); - buf += sprintf(buf, "emptyLostAndFound.. %d\n", dev->param.emptyLostAndFound); - buf += sprintf(buf, "disableLazyLoad.... %d\n", dev->param.disableLazyLoad); - - return buf; -} - static int yaffs_proc_read(char *page, char **start, off_t offset, int count, int *eof, void *data) diff --git a/yaffs_guts.c b/yaffs_guts.c index c5d20b1..dc9742b 100644 --- a/yaffs_guts.c +++ b/yaffs_guts.c @@ -12,7 +12,7 @@ */ const char *yaffs_guts_c_version = - "$Id: yaffs_guts.c,v 1.109 2010-02-24 21:06:39 charles Exp $"; + "$Id: yaffs_guts.c,v 1.110 2010-02-25 22:41:46 charles Exp $"; #include "yportenv.h" #include "yaffs_trace.h" @@ -682,12 +682,6 @@ static void yaffs_VerifyFile(yaffs_Object *obj) actualTallness = obj->variant.fileVariant.topLevel; - if (requiredTallness > actualTallness) - T(YAFFS_TRACE_VERIFY, - (TSTR("Obj %d had tnode tallness %d, needs to be %d"TENDSTR), - obj->objectId, actualTallness, requiredTallness)); - - /* Check that the chunks in the tnode tree are all correct. * We do this by scanning through the tnode tree and * checking the tags for every chunk match. @@ -2705,7 +2699,72 @@ static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device *dev, return (bi->sequenceNumber <= dev->oldestDirtySequence); } -/* FindDiretiestBlock is used to select the dirtiest block (or close enough) +/* + * yaffs_FindRefreshBlock() + * periodically finds the oldest full block by sequence number for refreshing. + * Only for yaffs2. + */ +static __u32 yaffs_FindRefreshBlock(yaffs_Device *dev) +{ + __u32 b ; + + __u32 oldest = 0; + __u32 oldestSequence = 0; + + yaffs_BlockInfo *bi; + + /* + * If refresh period < 10 then refreshing is disabled. + */ + if(dev->param.refreshPeriod < 10 || + !dev->param.isYaffs2) + return oldest; + + /* + * Fix broken values. + */ + if(dev->refreshSkip > dev->param.refreshPeriod) + dev->refreshSkip = dev->param.refreshPeriod; + + if(dev->refreshSkip > 0){ + dev->refreshSkip--; + return oldest; + } + + /* + * Refresh skip is now zero. + * We'll do a refresh this time around.... + * Update the refresh skip and find the oldest block. + */ + dev->refreshSkip = dev->param.refreshPeriod; + dev->refreshCount++; + + for (b = dev->internalStartBlock; b <=dev->internalEndBlock; b++){ + + bi = yaffs_GetBlockInfo(dev, b); + + + if (bi->blockState == YAFFS_BLOCK_STATE_FULL){ + + if(oldest < 1 || + bi->sequenceNumber < oldestSequence){ + oldest = b; + oldestSequence = bi->sequenceNumber; + } + } + } + + if (oldest > 0) { + T(YAFFS_TRACE_GC, + (TSTR("GC refresh count %d selected block %d with sequenceNumber %d" TENDSTR), + dev->refreshCount, oldest, oldestSequence)); + } + + return oldest; +} + +/* + * FindDiretiestBlock is used to select the dirtiest block (or close enough) * for garbage collection. */ @@ -2851,6 +2910,7 @@ static void yaffs_BlockBecameDirty(yaffs_Device *dev, int blockNo) if (erasedOk) { /* Clean it up... */ bi->blockState = YAFFS_BLOCK_STATE_EMPTY; + bi->sequenceNumber = 0; dev->nErasedBlocks++; bi->pagesInUse = 0; bi->softDeletions = 0; @@ -3226,13 +3286,16 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block, tags.extraIsShrinkHeader = 0; oh->shadowsObject = 0; oh->inbandShadowsObject = 0; + if(object->variantType == YAFFS_OBJECT_TYPE_FILE) + oh->fileSize = object->variant.fileVariant.fileSize; tags.extraShadows = 0; yaffs_VerifyObjectHeader(object, oh, &tags, 1); - } - - newChunk = - yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &tags, 1); + newChunk = + yaffs_WriteNewChunkWithTagsToNAND(dev,(__u8 *) oh, &tags, 1); + } else + newChunk = + yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &tags, 1); if (newChunk < 0) { retVal = YAFFS_FAIL; @@ -3289,16 +3352,17 @@ static int yaffs_GarbageCollectBlock(yaffs_Device *dev, int block, yaffs_VerifyCollectedBlock(dev, bi, block); - chunksAfter = yaffs_GetErasedChunks(dev); - if (chunksBefore >= chunksAfter) { - T(YAFFS_TRACE_GC, - (TSTR - ("gc did not increase free chunks before %d after %d" - TENDSTR), chunksBefore, chunksAfter)); - } + /* If the gc completed then clear the current gcBlock so that we find another. */ if (bi->blockState != YAFFS_BLOCK_STATE_COLLECTING) { + chunksAfter = yaffs_GetErasedChunks(dev); + if (chunksBefore >= chunksAfter) { + T(YAFFS_TRACE_GC, + (TSTR + ("gc did not increase free chunks before %d after %d" + TENDSTR), chunksBefore, chunksAfter)); + } dev->gcBlock = -1; dev->gcChunk = 0; } @@ -3342,15 +3406,19 @@ static int yaffs_CheckGarbageCollection(yaffs_Device *dev) if (checkpointBlockAdjust < 0) checkpointBlockAdjust = 0; - if (dev->nErasedBlocks < (dev->param.nReservedBlocks + checkpointBlockAdjust + 2)) { - /* We need a block soon...*/ + /* If we need a block soon then do aggressive gc.*/ + if (dev->nErasedBlocks < (dev->param.nReservedBlocks + checkpointBlockAdjust + 2)) aggressive = 1; - } else { - /* We're in no hurry */ + else aggressive = 0; - } - if (dev->gcBlock <= 0) { + /* If we don't already have a block being gc'd then see if we should start another */ + + if (dev->gcBlock < 1 && !aggressive) { + dev->gcBlock = yaffs_FindRefreshBlock(dev); + dev->gcChunk = 0; + } + if (dev->gcBlock < 1) { dev->gcBlock = yaffs_FindBlockForGarbageCollection(dev, aggressive); dev->gcChunk = 0; } @@ -6472,35 +6540,26 @@ static int yaffs_ScanBackwards(yaffs_Device *dev) if (in && in->variantType == YAFFS_OBJECT_TYPE_FILE - && chunkBase < - in->variant.fileVariant.shrinkSize) { + && chunkBase < in->variant.fileVariant.shrinkSize) { /* This has not been invalidated by a resize */ - if (!yaffs_PutChunkIntoFile(in, tags.chunkId, - chunk, -1)) { + if (!yaffs_PutChunkIntoFile(in, tags.chunkId, chunk, -1)) { alloc_failed = 1; } /* File size is calculated by looking at the data chunks if we have not * seen an object header yet. Stop this practice once we find an object header. */ - endpos = - (tags.chunkId - - 1) * dev->nDataBytesPerChunk + - tags.byteCount; + endpos = chunkBase + tags.byteCount; if (!in->valid && /* have not got an object header yet */ - in->variant.fileVariant. - scannedFileSize < endpos) { - in->variant.fileVariant. - scannedFileSize = endpos; - in->variant.fileVariant. - fileSize = - in->variant.fileVariant. - scannedFileSize; + in->variant.fileVariant.scannedFileSize < endpos) { + in->variant.fileVariant.scannedFileSize = endpos; + in->variant.fileVariant.fileSize = endpos; } } else if (in) { - /* This chunk has been invalidated by a resize, so delete */ + /* This chunk has been invalidated by a resize, or a past file deletion + * so delete the chunk*/ yaffs_DeleteChunk(dev, chunk, 1, __LINE__); } @@ -6517,9 +6576,9 @@ static int yaffs_ScanBackwards(yaffs_Device *dev) in = NULL; if (tags.extraHeaderInfoAvailable) { - in = yaffs_FindOrCreateObjectByNumber - (dev, tags.objectId, - tags.extraObjectType); + in = yaffs_FindOrCreateObjectByNumber(dev, + tags.objectId, + tags.extraObjectType); if (!in) alloc_failed = 1; } @@ -6601,13 +6660,8 @@ static int yaffs_ScanBackwards(yaffs_Device *dev) isShrink = 1; } - if (isShrink && - in->variant.fileVariant. - shrinkSize > thisSize) { - in->variant.fileVariant. - shrinkSize = - thisSize; - } + if (isShrink && in->variant.fileVariant.shrinkSize > thisSize) + in->variant.fileVariant.shrinkSize = thisSize; if (isShrink) bi->hasShrinkHeader = 1; @@ -6780,14 +6834,12 @@ static int yaffs_ScanBackwards(yaffs_Device *dev) * than its current data extents. */ in->variant.fileVariant.fileSize = fileSize; - in->variant.fileVariant.scannedFileSize = - in->variant.fileVariant.fileSize; + in->variant.fileVariant.scannedFileSize = fileSize; } - if (isShrink && - in->variant.fileVariant.shrinkSize > fileSize) { + if (in->variant.fileVariant.shrinkSize > fileSize) in->variant.fileVariant.shrinkSize = fileSize; - } + break; case YAFFS_OBJECT_TYPE_HARDLINK: diff --git a/yaffs_guts.h b/yaffs_guts.h index 53ab280..7d2ab53 100644 --- a/yaffs_guts.h +++ b/yaffs_guts.h @@ -557,6 +557,8 @@ struct yaffs_DeviceParamStruct { int emptyLostAndFound; /* Auto-empty lost+found directory on mount */ + int refreshPeriod; /* How often we should check to do a block refresh */ + /* Checkpoint control. Can be set before or after initialisation */ __u8 skipCheckpointRead; __u8 skipCheckpointWrite; @@ -723,8 +725,6 @@ struct yaffs_DeviceStruct { yaffs_ChunkCache *srCache; int srLastUse; - int cacheHits; - /* Stuff for background deletion and unlinked files.*/ yaffs_Object *unlinkedDir; /* Directory where unlinked and deleted files live. */ yaffs_Object *deletedDir; /* Directory where deleted objects are sent to disappear. */ @@ -733,7 +733,6 @@ struct yaffs_DeviceStruct { int nUnlinkedFiles; /* Count of unlinked files. */ int nBackgroundDeletions; /* Count of background deletions. */ - /* Temporary buffer management */ yaffs_TempBuffer tempBuffer[YAFFS_N_TEMP_BUFFERS]; int maxTemp; @@ -744,7 +743,9 @@ struct yaffs_DeviceStruct { /* yaffs2 runtime stuff */ unsigned sequenceNumber; /* Sequence number of currently allocating block */ unsigned oldestDirtySequence; - + + /* Block refreshing */ + int refreshSkip; /* A skip down counter. Refresh happens when this gets to zero. */ /* Statistcs */ int nPageWrites; @@ -762,6 +763,9 @@ struct yaffs_DeviceStruct { int tagsEccUnfixed; int nDeletions; int nUnmarkedDeletions; + int refreshCount; + int cacheHits; + }; typedef struct yaffs_DeviceStruct yaffs_Device; -- 2.30.2