From e39518410bfa410b09e4b270755f756b91456001 Mon Sep 17 00:00:00 2001 From: charles Date: Wed, 18 Jul 2007 19:40:37 +0000 Subject: [PATCH] Rolling in Ians and other changes --- Kconfig | 20 ++- Makefile | 1 + direct/Makefile | 2 +- direct/yaffsfs.c | 5 +- moduleconfig.h | 16 +- yaffs_fs.c | 28 +++- yaffs_guts.c | 141 +++++++++--------- yaffs_guts.h | 9 +- yaffs_mtdif1.c | 363 +++++++++++++++++++++++++++++++++++++++++++++ yaffs_qsort.c | 10 +- yaffs_qsort.h | 2 +- yaffs_tagscompat.c | 2 +- yaffs_tagscompat.h | 4 + yportenv.h | 12 +- 14 files changed, 529 insertions(+), 86 deletions(-) create mode 100644 yaffs_mtdif1.c diff --git a/Kconfig b/Kconfig index 6bd1a6b..f55b3c5 100644 --- a/Kconfig +++ b/Kconfig @@ -31,9 +31,25 @@ config YAFFS_YAFFS1 If unsure, say Y. +config YAFFS_9BYTE_TAGS + bool "Use older-style on-NAND data format with pageStatus byte" + depends on YAFFS_YAFFS1 + default n + help + + Older-style on-NAND data format has a "pageStatus" byte to record + chunk/page state. This byte is zero when the page is discarded. + Choose this option if you have existing on-NAND data using this + format that you need to continue to support. New data written + also uses the older-style format. Note: Use of this option + generally requires that MTD's oob layout be adjusted to use the + older-style format. See notes on tags formats and MTD versions. + + If unsure, say N. + config YAFFS_DOES_ECC bool "Lets Yaffs do its own ECC" - depends on YAFFS_FS && YAFFS_YAFFS1 + depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS default n help This enables Yaffs to use its own ECC functions instead of using @@ -43,7 +59,7 @@ config YAFFS_DOES_ECC config YAFFS_ECC_WRONG_ORDER bool "Use the same ecc byte order as Steven Hill's nand_ecc.c" - depends on YAFFS_FS && YAFFS_DOES_ECC + depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS default n help This makes yaffs_ecc.c use the same ecc byte order as Steven diff --git a/Makefile b/Makefile index a17f9e5..538aec4 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,7 @@ ifneq ($(KERNELRELEASE),) obj-m := yaffs2.o yaffs2-objs := yaffs_mtdif.o yaffs_mtdif2.o + yaffs2-objs += yaffs_mtdif1.o yaffs_packedtags1.o yaffs2-objs += yaffs_ecc.o yaffs_fs.o yaffs_guts.o yaffs2-objs += yaffs_packedtags2.o yaffs_qsort.o yaffs2-objs += yaffs_tagscompat.o yaffs_tagsvalidity.o diff --git a/direct/Makefile b/direct/Makefile index fd29221..dd6b61d 100644 --- a/direct/Makefile +++ b/direct/Makefile @@ -14,7 +14,7 @@ # # NB Warning this Makefile does not include header dependencies. # -# $Id: Makefile,v 1.14 2007-03-07 08:30:40 colin Exp $ +# $Id: Makefile,v 1.15 2007-07-18 19:40:38 charles Exp $ #EXTRA_COMPILE_FLAGS = -DYAFFS_IGNORE_TAGS_ECC diff --git a/direct/yaffsfs.c b/direct/yaffsfs.c index 9004685..dc6dab9 100644 --- a/direct/yaffsfs.c +++ b/direct/yaffsfs.c @@ -24,7 +24,7 @@ #endif -const char *yaffsfs_c_version="$Id: yaffsfs.c,v 1.17 2007-02-14 01:09:06 wookey Exp $"; +const char *yaffsfs_c_version="$Id: yaffsfs.c,v 1.18 2007-07-18 19:40:38 charles Exp $"; // configurationList is the list of devices that are supported static yaffsfs_DeviceConfiguration *yaffsfs_configurationList; @@ -38,6 +38,9 @@ static void yaffsfs_RemoveObjectCallback(yaffs_Object *obj); // Handle management. // + +unsigned int yaffs_wr_attempts; + typedef struct { __u8 inUse:1; // this handle is in use diff --git a/moduleconfig.h b/moduleconfig.h index b51a150..8eb1ca3 100644 --- a/moduleconfig.h +++ b/moduleconfig.h @@ -44,7 +44,21 @@ /* Default: 10 */ /* Meaning: set the count of blocks to reserve for checkpointing */ -#define YAFFS_CHECKPOINT_RESERVED_BLOCKS 10 +#define CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS 10 + +/* +Older-style on-NAND data format has a "pageStatus" byte to record +chunk/page state. This byte is zeroed when the page is discarded. +Choose this option if you have existing on-NAND data in this format +that you need to continue to support. New data written also uses the +older-style format. +Note: Use of this option generally requires that MTD's oob layout be +adjusted to use the older-style format. See notes on tags formats and +MTD versions. +*/ +/* Default: Not selected */ +/* Meaning: Use older-style on-NAND data format with pageStatus byte */ +#define CONFIG_YAFFS_9BYTE_TAGS #endif /* YAFFS_OUT_OF_TREE */ diff --git a/yaffs_fs.c b/yaffs_fs.c index 15c2783..6e5aa70 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.60 2007-05-15 20:07:40 charles Exp $"; + "$Id: yaffs_fs.c,v 1.61 2007-07-18 19:40:38 charles Exp $"; extern const char *yaffs_guts_c_version; #include @@ -89,13 +89,23 @@ extern const char *yaffs_guts_c_version; #include "yportenv.h" #include "yaffs_guts.h" -unsigned yaffs_traceMask = YAFFS_TRACE_BAD_BLOCKS - /* | 0xFFFFFFFF */; - #include #include "yaffs_mtdif.h" +#include "yaffs_mtdif1.h" #include "yaffs_mtdif2.h" +unsigned int yaffs_traceMask = YAFFS_TRACE_BAD_BLOCKS; +unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; + +/* Module Parameters */ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +module_param(yaffs_traceMask,uint,0644); +module_param(yaffs_wr_attempts,uint,0644); +#else +MODULE_PARM(yaffs_traceMask,"i"); +MODULE_PARM(yaffs_wr_attempts,"i"); +#endif + /*#define T(x) printk x */ #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) @@ -1778,8 +1788,18 @@ static struct super_block *yaffs_internal_read_super(int yaffsVersion, dev->startBlock = 0; dev->endBlock = nBlocks - 1; } else { +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + /* use the MTD interface in yaffs_mtdif1.c */ + dev->writeChunkWithTagsToNAND = + nandmtd1_WriteChunkWithTagsToNAND; + dev->readChunkWithTagsFromNAND = + nandmtd1_ReadChunkWithTagsFromNAND; + dev->markNANDBlockBad = nandmtd1_MarkNANDBlockBad; + dev->queryNANDBlock = nandmtd1_QueryNANDBlock; +#else dev->writeChunkToNAND = nandmtd_WriteChunkToNAND; dev->readChunkFromNAND = nandmtd_ReadChunkFromNAND; +#endif dev->isYaffs2 = 0; } /* ... and common functions */ diff --git a/yaffs_guts.c b/yaffs_guts.c index c81b1e7..90a05f3 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.49 2007-05-15 20:07:40 charles Exp $"; + "$Id: yaffs_guts.c,v 1.50 2007-07-18 19:40:38 charles Exp $"; #include "yportenv.h" @@ -920,86 +920,88 @@ static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, yaffs_ExtendedTags * tags, int useReserve) { + int attempts = 0; + int writeOk = 0; int chunk; - int writeOk = 0; - int erasedOk = 1; - int attempts = 0; - yaffs_BlockInfo *bi; - yaffs_InvalidateCheckpoint(dev); do { - chunk = yaffs_AllocateChunk(dev, useReserve,&bi); - - if (chunk >= 0) { - /* 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 stop using it. - * - * 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. - */ - - if(bi->gcPrioritise){ - yaffs_DeleteChunk(dev, chunk, 1, __LINE__); - } else { -#ifdef CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED + yaffs_BlockInfo *bi = 0; + int erasedOk = 0; - bi->skipErasedCheck = 0; + chunk = yaffs_AllocateChunk(dev, useReserve, &bi); + if (chunk < 0) { + /* no space */ + break; + } -#endif - if(!bi->skipErasedCheck){ - erasedOk = yaffs_CheckChunkErased(dev, chunk); - if(erasedOk && !bi->gcPrioritise) - bi->skipErasedCheck = 1; - } + /* 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 stop using it. + * + * 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. + */ + if (bi->gcPrioritise) { + yaffs_DeleteChunk(dev, chunk, 1, __LINE__); + /* try another chunk */ + continue; + } - if (!erasedOk) { - T(YAFFS_TRACE_ERROR, - (TSTR - ("**>> yaffs chunk %d was not erased" - TENDSTR), chunk)); - } else { - writeOk = - yaffs_WriteChunkWithTagsToNAND(dev, chunk, - data, tags); - } - - attempts++; + /* let's give it a try */ + attempts++; - if (writeOk) { - /* - * Copy the data into the robustification buffer. - * NB We do this at the end to prevent duplicates in the case of a write error. - * Todo - */ - yaffs_HandleWriteChunkOk(dev, chunk, data, tags); - - } else { - /* The erased check or write failed */ - yaffs_HandleWriteChunkError(dev, chunk, erasedOk); - } +#ifdef CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED + bi->skipErasedCheck = 0; +#endif + if (!bi->skipErasedCheck) { + erasedOk = yaffs_CheckChunkErased(dev, chunk); + if (erasedOk != YAFFS_OK) { + T(YAFFS_TRACE_ERROR, + (TSTR ("**>> yaffs chunk %d was not erased" + TENDSTR), chunk)); + + /* try another chunk */ + continue; } + bi->skipErasedCheck = 1; } - } while (chunk >= 0 && !writeOk); + writeOk = yaffs_WriteChunkWithTagsToNAND(dev, chunk, + data, tags); + if (writeOk != YAFFS_OK) { + yaffs_HandleWriteChunkError(dev, chunk, erasedOk); + /* try another chunk */ + continue; + } + + /* Copy the data into the robustification buffer */ + yaffs_HandleWriteChunkOk(dev, chunk, data, tags); + + } while (writeOk != YAFFS_OK && + (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); + + if(!writeOk) + chunk = -1; if (attempts > 1) { T(YAFFS_TRACE_ERROR, - (TSTR("**>> yaffs write required %d attempts" TENDSTR), - attempts)); + (TSTR("**>> yaffs write required %d attempts" TENDSTR), + attempts)); + dev->nRetriedWrites += (attempts - 1); } @@ -2591,7 +2593,6 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev, int pagesInUse = 0; 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 */ @@ -2623,9 +2624,9 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev, * block has only a few pages in use. */ - nonAggressiveSkip--; + dev->nonAggressiveSkip--; - if (!aggressive && (nonAggressiveSkip > 0)) { + if (!aggressive && (dev->nonAggressiveSkip > 0)) { return -1; } @@ -2686,7 +2687,7 @@ static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev, dev->oldestDirtySequence = 0; if (dirtiest > 0) { - nonAggressiveSkip = 4; + dev->nonAggressiveSkip = 4; } return dirtiest; diff --git a/yaffs_guts.h b/yaffs_guts.h index b780315..87b539b 100644 --- a/yaffs_guts.h +++ b/yaffs_guts.h @@ -92,6 +92,12 @@ #define YAFFS_N_TEMP_BUFFERS 4 +/* We limit the number attempts at sucessfully saving a chunk of data. + * Small-page devices have 32 pages per block; large-page devices have 64. + * Default to something in the order of 5 to 10 blocks worth of chunks. + */ +#define YAFFS_WR_ATTEMPTS (5*64) + /* Sequence numbers are used in YAFFS2 to determine block allocation order. * The range is limited slightly to help distinguish bad numbers from good. * This also allows us to perhaps in the future use special numbers for @@ -271,7 +277,7 @@ typedef struct { int softDeletions:10; /* number of soft deleted pages */ int pagesInUse:10; /* number of pages in use */ - __u32 blockState:4; /* One of the above block states. NB use unsigned because enum is sometimes an int */ + unsigned blockState:4; /* One of the above block states. NB use unsigned because enum is sometimes an int */ __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 */ @@ -692,6 +698,7 @@ struct yaffs_DeviceStruct { int currentDirtyChecker; /* Used to find current dirtiest block */ __u32 *gcCleanupList; /* objects to delete at the end of a GC. */ + int nonAggressiveSkip; /* GC state/mode */ /* Statistcs */ int nPageWrites; diff --git a/yaffs_mtdif1.c b/yaffs_mtdif1.c new file mode 100644 index 0000000..4fd3ea3 --- /dev/null +++ b/yaffs_mtdif1.c @@ -0,0 +1,363 @@ +/* + * YAFFS: Yet another FFS. A NAND-flash specific file system. + * yaffs_mtdif1.c NAND mtd interface functions for small-page NAND. + * + * Copyright (C) 2002 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * This module provides the interface between yaffs_nand.c and the + * MTD API. This version is used when the MTD interface supports the + * 'mtd_oob_ops' style calls to read_oob and write_oob, circa 2.6.17, + * and we have small-page NAND device. + * + * These functions are invoked via function pointers in yaffs_nand.c. + * This replaces functionality provided by functions in yaffs_mtdif.c + * and the yaffs_TagsCompatability functions in yaffs_tagscompat.c that are + * called in yaffs_mtdif.c when the function pointers are NULL. + * We assume the MTD layer is performing ECC (useNANDECC is true). + */ + +#include "yportenv.h" +#include "yaffs_guts.h" +#include "yaffs_packedtags1.h" +#include "yaffs_tagscompat.h" // for yaffs_CalcTagsECC + +#include "linux/kernel.h" +#include "linux/version.h" +#include "linux/types.h" +#include "linux/mtd/mtd.h" + +/* Don't compile this module if we don't have MTD's mtd_oob_ops interface */ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + +const char *yaffs_mtdif1_c_version = "$Id: yaffs_mtdif1.c,v 1.1 2007-07-18 19:40:38 charles Exp $"; + +#ifndef CONFIG_YAFFS_9BYTE_TAGS +# define YTAG1_SIZE 8 +#else +# define YTAG1_SIZE 9 +#endif + +#if 0 +/* Use the following nand_ecclayout with MTD when using + * CONFIG_YAFFS_9BYTE_TAGS and the older on-NAND tags layout. + * If you have existing Yaffs images and the byte order differs from this, + * adjust 'oobfree' to match your existing Yaffs data. + * + * This nand_ecclayout scatters/gathers to/from the old-yaffs layout with the + * pageStatus byte (at NAND spare offset 4) scattered/gathered from/to + * the 9th byte. + * + * Old-style on-NAND format: T0,T1,T2,T3,P,B,T4,T5,E0,E1,E2,T6,T7,E3,E4,E5 + * We have/need PackedTags1 plus pageStatus: T0,T1,T2,T3,T4,T5,T6,T7,P + * where Tn are the tag bytes, En are MTD's ECC bytes, P is the pageStatus + * byte and B is the small-page bad-block indicator byte. + */ +static struct nand_ecclayout nand_oob_16 = { + .eccbytes = 6, + .eccpos = { 8, 9, 10, 13, 14, 15 }, + .oobavail = 9, + .oobfree = { { 0, 4 }, { 6, 2 }, { 11, 2 }, { 4, 1 } } +}; +#endif + +/* Write a chunk (page) of data to NAND. + * + * Caller always provides ExtendedTags data which are converted to a more + * compact (packed) form for storage in NAND. A mini-ECC runs over the + * contents of the tags meta-data; used to valid the tags when read. + * + * - Pack ExtendedTags to PackedTags1 form + * - Compute mini-ECC for PackedTags1 + * - Write data and packed tags to NAND. + * + * Note: Due to the use of the PackedTags1 meta-data which does not include + * a full sequence number (as found in the larger PackedTags2 form) it is + * necessary for Yaffs to re-write a chunk/page (just once) to mark it as + * discarded and dirty. This is not ideal: newer NAND parts are supposed + * to be written just once. When Yaffs performs this operation, this + * function is called with a NULL data pointer -- calling MTD write_oob + * without data is valid usage (2.6.17). + * + * Any underlying MTD error results in YAFFS_FAIL. + * Returns YAFFS_OK or YAFFS_FAIL. + */ +int nandmtd1_WriteChunkWithTagsToNAND(yaffs_Device *dev, + int chunkInNAND, const __u8 * data, const yaffs_ExtendedTags * etags) +{ + struct mtd_info * mtd = dev->genericDevice; + int chunkBytes = dev->nDataBytesPerChunk; + loff_t addr = ((loff_t)chunkInNAND) * chunkBytes; + struct mtd_oob_ops ops; + yaffs_PackedTags1 pt1; + int retval; + + /* we assume that PackedTags1 and yaffs_Tags are compatible */ + compile_time_assertion(sizeof(yaffs_PackedTags1) == 12); + compile_time_assertion(sizeof(yaffs_Tags) == 8); + + yaffs_PackTags1(&pt1, etags); + yaffs_CalcTagsECC((yaffs_Tags *)&pt1); + + /* When deleting a chunk, the upper layer provides only skeletal + * etags, one with chunkDeleted set. However, we need to update the + * tags, not erase them completely. So we use the NAND write property + * that only zeroed-bits stick and set tag bytes to all-ones and + * zero just the (not) deleted bit. + */ +#ifndef CONFIG_YAFFS_9BYTE_TAGS + if (etags->chunkDeleted) { + memset(&pt1, 0xff, 8); + /* clear delete status bit to indicate deleted */ + pt1.deleted = 0; + } +#else + ((__u8 *)&pt1)[8] = 0xff; + if (etags->chunkDeleted) { + memset(&pt1, 0xff, 8); + /* zero pageStatus byte to indicate deleted */ + ((__u8 *)&pt1)[8] = 0; + } +#endif + + memset(&ops, 0, sizeof(ops)); + ops.mode = MTD_OOB_AUTO; + ops.len = (data) ? chunkBytes : 0; + ops.ooblen = YTAG1_SIZE; + ops.datbuf = (__u8 *)data; + ops.oobbuf = (__u8 *)&pt1; + + retval = mtd->write_oob(mtd, addr, &ops); + if (retval) { + yaffs_trace(YAFFS_TRACE_MTD, + "write_oob failed, chunk %d, mtd error %d\n", + chunkInNAND, retval); + } + return retval ? YAFFS_FAIL : YAFFS_OK; +} + +/* Return with empty ExtendedTags but add eccResult. + */ +static int rettags(yaffs_ExtendedTags * etags, int eccResult, int retval) +{ + if (etags) { + memset(etags, 0, sizeof(*etags)); + etags->eccResult = eccResult; + } + return retval; +} + +/* Read a chunk (page) from NAND. + * + * Caller expects ExtendedTags data to be usable even on error; that is, + * all members except eccResult and blockBad are zeroed. + * + * - Check ECC results for data (if applicable) + * - Check for blank/erased block (return empty ExtendedTags if blank) + * - Check the PackedTags1 mini-ECC (correct if necessary/possible) + * - Convert PackedTags1 to ExtendedTags + * - Update eccResult and blockBad members to refect state. + * + * Returns YAFFS_OK or YAFFS_FAIL. + */ +int nandmtd1_ReadChunkWithTagsFromNAND(yaffs_Device *dev, + int chunkInNAND, __u8 * data, yaffs_ExtendedTags * etags) +{ + struct mtd_info * mtd = dev->genericDevice; + int chunkBytes = dev->nDataBytesPerChunk; + loff_t addr = ((loff_t)chunkInNAND) * chunkBytes; + int eccres = YAFFS_ECC_RESULT_NO_ERROR; + struct mtd_oob_ops ops; + yaffs_PackedTags1 pt1; + int retval; + int deleted; + + memset(&ops, 0, sizeof(ops)); + ops.mode = MTD_OOB_AUTO; + ops.len = (data) ? chunkBytes : 0; + ops.ooblen = YTAG1_SIZE; + ops.datbuf = data; + ops.oobbuf = (__u8 *)&pt1; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) + /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug; + * help it out with ops.len = ops.ooblen when ops.datbuf == NULL. + */ + ops.len = (ops.datbuf) ? ops.len : ops.ooblen; +#endif + /* Read page and oob using MTD. + * Check status and determine ECC result. + */ + retval = mtd->read_oob(mtd, addr, &ops); + if (retval) { + yaffs_trace(YAFFS_TRACE_MTD, + "read_oob failed, chunk %d, mtd error %d\n", + chunkInNAND, retval); + } + + switch (retval) { + case 0: + /* no error */ + break; + + case -EUCLEAN: + /* MTD's ECC fixed the data */ + eccres = YAFFS_ECC_RESULT_FIXED; + dev->eccFixed++; + break; + + case -EBADMSG: + /* MTD's ECC could not fix the data */ + dev->eccUnfixed++; + /* fall into... */ + default: + rettags(etags, YAFFS_ECC_RESULT_UNFIXED, 0); + etags->blockBad = (mtd->block_isbad)(mtd, addr); + return YAFFS_FAIL; + } + + /* Check for a blank/erased chunk. + */ + if (yaffs_CheckFF((__u8 *)&pt1, 8)) { + /* when blank, upper layers want eccResult to be <= NO_ERROR */ + return rettags(etags, YAFFS_ECC_RESULT_NO_ERROR, YAFFS_OK); + } + +#ifndef CONFIG_YAFFS_9BYTE_TAGS + /* Read deleted status (bit) then return it to it's non-deleted + * state before performing tags mini-ECC check. pt1.deleted is + * inverted. + */ + deleted = !pt1.deleted; + pt1.deleted = 1; +#else + (void) deleted; /* not used */ +#endif + + /* Check the packed tags mini-ECC and correct if necessary/possible. + */ + retval = yaffs_CheckECCOnTags((yaffs_Tags *)&pt1); + switch (retval) { + case 0: + /* no tags error, use MTD result */ + break; + case 1: + /* recovered tags-ECC error */ + dev->tagsEccFixed++; + eccres = YAFFS_ECC_RESULT_FIXED; + break; + default: + /* unrecovered tags-ECC error */ + dev->tagsEccUnfixed++; + return rettags(etags, YAFFS_ECC_RESULT_UNFIXED, YAFFS_FAIL); + } + + /* Unpack the tags to extended form and set ECC result. + * [set shouldBeFF just to keep yaffs_UnpackTags1 happy] + */ + pt1.shouldBeFF = 0xFFFFFFFF; + yaffs_UnpackTags1(etags, &pt1); + etags->eccResult = eccres; + + /* Set deleted state. + */ +#ifndef CONFIG_YAFFS_9BYTE_TAGS + etags->chunkDeleted = deleted; +#else + etags->chunkDeleted = (yaffs_CountBits(((__u8 *)&pt1)[8]) < 7); +#endif + return YAFFS_OK; +} + +/* Mark a block bad. + * + * This is a persistant state. + * Use of this function should be rare. + * + * Returns YAFFS_OK or YAFFS_FAIL. + */ +int nandmtd1_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo) +{ + struct mtd_info * mtd = dev->genericDevice; + int blocksize = dev->nChunksPerBlock * dev->nDataBytesPerChunk; + int retval; + + yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", blockNo); + + retval = mtd->block_markbad(mtd, (loff_t)blocksize * blockNo); + return (retval) ? YAFFS_FAIL : YAFFS_OK; +} + +/* Check any MTD prerequists. + * + * Returns YAFFS_OK or YAFFS_FAIL. + */ +static int nandmtd1_TestPrerequists(struct mtd_info * mtd) +{ + /* 2.6.18 has mtd->ecclayout->oobavail */ + /* 2.6.21 has mtd->ecclayout->oobavail and mtd->oobavail */ + int oobavail = mtd->ecclayout->oobavail; + + if (oobavail < YTAG1_SIZE) { + yaffs_trace(YAFFS_TRACE_ERROR, + "mtd device has only %d bytes for tags, need %d", + oobavail, YTAG1_SIZE); + return YAFFS_FAIL; + } + return YAFFS_OK; +} + +/* Query for the current state of a specific block. + * + * Examine the tags of the first chunk of the block and return the state: + * - YAFFS_BLOCK_STATE_DEAD, the block is marked bad + * - YAFFS_BLOCK_STATE_NEEDS_SCANNING, the block is in use + * - YAFFS_BLOCK_STATE_EMPTY, the block is clean + * + * Always returns YAFFS_OK. + */ +int nandmtd1_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, + yaffs_BlockState * pState, int *pSequenceNumber) +{ + struct mtd_info * mtd = dev->genericDevice; + int chunkNo = blockNo * dev->nChunksPerBlock; + yaffs_ExtendedTags etags; + int state = YAFFS_BLOCK_STATE_DEAD; + int seqnum = 0; + int retval; + + /* We don't yet have a good place to test for MTD config prerequists. + * Do it here as we are called during the initial scan. + */ + if (nandmtd1_TestPrerequists(mtd) != YAFFS_OK) { + return YAFFS_FAIL; + } + + retval = nandmtd1_ReadChunkWithTagsFromNAND(dev, chunkNo, NULL, &etags); + if (etags.blockBad) { + yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, + "block %d is marked bad", blockNo); + state = YAFFS_BLOCK_STATE_DEAD; + } + else if (etags.chunkUsed) { + state = YAFFS_BLOCK_STATE_NEEDS_SCANNING; + seqnum = etags.sequenceNumber; + } + else { + state = YAFFS_BLOCK_STATE_EMPTY; + } + + *pState = state; + *pSequenceNumber = seqnum; + + /* query always succeeds */ + return YAFFS_OK; +} + +#endif /*KERNEL_VERSION*/ diff --git a/yaffs_qsort.c b/yaffs_qsort.c index 296701d..0429838 100644 --- a/yaffs_qsort.c +++ b/yaffs_qsort.c @@ -74,9 +74,13 @@ med3(char *a, char *b, char *c, int (*cmp)(const void *, const void *)) :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c )); } +#ifndef min #define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + void -qsort(void *aa, size_t n, size_t es, int (*cmp)(const void *, const void *)) +yaffs_qsort(void *aa, size_t n, size_t es, + int (*cmp)(const void *, const void *)) { char *pa, *pb, *pc, *pd, *pl, *pm, *pn; int d, r, swaptype, swap_cnt; @@ -145,12 +149,12 @@ loop: SWAPINIT(a, es); r = min((long)(pd - pc), (long)(pn - pd - es)); vecswap(pb, pn - r, r); if ((r = pb - pa) > es) - qsort(a, r / es, es, cmp); + yaffs_qsort(a, r / es, es, cmp); if ((r = pd - pc) > es) { /* Iterate rather than recurse to save stack space */ a = pn - r; n = r / es; goto loop; } -/* qsort(pn - r, r / es, es, cmp);*/ +/* yaffs_qsort(pn - r, r / es, es, cmp);*/ } diff --git a/yaffs_qsort.h b/yaffs_qsort.h index 6d3f554..3ec7397 100644 --- a/yaffs_qsort.h +++ b/yaffs_qsort.h @@ -17,7 +17,7 @@ #ifndef __YAFFS_QSORT_H__ #define __YAFFS_QSORT_H__ -extern void qsort (void *const base, size_t total_elems, size_t size, +extern void yaffs_qsort (void *const base, size_t total_elems, size_t size, int (*cmp)(const void *, const void *)); #endif diff --git a/yaffs_tagscompat.c b/yaffs_tagscompat.c index d594d61..7622b1a 100644 --- a/yaffs_tagscompat.c +++ b/yaffs_tagscompat.c @@ -45,7 +45,7 @@ static const char yaffs_countBitsTable[256] = { 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 }; -static int yaffs_CountBits(__u8 x) +int yaffs_CountBits(__u8 x) { int retVal; retVal = yaffs_countBitsTable[x]; diff --git a/yaffs_tagscompat.h b/yaffs_tagscompat.h index c746ad9..c1edb6a 100644 --- a/yaffs_tagscompat.h +++ b/yaffs_tagscompat.h @@ -33,4 +33,8 @@ int yaffs_TagsCompatabilityQueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, yaffs_BlockState * state, int *sequenceNumber); +void yaffs_CalcTagsECC(yaffs_Tags * tags); +int yaffs_CheckECCOnTags(yaffs_Tags * tags); +int yaffs_CountBits(__u8 byte); + #endif diff --git a/yportenv.h b/yportenv.h index de7f38e..e5e3dbe 100644 --- a/yportenv.h +++ b/yportenv.h @@ -79,6 +79,14 @@ #define TSTR(x) KERN_WARNING x #define TOUT(p) printk p +#define yaffs_trace(mask, fmt, args...) \ + do { if ((mask) & (yaffs_traceMask|YAFFS_TRACE_ERROR)) \ + printk(KERN_WARNING "yaffs: " fmt, ## args); \ + } while (0) + +#define compile_time_assertion(assertion) \ + ({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; }) + #elif defined CONFIG_YAFFS_DIRECT /* Direct interface */ @@ -133,7 +141,9 @@ #endif -extern unsigned yaffs_traceMask; +/* see yaffs_fs.c */ +extern unsigned int yaffs_traceMask; +extern unsigned int yaffs_wr_attempts; /* * Tracing flags. -- 2.30.2