From patchwork Wed Mar 6 08:54:59 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Artem Bityutskiy X-Patchwork-Id: 225454 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from merlin.infradead.org (merlin.infradead.org [IPv6:2001:4978:20e::2]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id A3EF32C039E for ; Wed, 6 Mar 2013 19:55:21 +1100 (EST) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1UDA70-0001uo-23; Wed, 06 Mar 2013 08:54:12 +0000 Received: from mga02.intel.com ([134.134.136.20]) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1UDA6d-0001oB-D7 for linux-mtd@lists.infradead.org; Wed, 06 Mar 2013 08:53:59 +0000 Received: from orsmga002.jf.intel.com ([10.7.209.21]) by orsmga101.jf.intel.com with ESMTP; 06 Mar 2013 00:53:27 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.84,793,1355126400"; d="scan'208";a="294636927" Received: from blue.fi.intel.com ([10.237.72.156]) by orsmga002.jf.intel.com with ESMTP; 06 Mar 2013 00:53:24 -0800 From: Artem Bityutskiy To: David Woodhouse Subject: =?utf-8?q?=5BPATCH=202/2=5D=20mtd=3A=20remove=20nftl=20support?= Date: Wed, 6 Mar 2013 10:54:59 +0200 Message-Id: <1362560099-19638-2-git-send-email-dedekind1@gmail.com> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1362560099-19638-1-git-send-email-dedekind1@gmail.com> References: <1362560099-19638-1-git-send-email-dedekind1@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130306_035348_475298_940921A8 X-CRM114-Status: GOOD ( 43.11 ) X-Spam-Score: -5.0 (-----) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-5.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- -5.0 RCVD_IN_DNSWL_HI RBL: Sender listed at http://www.dnswl.org/, high trust [134.134.136.20 listed in list.dnswl.org] 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider (dedekind1[at]gmail.com) 0.0 DKIM_ADSP_CUSTOM_MED No valid author signature, adsp_override is CUSTOM_MED 0.8 SPF_NEUTRAL SPF: sender does not match SPF record (neutral) 0.2 FREEMAIL_ENVFROM_END_DIGIT Envelope-from freemail username ends in digit (dedekind1[at]gmail.com) -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] 0.9 NML_ADSP_CUSTOM_MED ADSP custom_med hit, and not from a mailing list Cc: MTD Maling List X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-mtd" Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org From: Artem Bityutskiy This is very old cruft with all the users probably dead. Newer DoC devices use inftl. Signed-off-by: Artem Bityutskiy --- drivers/mtd/Kconfig | 30 -- drivers/mtd/Makefile | 2 - drivers/mtd/nftlcore.c | 829 ----------------------------------------------- drivers/mtd/nftlmount.c | 786 -------------------------------------------- 4 files changed, 1647 deletions(-) delete mode 100644 drivers/mtd/nftlcore.c delete mode 100644 drivers/mtd/nftlmount.c diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 557bec5..eaf2a44 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -195,9 +195,6 @@ config MTD_BLOCK this is very unsafe, but could be useful for file systems which are almost never written to. - You do not need this option for use with the DiskOnChip devices. For - those, enable NFTL support (CONFIG_NFTL) instead. - config MTD_BLOCK_RO tristate "Readonly block device access to MTD devices" depends on MTD_BLOCK!=y && BLOCK @@ -207,9 +204,6 @@ config MTD_BLOCK_RO from an MTD device, without the overhead (and danger) of the caching driver. - You do not need this option for use with the DiskOnChip devices. For - those, enable NFTL support (CONFIG_NFTL) instead. - config FTL tristate "FTL (Flash Translation Layer) support" depends on BLOCK @@ -227,30 +221,6 @@ config FTL permitted to copy, modify and distribute the code as you wish. Just not use it. -config NFTL - tristate "NFTL (NAND Flash Translation Layer) support" - depends on BLOCK - select MTD_BLKDEVS - ---help--- - This provides support for the NAND Flash Translation Layer which is - used on M-Systems' DiskOnChip devices. It uses a kind of pseudo- - file system on a flash device to emulate a block device with - 512-byte sectors, on top of which you put a 'normal' file system. - - You may find that the algorithms used in this code are patented - unless you live in the Free World where software patents aren't - legal - in the USA you are only permitted to use this on DiskOnChip - hardware, although under the terms of the GPL you're obviously - permitted to copy, modify and distribute the code as you wish. Just - not use it. - -config NFTL_RW - bool "Write support for NFTL" - depends on NFTL - help - Support for writing to the NAND Flash Translation Layer, as used - on the DiskOnChip. - config INFTL tristate "INFTL (Inverse NAND Flash Translation Layer) support" depends on BLOCK diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 18a38e5..39f616a 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -20,7 +20,6 @@ obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o obj-$(CONFIG_MTD_BLOCK) += mtdblock.o obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o obj-$(CONFIG_FTL) += ftl.o -obj-$(CONFIG_NFTL) += nftl.o obj-$(CONFIG_INFTL) += inftl.o obj-$(CONFIG_RFD_FTL) += rfd_ftl.o obj-$(CONFIG_SSFDC) += ssfdc.o @@ -28,7 +27,6 @@ obj-$(CONFIG_SM_FTL) += sm_ftl.o obj-$(CONFIG_MTD_OOPS) += mtdoops.o obj-$(CONFIG_MTD_SWAP) += mtdswap.o -nftl-objs := nftlcore.o nftlmount.o inftl-objs := inftlcore.o inftlmount.o obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/ diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c deleted file mode 100644 index c5f4ebf..0000000 --- a/drivers/mtd/nftlcore.c +++ /dev/null @@ -1,829 +0,0 @@ -/* - * Linux driver for NAND Flash Translation Layer - * - * Copyright © 1999 Machine Vision Holdings, Inc. - * Copyright © 1999-2010 David Woodhouse - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#define PRERELEASE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* maximum number of loops while examining next block, to have a - chance to detect consistency problems (they should never happen - because of the checks done in the mounting */ - -#define MAX_LOOPS 10000 - - -static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) -{ - struct NFTLrecord *nftl; - unsigned long temp; - - if (mtd->type != MTD_NANDFLASH || mtd->size > UINT_MAX) - return; - /* OK, this is moderately ugly. But probably safe. Alternatives? */ - if (memcmp(mtd->name, "DiskOnChip", 10)) - return; - - pr_debug("NFTL: add_mtd for %s\n", mtd->name); - - nftl = kzalloc(sizeof(struct NFTLrecord), GFP_KERNEL); - - if (!nftl) - return; - - nftl->mbd.mtd = mtd; - nftl->mbd.devnum = -1; - - nftl->mbd.tr = tr; - - if (NFTL_mount(nftl) < 0) { - printk(KERN_WARNING "NFTL: could not mount device\n"); - kfree(nftl); - return; - } - - /* OK, it's a new one. Set up all the data structures. */ - - /* Calculate geometry */ - nftl->cylinders = 1024; - nftl->heads = 16; - - temp = nftl->cylinders * nftl->heads; - nftl->sectors = nftl->mbd.size / temp; - if (nftl->mbd.size % temp) { - nftl->sectors++; - temp = nftl->cylinders * nftl->sectors; - nftl->heads = nftl->mbd.size / temp; - - if (nftl->mbd.size % temp) { - nftl->heads++; - temp = nftl->heads * nftl->sectors; - nftl->cylinders = nftl->mbd.size / temp; - } - } - - if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) { - /* - Oh no we don't have - mbd.size == heads * cylinders * sectors - */ - printk(KERN_WARNING "NFTL: cannot calculate a geometry to " - "match size of 0x%lx.\n", nftl->mbd.size); - printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d " - "(== 0x%lx sects)\n", - nftl->cylinders, nftl->heads , nftl->sectors, - (long)nftl->cylinders * (long)nftl->heads * - (long)nftl->sectors ); - } - - if (add_mtd_blktrans_dev(&nftl->mbd)) { - kfree(nftl->ReplUnitTable); - kfree(nftl->EUNtable); - kfree(nftl); - return; - } -#ifdef PSYCHO_DEBUG - printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a'); -#endif -} - -static void nftl_remove_dev(struct mtd_blktrans_dev *dev) -{ - struct NFTLrecord *nftl = (void *)dev; - - pr_debug("NFTL: remove_dev (i=%d)\n", dev->devnum); - - del_mtd_blktrans_dev(dev); - kfree(nftl->ReplUnitTable); - kfree(nftl->EUNtable); -} - -/* - * Read oob data from flash - */ -int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, - size_t *retlen, uint8_t *buf) -{ - loff_t mask = mtd->writesize - 1; - struct mtd_oob_ops ops; - int res; - - ops.mode = MTD_OPS_PLACE_OOB; - ops.ooboffs = offs & mask; - ops.ooblen = len; - ops.oobbuf = buf; - ops.datbuf = NULL; - - res = mtd_read_oob(mtd, offs & ~mask, &ops); - *retlen = ops.oobretlen; - return res; -} - -/* - * Write oob data to flash - */ -int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, - size_t *retlen, uint8_t *buf) -{ - loff_t mask = mtd->writesize - 1; - struct mtd_oob_ops ops; - int res; - - ops.mode = MTD_OPS_PLACE_OOB; - ops.ooboffs = offs & mask; - ops.ooblen = len; - ops.oobbuf = buf; - ops.datbuf = NULL; - - res = mtd_write_oob(mtd, offs & ~mask, &ops); - *retlen = ops.oobretlen; - return res; -} - -#ifdef CONFIG_NFTL_RW - -/* - * Write data and oob to flash - */ -static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len, - size_t *retlen, uint8_t *buf, uint8_t *oob) -{ - loff_t mask = mtd->writesize - 1; - struct mtd_oob_ops ops; - int res; - - ops.mode = MTD_OPS_PLACE_OOB; - ops.ooboffs = offs & mask; - ops.ooblen = mtd->oobsize; - ops.oobbuf = oob; - ops.datbuf = buf; - ops.len = len; - - res = mtd_write_oob(mtd, offs & ~mask, &ops); - *retlen = ops.retlen; - return res; -} - -/* Actual NFTL access routines */ -/* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used - * when the give Virtual Unit Chain - */ -static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate ) -{ - /* For a given Virtual Unit Chain: find or create a free block and - add it to the chain */ - /* We're passed the number of the last EUN in the chain, to save us from - having to look it up again */ - u16 pot = nftl->LastFreeEUN; - int silly = nftl->nb_blocks; - - /* Normally, we force a fold to happen before we run out of free blocks completely */ - if (!desperate && nftl->numfreeEUNs < 2) { - pr_debug("NFTL_findfreeblock: there are too few free EUNs\n"); - return BLOCK_NIL; - } - - /* Scan for a free block */ - do { - if (nftl->ReplUnitTable[pot] == BLOCK_FREE) { - nftl->LastFreeEUN = pot; - nftl->numfreeEUNs--; - return pot; - } - - /* This will probably point to the MediaHdr unit itself, - right at the beginning of the partition. But that unit - (and the backup unit too) should have the UCI set - up so that it's not selected for overwriting */ - if (++pot > nftl->lastEUN) - pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN); - - if (!silly--) { - printk("Argh! No free blocks found! LastFreeEUN = %d, " - "FirstEUN = %d\n", nftl->LastFreeEUN, - le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN)); - return BLOCK_NIL; - } - } while (pot != nftl->LastFreeEUN); - - return BLOCK_NIL; -} - -static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock ) -{ - struct mtd_info *mtd = nftl->mbd.mtd; - u16 BlockMap[MAX_SECTORS_PER_UNIT]; - unsigned char BlockLastState[MAX_SECTORS_PER_UNIT]; - unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT]; - unsigned int thisEUN; - int block; - int silly; - unsigned int targetEUN; - struct nftl_oob oob; - int inplace = 1; - size_t retlen; - - memset(BlockMap, 0xff, sizeof(BlockMap)); - memset(BlockFreeFound, 0, sizeof(BlockFreeFound)); - - thisEUN = nftl->EUNtable[thisVUC]; - - if (thisEUN == BLOCK_NIL) { - printk(KERN_WARNING "Trying to fold non-existent " - "Virtual Unit Chain %d!\n", thisVUC); - return BLOCK_NIL; - } - - /* Scan to find the Erase Unit which holds the actual data for each - 512-byte block within the Chain. - */ - silly = MAX_LOOPS; - targetEUN = BLOCK_NIL; - while (thisEUN <= nftl->lastEUN ) { - unsigned int status, foldmark; - - targetEUN = thisEUN; - for (block = 0; block < nftl->EraseSize / 512; block ++) { - nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) + - (block * 512), 16 , &retlen, - (char *)&oob); - if (block == 2) { - foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1; - if (foldmark == FOLD_MARK_IN_PROGRESS) { - pr_debug("Write Inhibited on EUN %d\n", thisEUN); - inplace = 0; - } else { - /* There's no other reason not to do inplace, - except ones that come later. So we don't need - to preserve inplace */ - inplace = 1; - } - } - status = oob.b.Status | oob.b.Status1; - BlockLastState[block] = status; - - switch(status) { - case SECTOR_FREE: - BlockFreeFound[block] = 1; - break; - - case SECTOR_USED: - if (!BlockFreeFound[block]) - BlockMap[block] = thisEUN; - else - printk(KERN_WARNING - "SECTOR_USED found after SECTOR_FREE " - "in Virtual Unit Chain %d for block %d\n", - thisVUC, block); - break; - case SECTOR_DELETED: - if (!BlockFreeFound[block]) - BlockMap[block] = BLOCK_NIL; - else - printk(KERN_WARNING - "SECTOR_DELETED found after SECTOR_FREE " - "in Virtual Unit Chain %d for block %d\n", - thisVUC, block); - break; - - case SECTOR_IGNORE: - break; - default: - printk("Unknown status for block %d in EUN %d: %x\n", - block, thisEUN, status); - } - } - - if (!silly--) { - printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", - thisVUC); - return BLOCK_NIL; - } - - thisEUN = nftl->ReplUnitTable[thisEUN]; - } - - if (inplace) { - /* We're being asked to be a fold-in-place. Check - that all blocks which actually have data associated - with them (i.e. BlockMap[block] != BLOCK_NIL) are - either already present or SECTOR_FREE in the target - block. If not, we're going to have to fold out-of-place - anyway. - */ - for (block = 0; block < nftl->EraseSize / 512 ; block++) { - if (BlockLastState[block] != SECTOR_FREE && - BlockMap[block] != BLOCK_NIL && - BlockMap[block] != targetEUN) { - pr_debug("Setting inplace to 0. VUC %d, " - "block %d was %x lastEUN, " - "and is in EUN %d (%s) %d\n", - thisVUC, block, BlockLastState[block], - BlockMap[block], - BlockMap[block]== targetEUN ? "==" : "!=", - targetEUN); - inplace = 0; - break; - } - } - - if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) && - pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) && - BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] != - SECTOR_FREE) { - pr_debug("Pending write not free in EUN %d. " - "Folding out of place.\n", targetEUN); - inplace = 0; - } - } - - if (!inplace) { - pr_debug("Cannot fold Virtual Unit Chain %d in place. " - "Trying out-of-place\n", thisVUC); - /* We need to find a targetEUN to fold into. */ - targetEUN = NFTL_findfreeblock(nftl, 1); - if (targetEUN == BLOCK_NIL) { - /* Ouch. Now we're screwed. We need to do a - fold-in-place of another chain to make room - for this one. We need a better way of selecting - which chain to fold, because makefreeblock will - only ask us to fold the same one again. - */ - printk(KERN_WARNING - "NFTL_findfreeblock(desperate) returns 0xffff.\n"); - return BLOCK_NIL; - } - } else { - /* We put a fold mark in the chain we are folding only if we - fold in place to help the mount check code. If we do not fold in - place, it is possible to find the valid chain by selecting the - longer one */ - oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS); - oob.u.c.unused = 0xffffffff; - nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, - 8, &retlen, (char *)&oob.u); - } - - /* OK. We now know the location of every block in the Virtual Unit Chain, - and the Erase Unit into which we are supposed to be copying. - Go for it. - */ - pr_debug("Folding chain %d into unit %d\n", thisVUC, targetEUN); - for (block = 0; block < nftl->EraseSize / 512 ; block++) { - unsigned char movebuf[512]; - int ret; - - /* If it's in the target EUN already, or if it's pending write, do nothing */ - if (BlockMap[block] == targetEUN || - (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) { - continue; - } - - /* copy only in non free block (free blocks can only - happen in case of media errors or deleted blocks) */ - if (BlockMap[block] == BLOCK_NIL) - continue; - - ret = mtd_read(mtd, - (nftl->EraseSize * BlockMap[block]) + (block * 512), - 512, - &retlen, - movebuf); - if (ret < 0 && !mtd_is_bitflip(ret)) { - ret = mtd_read(mtd, - (nftl->EraseSize * BlockMap[block]) + (block * 512), - 512, - &retlen, - movebuf); - if (ret != -EIO) - printk("Error went away on retry.\n"); - } - memset(&oob, 0xff, sizeof(struct nftl_oob)); - oob.b.Status = oob.b.Status1 = SECTOR_USED; - - nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + - (block * 512), 512, &retlen, movebuf, (char *)&oob); - } - - /* add the header so that it is now a valid chain */ - oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); - oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = BLOCK_NIL; - - nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8, - 8, &retlen, (char *)&oob.u); - - /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */ - - /* At this point, we have two different chains for this Virtual Unit, and no way to tell - them apart. If we crash now, we get confused. However, both contain the same data, so we - shouldn't actually lose data in this case. It's just that when we load up on a medium which - has duplicate chains, we need to free one of the chains because it's not necessary any more. - */ - thisEUN = nftl->EUNtable[thisVUC]; - pr_debug("Want to erase\n"); - - /* For each block in the old chain (except the targetEUN of course), - free it and make it available for future use */ - while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) { - unsigned int EUNtmp; - - EUNtmp = nftl->ReplUnitTable[thisEUN]; - - if (NFTL_formatblock(nftl, thisEUN) < 0) { - /* could not erase : mark block as reserved - */ - nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED; - } else { - /* correctly erased : mark it as free */ - nftl->ReplUnitTable[thisEUN] = BLOCK_FREE; - nftl->numfreeEUNs++; - } - thisEUN = EUNtmp; - } - - /* Make this the new start of chain for thisVUC */ - nftl->ReplUnitTable[targetEUN] = BLOCK_NIL; - nftl->EUNtable[thisVUC] = targetEUN; - - return targetEUN; -} - -static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock) -{ - /* This is the part that needs some cleverness applied. - For now, I'm doing the minimum applicable to actually - get the thing to work. - Wear-levelling and other clever stuff needs to be implemented - and we also need to do some assessment of the results when - the system loses power half-way through the routine. - */ - u16 LongestChain = 0; - u16 ChainLength = 0, thislen; - u16 chain, EUN; - - for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) { - EUN = nftl->EUNtable[chain]; - thislen = 0; - - while (EUN <= nftl->lastEUN) { - thislen++; - //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN); - EUN = nftl->ReplUnitTable[EUN] & 0x7fff; - if (thislen > 0xff00) { - printk("Endless loop in Virtual Chain %d: Unit %x\n", - chain, EUN); - } - if (thislen > 0xff10) { - /* Actually, don't return failure. Just ignore this chain and - get on with it. */ - thislen = 0; - break; - } - } - - if (thislen > ChainLength) { - //printk("New longest chain is %d with length %d\n", chain, thislen); - ChainLength = thislen; - LongestChain = chain; - } - } - - if (ChainLength < 2) { - printk(KERN_WARNING "No Virtual Unit Chains available for folding. " - "Failing request\n"); - return BLOCK_NIL; - } - - return NFTL_foldchain (nftl, LongestChain, pendingblock); -} - -/* NFTL_findwriteunit: Return the unit number into which we can write - for this block. Make it available if it isn't already -*/ -static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) -{ - u16 lastEUN; - u16 thisVUC = block / (nftl->EraseSize / 512); - struct mtd_info *mtd = nftl->mbd.mtd; - unsigned int writeEUN; - unsigned long blockofs = (block * 512) & (nftl->EraseSize -1); - size_t retlen; - int silly, silly2 = 3; - struct nftl_oob oob; - - do { - /* Scan the media to find a unit in the VUC which has - a free space for the block in question. - */ - - /* This condition catches the 0x[7f]fff cases, as well as - being a sanity check for past-end-of-media access - */ - lastEUN = BLOCK_NIL; - writeEUN = nftl->EUNtable[thisVUC]; - silly = MAX_LOOPS; - while (writeEUN <= nftl->lastEUN) { - struct nftl_bci bci; - size_t retlen; - unsigned int status; - - lastEUN = writeEUN; - - nftl_read_oob(mtd, - (writeEUN * nftl->EraseSize) + blockofs, - 8, &retlen, (char *)&bci); - - pr_debug("Status of block %d in EUN %d is %x\n", - block , writeEUN, le16_to_cpu(bci.Status)); - - status = bci.Status | bci.Status1; - switch(status) { - case SECTOR_FREE: - return writeEUN; - - case SECTOR_DELETED: - case SECTOR_USED: - case SECTOR_IGNORE: - break; - default: - // Invalid block. Don't use it any more. Must implement. - break; - } - - if (!silly--) { - printk(KERN_WARNING - "Infinite loop in Virtual Unit Chain 0x%x\n", - thisVUC); - return BLOCK_NIL; - } - - /* Skip to next block in chain */ - writeEUN = nftl->ReplUnitTable[writeEUN]; - } - - /* OK. We didn't find one in the existing chain, or there - is no existing chain. */ - - /* Try to find an already-free block */ - writeEUN = NFTL_findfreeblock(nftl, 0); - - if (writeEUN == BLOCK_NIL) { - /* That didn't work - there were no free blocks just - waiting to be picked up. We're going to have to fold - a chain to make room. - */ - - /* First remember the start of this chain */ - //u16 startEUN = nftl->EUNtable[thisVUC]; - - //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC); - writeEUN = NFTL_makefreeblock(nftl, BLOCK_NIL); - - if (writeEUN == BLOCK_NIL) { - /* OK, we accept that the above comment is - lying - there may have been free blocks - last time we called NFTL_findfreeblock(), - but they are reserved for when we're - desperate. Well, now we're desperate. - */ - pr_debug("Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC); - writeEUN = NFTL_findfreeblock(nftl, 1); - } - if (writeEUN == BLOCK_NIL) { - /* Ouch. This should never happen - we should - always be able to make some room somehow. - If we get here, we've allocated more storage - space than actual media, or our makefreeblock - routine is missing something. - */ - printk(KERN_WARNING "Cannot make free space.\n"); - return BLOCK_NIL; - } - //printk("Restarting scan\n"); - lastEUN = BLOCK_NIL; - continue; - } - - /* We've found a free block. Insert it into the chain. */ - - if (lastEUN != BLOCK_NIL) { - thisVUC |= 0x8000; /* It's a replacement block */ - } else { - /* The first block in a new chain */ - nftl->EUNtable[thisVUC] = writeEUN; - } - - /* set up the actual EUN we're writing into */ - /* Both in our cache... */ - nftl->ReplUnitTable[writeEUN] = BLOCK_NIL; - - /* ... and on the flash itself */ - nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, - &retlen, (char *)&oob.u); - - oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); - - nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, - &retlen, (char *)&oob.u); - - /* we link the new block to the chain only after the - block is ready. It avoids the case where the chain - could point to a free block */ - if (lastEUN != BLOCK_NIL) { - /* Both in our cache... */ - nftl->ReplUnitTable[lastEUN] = writeEUN; - /* ... and on the flash itself */ - nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8, - 8, &retlen, (char *)&oob.u); - - oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum - = cpu_to_le16(writeEUN); - - nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8, - 8, &retlen, (char *)&oob.u); - } - - return writeEUN; - - } while (silly2--); - - printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n", - thisVUC); - return BLOCK_NIL; -} - -static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, - char *buffer) -{ - struct NFTLrecord *nftl = (void *)mbd; - u16 writeEUN; - unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); - size_t retlen; - struct nftl_oob oob; - - writeEUN = NFTL_findwriteunit(nftl, block); - - if (writeEUN == BLOCK_NIL) { - printk(KERN_WARNING - "NFTL_writeblock(): Cannot find block to write to\n"); - /* If we _still_ haven't got a block to use, we're screwed */ - return 1; - } - - memset(&oob, 0xff, sizeof(struct nftl_oob)); - oob.b.Status = oob.b.Status1 = SECTOR_USED; - - nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, - 512, &retlen, (char *)buffer, (char *)&oob); - return 0; -} -#endif /* CONFIG_NFTL_RW */ - -static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, - char *buffer) -{ - struct NFTLrecord *nftl = (void *)mbd; - struct mtd_info *mtd = nftl->mbd.mtd; - u16 lastgoodEUN; - u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)]; - unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); - unsigned int status; - int silly = MAX_LOOPS; - size_t retlen; - struct nftl_bci bci; - - lastgoodEUN = BLOCK_NIL; - - if (thisEUN != BLOCK_NIL) { - while (thisEUN < nftl->nb_blocks) { - if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) + - blockofs, 8, &retlen, - (char *)&bci) < 0) - status = SECTOR_IGNORE; - else - status = bci.Status | bci.Status1; - - switch (status) { - case SECTOR_FREE: - /* no modification of a sector should follow a free sector */ - goto the_end; - case SECTOR_DELETED: - lastgoodEUN = BLOCK_NIL; - break; - case SECTOR_USED: - lastgoodEUN = thisEUN; - break; - case SECTOR_IGNORE: - break; - default: - printk("Unknown status for block %ld in EUN %d: %x\n", - block, thisEUN, status); - break; - } - - if (!silly--) { - printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n", - block / (nftl->EraseSize / 512)); - return 1; - } - thisEUN = nftl->ReplUnitTable[thisEUN]; - } - } - - the_end: - if (lastgoodEUN == BLOCK_NIL) { - /* the requested block is not on the media, return all 0x00 */ - memset(buffer, 0, 512); - } else { - loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs; - size_t retlen; - int res = mtd_read(mtd, ptr, 512, &retlen, buffer); - - if (res < 0 && !mtd_is_bitflip(res)) - return -EIO; - } - return 0; -} - -static int nftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) -{ - struct NFTLrecord *nftl = (void *)dev; - - geo->heads = nftl->heads; - geo->sectors = nftl->sectors; - geo->cylinders = nftl->cylinders; - - return 0; -} - -/**************************************************************************** - * - * Module stuff - * - ****************************************************************************/ - - -static struct mtd_blktrans_ops nftl_tr = { - .name = "nftl", - .major = NFTL_MAJOR, - .part_bits = NFTL_PARTN_BITS, - .blksize = 512, - .getgeo = nftl_getgeo, - .readsect = nftl_readblock, -#ifdef CONFIG_NFTL_RW - .writesect = nftl_writeblock, -#endif - .add_mtd = nftl_add_mtd, - .remove_dev = nftl_remove_dev, - .owner = THIS_MODULE, -}; - -static int __init init_nftl(void) -{ - return register_mtd_blktrans(&nftl_tr); -} - -static void __exit cleanup_nftl(void) -{ - deregister_mtd_blktrans(&nftl_tr); -} - -module_init(init_nftl); -module_exit(cleanup_nftl); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("David Woodhouse , Fabrice Bellard et al."); -MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium"); -MODULE_ALIAS_BLOCKDEV_MAJOR(NFTL_MAJOR); diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c deleted file mode 100644 index 51b9d6a..0000000 --- a/drivers/mtd/nftlmount.c +++ /dev/null @@ -1,786 +0,0 @@ -/* - * NFTL mount code with extensive checks - * - * Author: Fabrice Bellard (fabrice.bellard@netgem.com) - * Copyright © 2000 Netgem S.A. - * Copyright © 1999-2010 David Woodhouse - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include - -#define SECTORSIZE 512 - -/* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the - * various device information of the NFTL partition and Bad Unit Table. Update - * the ReplUnitTable[] table according to the Bad Unit Table. ReplUnitTable[] - * is used for management of Erase Unit in other routines in nftl.c and nftlmount.c - */ -static int find_boot_record(struct NFTLrecord *nftl) -{ - struct nftl_uci1 h1; - unsigned int block, boot_record_count = 0; - size_t retlen; - u8 buf[SECTORSIZE]; - struct NFTLMediaHeader *mh = &nftl->MediaHdr; - struct mtd_info *mtd = nftl->mbd.mtd; - unsigned int i; - - /* Assume logical EraseSize == physical erasesize for starting the scan. - We'll sort it out later if we find a MediaHeader which says otherwise */ - /* Actually, we won't. The new DiskOnChip driver has already scanned - the MediaHeader and adjusted the virtual erasesize it presents in - the mtd device accordingly. We could even get rid of - nftl->EraseSize if there were any point in doing so. */ - nftl->EraseSize = nftl->mbd.mtd->erasesize; - nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize; - - nftl->MediaUnit = BLOCK_NIL; - nftl->SpareMediaUnit = BLOCK_NIL; - - /* search for a valid boot record */ - for (block = 0; block < nftl->nb_blocks; block++) { - int ret; - - /* Check for ANAND header first. Then can whinge if it's found but later - checks fail */ - ret = mtd_read(mtd, block * nftl->EraseSize, SECTORSIZE, - &retlen, buf); - /* We ignore ret in case the ECC of the MediaHeader is invalid - (which is apparently acceptable) */ - if (retlen != SECTORSIZE) { - static int warncount = 5; - - if (warncount) { - printk(KERN_WARNING "Block read at 0x%x of mtd%d failed: %d\n", - block * nftl->EraseSize, nftl->mbd.mtd->index, ret); - if (!--warncount) - printk(KERN_WARNING "Further failures for this block will not be printed\n"); - } - continue; - } - - if (retlen < 6 || memcmp(buf, "ANAND", 6)) { - /* ANAND\0 not found. Continue */ -#if 0 - printk(KERN_DEBUG "ANAND header not found at 0x%x in mtd%d\n", - block * nftl->EraseSize, nftl->mbd.mtd->index); -#endif - continue; - } - - /* To be safer with BIOS, also use erase mark as discriminant */ - if ((ret = nftl_read_oob(mtd, block * nftl->EraseSize + - SECTORSIZE + 8, 8, &retlen, - (char *)&h1) < 0)) { - printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n", - block * nftl->EraseSize, nftl->mbd.mtd->index, ret); - continue; - } - -#if 0 /* Some people seem to have devices without ECC or erase marks - on the Media Header blocks. There are enough other sanity - checks in here that we can probably do without it. - */ - if (le16_to_cpu(h1.EraseMark | h1.EraseMark1) != ERASE_MARK) { - printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but erase mark not present (0x%04x,0x%04x instead)\n", - block * nftl->EraseSize, nftl->mbd.mtd->index, - le16_to_cpu(h1.EraseMark), le16_to_cpu(h1.EraseMark1)); - continue; - } - - /* Finally reread to check ECC */ - if ((ret = mtd->read(mtd, block * nftl->EraseSize, SECTORSIZE, - &retlen, buf) < 0)) { - printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n", - block * nftl->EraseSize, nftl->mbd.mtd->index, ret); - continue; - } - - /* Paranoia. Check the ANAND header is still there after the ECC read */ - if (memcmp(buf, "ANAND", 6)) { - printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but went away on reread!\n", - block * nftl->EraseSize, nftl->mbd.mtd->index); - printk(KERN_NOTICE "New data are: %02x %02x %02x %02x %02x %02x\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); - continue; - } -#endif - /* OK, we like it. */ - - if (boot_record_count) { - /* We've already processed one. So we just check if - this one is the same as the first one we found */ - if (memcmp(mh, buf, sizeof(struct NFTLMediaHeader))) { - printk(KERN_NOTICE "NFTL Media Headers at 0x%x and 0x%x disagree.\n", - nftl->MediaUnit * nftl->EraseSize, block * nftl->EraseSize); - /* if (debug) Print both side by side */ - if (boot_record_count < 2) { - /* We haven't yet seen two real ones */ - return -1; - } - continue; - } - if (boot_record_count == 1) - nftl->SpareMediaUnit = block; - - /* Mark this boot record (NFTL MediaHeader) block as reserved */ - nftl->ReplUnitTable[block] = BLOCK_RESERVED; - - - boot_record_count++; - continue; - } - - /* This is the first we've seen. Copy the media header structure into place */ - memcpy(mh, buf, sizeof(struct NFTLMediaHeader)); - - /* Do some sanity checks on it */ -#if 0 -The new DiskOnChip driver scans the MediaHeader itself, and presents a virtual -erasesize based on UnitSizeFactor. So the erasesize we read from the mtd -device is already correct. - if (mh->UnitSizeFactor == 0) { - printk(KERN_NOTICE "NFTL: UnitSizeFactor 0x00 detected. This violates the spec but we think we know what it means...\n"); - } else if (mh->UnitSizeFactor < 0xfc) { - printk(KERN_NOTICE "Sorry, we don't support UnitSizeFactor 0x%02x\n", - mh->UnitSizeFactor); - return -1; - } else if (mh->UnitSizeFactor != 0xff) { - printk(KERN_NOTICE "WARNING: Support for NFTL with UnitSizeFactor 0x%02x is experimental\n", - mh->UnitSizeFactor); - nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor); - nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize; - } -#endif - nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN); - if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) { - printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n"); - printk(KERN_NOTICE "nb_boot_blocks (%d) + 2 > nb_blocks (%d)\n", - nftl->nb_boot_blocks, nftl->nb_blocks); - return -1; - } - - nftl->numvunits = le32_to_cpu(mh->FormattedSize) / nftl->EraseSize; - if (nftl->numvunits > (nftl->nb_blocks - nftl->nb_boot_blocks - 2)) { - printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n"); - printk(KERN_NOTICE "numvunits (%d) > nb_blocks (%d) - nb_boot_blocks(%d) - 2\n", - nftl->numvunits, nftl->nb_blocks, nftl->nb_boot_blocks); - return -1; - } - - nftl->mbd.size = nftl->numvunits * (nftl->EraseSize / SECTORSIZE); - - /* If we're not using the last sectors in the device for some reason, - reduce nb_blocks accordingly so we forget they're there */ - nftl->nb_blocks = le16_to_cpu(mh->NumEraseUnits) + le16_to_cpu(mh->FirstPhysicalEUN); - - /* XXX: will be suppressed */ - nftl->lastEUN = nftl->nb_blocks - 1; - - /* memory alloc */ - nftl->EUNtable = kmalloc(nftl->nb_blocks * sizeof(u16), GFP_KERNEL); - if (!nftl->EUNtable) { - printk(KERN_NOTICE "NFTL: allocation of EUNtable failed\n"); - return -ENOMEM; - } - - nftl->ReplUnitTable = kmalloc(nftl->nb_blocks * sizeof(u16), GFP_KERNEL); - if (!nftl->ReplUnitTable) { - kfree(nftl->EUNtable); - printk(KERN_NOTICE "NFTL: allocation of ReplUnitTable failed\n"); - return -ENOMEM; - } - - /* mark the bios blocks (blocks before NFTL MediaHeader) as reserved */ - for (i = 0; i < nftl->nb_boot_blocks; i++) - nftl->ReplUnitTable[i] = BLOCK_RESERVED; - /* mark all remaining blocks as potentially containing data */ - for (; i < nftl->nb_blocks; i++) { - nftl->ReplUnitTable[i] = BLOCK_NOTEXPLORED; - } - - /* Mark this boot record (NFTL MediaHeader) block as reserved */ - nftl->ReplUnitTable[block] = BLOCK_RESERVED; - - /* read the Bad Erase Unit Table and modify ReplUnitTable[] accordingly */ - for (i = 0; i < nftl->nb_blocks; i++) { -#if 0 -The new DiskOnChip driver already scanned the bad block table. Just query it. - if ((i & (SECTORSIZE - 1)) == 0) { - /* read one sector for every SECTORSIZE of blocks */ - if ((ret = mtd->read(nftl->mbd.mtd, block * nftl->EraseSize + - i + SECTORSIZE, SECTORSIZE, &retlen, - buf)) < 0) { - printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n", - ret); - kfree(nftl->ReplUnitTable); - kfree(nftl->EUNtable); - return -1; - } - } - /* mark the Bad Erase Unit as RESERVED in ReplUnitTable */ - if (buf[i & (SECTORSIZE - 1)] != 0xff) - nftl->ReplUnitTable[i] = BLOCK_RESERVED; -#endif - if (mtd_block_isbad(nftl->mbd.mtd, - i * nftl->EraseSize)) - nftl->ReplUnitTable[i] = BLOCK_RESERVED; - } - - nftl->MediaUnit = block; - boot_record_count++; - - } /* foreach (block) */ - - return boot_record_count?0:-1; -} - -static int memcmpb(void *a, int c, int n) -{ - int i; - for (i = 0; i < n; i++) { - if (c != ((unsigned char *)a)[i]) - return 1; - } - return 0; -} - -/* check_free_sector: check if a free sector is actually FREE, i.e. All 0xff in data and oob area */ -static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int len, - int check_oob) -{ - u8 buf[SECTORSIZE + nftl->mbd.mtd->oobsize]; - struct mtd_info *mtd = nftl->mbd.mtd; - size_t retlen; - int i; - - for (i = 0; i < len; i += SECTORSIZE) { - if (mtd_read(mtd, address, SECTORSIZE, &retlen, buf)) - return -1; - if (memcmpb(buf, 0xff, SECTORSIZE) != 0) - return -1; - - if (check_oob) { - if(nftl_read_oob(mtd, address, mtd->oobsize, - &retlen, &buf[SECTORSIZE]) < 0) - return -1; - if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0) - return -1; - } - address += SECTORSIZE; - } - - return 0; -} - -/* NFTL_format: format a Erase Unit by erasing ALL Erase Zones in the Erase Unit and - * Update NFTL metadata. Each erase operation is checked with check_free_sectors - * - * Return: 0 when succeed, -1 on error. - * - * ToDo: 1. Is it necessary to check_free_sector after erasing ?? - */ -int NFTL_formatblock(struct NFTLrecord *nftl, int block) -{ - size_t retlen; - unsigned int nb_erases, erase_mark; - struct nftl_uci1 uci; - struct erase_info *instr = &nftl->instr; - struct mtd_info *mtd = nftl->mbd.mtd; - - /* Read the Unit Control Information #1 for Wear-Leveling */ - if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, - 8, &retlen, (char *)&uci) < 0) - goto default_uci1; - - erase_mark = le16_to_cpu ((uci.EraseMark | uci.EraseMark1)); - if (erase_mark != ERASE_MARK) { - default_uci1: - uci.EraseMark = cpu_to_le16(ERASE_MARK); - uci.EraseMark1 = cpu_to_le16(ERASE_MARK); - uci.WearInfo = cpu_to_le32(0); - } - - memset(instr, 0, sizeof(struct erase_info)); - - /* XXX: use async erase interface, XXX: test return code */ - instr->mtd = nftl->mbd.mtd; - instr->addr = block * nftl->EraseSize; - instr->len = nftl->EraseSize; - mtd_erase(mtd, instr); - - if (instr->state == MTD_ERASE_FAILED) { - printk("Error while formatting block %d\n", block); - goto fail; - } - - /* increase and write Wear-Leveling info */ - nb_erases = le32_to_cpu(uci.WearInfo); - nb_erases++; - - /* wrap (almost impossible with current flash) or free block */ - if (nb_erases == 0) - nb_erases = 1; - - /* check the "freeness" of Erase Unit before updating metadata - * FixMe: is this check really necessary ? since we have check the - * return code after the erase operation. */ - if (check_free_sectors(nftl, instr->addr, nftl->EraseSize, 1) != 0) - goto fail; - - uci.WearInfo = le32_to_cpu(nb_erases); - if (nftl_write_oob(mtd, block * nftl->EraseSize + SECTORSIZE + - 8, 8, &retlen, (char *)&uci) < 0) - goto fail; - return 0; -fail: - /* could not format, update the bad block table (caller is responsible - for setting the ReplUnitTable to BLOCK_RESERVED on failure) */ - mtd_block_markbad(nftl->mbd.mtd, instr->addr); - return -1; -} - -/* check_sectors_in_chain: Check that each sector of a Virtual Unit Chain is correct. - * Mark as 'IGNORE' each incorrect sector. This check is only done if the chain - * was being folded when NFTL was interrupted. - * - * The check_free_sectors in this function is necessary. There is a possible - * situation that after writing the Data area, the Block Control Information is - * not updated according (due to power failure or something) which leaves the block - * in an inconsistent state. So we have to check if a block is really FREE in this - * case. */ -static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_block) -{ - struct mtd_info *mtd = nftl->mbd.mtd; - unsigned int block, i, status; - struct nftl_bci bci; - int sectors_per_block; - size_t retlen; - - sectors_per_block = nftl->EraseSize / SECTORSIZE; - block = first_block; - for (;;) { - for (i = 0; i < sectors_per_block; i++) { - if (nftl_read_oob(mtd, - block * nftl->EraseSize + i * SECTORSIZE, - 8, &retlen, (char *)&bci) < 0) - status = SECTOR_IGNORE; - else - status = bci.Status | bci.Status1; - - switch(status) { - case SECTOR_FREE: - /* verify that the sector is really free. If not, mark - as ignore */ - if (memcmpb(&bci, 0xff, 8) != 0 || - check_free_sectors(nftl, block * nftl->EraseSize + i * SECTORSIZE, - SECTORSIZE, 0) != 0) { - printk("Incorrect free sector %d in block %d: " - "marking it as ignored\n", - i, block); - - /* sector not free actually : mark it as SECTOR_IGNORE */ - bci.Status = SECTOR_IGNORE; - bci.Status1 = SECTOR_IGNORE; - nftl_write_oob(mtd, block * - nftl->EraseSize + - i * SECTORSIZE, 8, - &retlen, (char *)&bci); - } - break; - default: - break; - } - } - - /* proceed to next Erase Unit on the chain */ - block = nftl->ReplUnitTable[block]; - if (!(block == BLOCK_NIL || block < nftl->nb_blocks)) - printk("incorrect ReplUnitTable[] : %d\n", block); - if (block == BLOCK_NIL || block >= nftl->nb_blocks) - break; - } -} - -/* calc_chain_length: Walk through a Virtual Unit Chain and estimate chain length */ -static int calc_chain_length(struct NFTLrecord *nftl, unsigned int first_block) -{ - unsigned int length = 0, block = first_block; - - for (;;) { - length++; - /* avoid infinite loops, although this is guaranteed not to - happen because of the previous checks */ - if (length >= nftl->nb_blocks) { - printk("nftl: length too long %d !\n", length); - break; - } - - block = nftl->ReplUnitTable[block]; - if (!(block == BLOCK_NIL || block < nftl->nb_blocks)) - printk("incorrect ReplUnitTable[] : %d\n", block); - if (block == BLOCK_NIL || block >= nftl->nb_blocks) - break; - } - return length; -} - -/* format_chain: Format an invalid Virtual Unit chain. It frees all the Erase Units in a - * Virtual Unit Chain, i.e. all the units are disconnected. - * - * It is not strictly correct to begin from the first block of the chain because - * if we stop the code, we may see again a valid chain if there was a first_block - * flag in a block inside it. But is it really a problem ? - * - * FixMe: Figure out what the last statement means. What if power failure when we are - * in the for (;;) loop formatting blocks ?? - */ -static void format_chain(struct NFTLrecord *nftl, unsigned int first_block) -{ - unsigned int block = first_block, block1; - - printk("Formatting chain at block %d\n", first_block); - - for (;;) { - block1 = nftl->ReplUnitTable[block]; - - printk("Formatting block %d\n", block); - if (NFTL_formatblock(nftl, block) < 0) { - /* cannot format !!!! Mark it as Bad Unit */ - nftl->ReplUnitTable[block] = BLOCK_RESERVED; - } else { - nftl->ReplUnitTable[block] = BLOCK_FREE; - } - - /* goto next block on the chain */ - block = block1; - - if (!(block == BLOCK_NIL || block < nftl->nb_blocks)) - printk("incorrect ReplUnitTable[] : %d\n", block); - if (block == BLOCK_NIL || block >= nftl->nb_blocks) - break; - } -} - -/* check_and_mark_free_block: Verify that a block is free in the NFTL sense (valid erase mark) or - * totally free (only 0xff). - * - * Definition: Free Erase Unit -- A properly erased/formatted Free Erase Unit should have meet the - * following criteria: - * 1. */ -static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) -{ - struct mtd_info *mtd = nftl->mbd.mtd; - struct nftl_uci1 h1; - unsigned int erase_mark; - size_t retlen; - - /* check erase mark. */ - if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, - &retlen, (char *)&h1) < 0) - return -1; - - erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1)); - if (erase_mark != ERASE_MARK) { - /* if no erase mark, the block must be totally free. This is - possible in two cases : empty filesystem or interrupted erase (very unlikely) */ - if (check_free_sectors (nftl, block * nftl->EraseSize, nftl->EraseSize, 1) != 0) - return -1; - - /* free block : write erase mark */ - h1.EraseMark = cpu_to_le16(ERASE_MARK); - h1.EraseMark1 = cpu_to_le16(ERASE_MARK); - h1.WearInfo = cpu_to_le32(0); - if (nftl_write_oob(mtd, - block * nftl->EraseSize + SECTORSIZE + 8, 8, - &retlen, (char *)&h1) < 0) - return -1; - } else { -#if 0 - /* if erase mark present, need to skip it when doing check */ - for (i = 0; i < nftl->EraseSize; i += SECTORSIZE) { - /* check free sector */ - if (check_free_sectors (nftl, block * nftl->EraseSize + i, - SECTORSIZE, 0) != 0) - return -1; - - if (nftl_read_oob(mtd, block * nftl->EraseSize + i, - 16, &retlen, buf) < 0) - return -1; - if (i == SECTORSIZE) { - /* skip erase mark */ - if (memcmpb(buf, 0xff, 8)) - return -1; - } else { - if (memcmpb(buf, 0xff, 16)) - return -1; - } - } -#endif - } - - return 0; -} - -/* get_fold_mark: Read fold mark from Unit Control Information #2, we use FOLD_MARK_IN_PROGRESS - * to indicate that we are in the progression of a Virtual Unit Chain folding. If the UCI #2 - * is FOLD_MARK_IN_PROGRESS when mounting the NFTL, the (previous) folding process is interrupted - * for some reason. A clean up/check of the VUC is necessary in this case. - * - * WARNING: return 0 if read error - */ -static int get_fold_mark(struct NFTLrecord *nftl, unsigned int block) -{ - struct mtd_info *mtd = nftl->mbd.mtd; - struct nftl_uci2 uci; - size_t retlen; - - if (nftl_read_oob(mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8, - 8, &retlen, (char *)&uci) < 0) - return 0; - - return le16_to_cpu((uci.FoldMark | uci.FoldMark1)); -} - -int NFTL_mount(struct NFTLrecord *s) -{ - int i; - unsigned int first_logical_block, logical_block, rep_block, nb_erases, erase_mark; - unsigned int block, first_block, is_first_block; - int chain_length, do_format_chain; - struct nftl_uci0 h0; - struct nftl_uci1 h1; - struct mtd_info *mtd = s->mbd.mtd; - size_t retlen; - - /* search for NFTL MediaHeader and Spare NFTL Media Header */ - if (find_boot_record(s) < 0) { - printk("Could not find valid boot record\n"); - return -1; - } - - /* init the logical to physical table */ - for (i = 0; i < s->nb_blocks; i++) { - s->EUNtable[i] = BLOCK_NIL; - } - - /* first pass : explore each block chain */ - first_logical_block = 0; - for (first_block = 0; first_block < s->nb_blocks; first_block++) { - /* if the block was not already explored, we can look at it */ - if (s->ReplUnitTable[first_block] == BLOCK_NOTEXPLORED) { - block = first_block; - chain_length = 0; - do_format_chain = 0; - - for (;;) { - /* read the block header. If error, we format the chain */ - if (nftl_read_oob(mtd, - block * s->EraseSize + 8, 8, - &retlen, (char *)&h0) < 0 || - nftl_read_oob(mtd, - block * s->EraseSize + - SECTORSIZE + 8, 8, - &retlen, (char *)&h1) < 0) { - s->ReplUnitTable[block] = BLOCK_NIL; - do_format_chain = 1; - break; - } - - logical_block = le16_to_cpu ((h0.VirtUnitNum | h0.SpareVirtUnitNum)); - rep_block = le16_to_cpu ((h0.ReplUnitNum | h0.SpareReplUnitNum)); - nb_erases = le32_to_cpu (h1.WearInfo); - erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1)); - - is_first_block = !(logical_block >> 15); - logical_block = logical_block & 0x7fff; - - /* invalid/free block test */ - if (erase_mark != ERASE_MARK || logical_block >= s->nb_blocks) { - if (chain_length == 0) { - /* if not currently in a chain, we can handle it safely */ - if (check_and_mark_free_block(s, block) < 0) { - /* not really free: format it */ - printk("Formatting block %d\n", block); - if (NFTL_formatblock(s, block) < 0) { - /* could not format: reserve the block */ - s->ReplUnitTable[block] = BLOCK_RESERVED; - } else { - s->ReplUnitTable[block] = BLOCK_FREE; - } - } else { - /* free block: mark it */ - s->ReplUnitTable[block] = BLOCK_FREE; - } - /* directly examine the next block. */ - goto examine_ReplUnitTable; - } else { - /* the block was in a chain : this is bad. We - must format all the chain */ - printk("Block %d: free but referenced in chain %d\n", - block, first_block); - s->ReplUnitTable[block] = BLOCK_NIL; - do_format_chain = 1; - break; - } - } - - /* we accept only first blocks here */ - if (chain_length == 0) { - /* this block is not the first block in chain : - ignore it, it will be included in a chain - later, or marked as not explored */ - if (!is_first_block) - goto examine_ReplUnitTable; - first_logical_block = logical_block; - } else { - if (logical_block != first_logical_block) { - printk("Block %d: incorrect logical block: %d expected: %d\n", - block, logical_block, first_logical_block); - /* the chain is incorrect : we must format it, - but we need to read it completely */ - do_format_chain = 1; - } - if (is_first_block) { - /* we accept that a block is marked as first - block while being last block in a chain - only if the chain is being folded */ - if (get_fold_mark(s, block) != FOLD_MARK_IN_PROGRESS || - rep_block != 0xffff) { - printk("Block %d: incorrectly marked as first block in chain\n", - block); - /* the chain is incorrect : we must format it, - but we need to read it completely */ - do_format_chain = 1; - } else { - printk("Block %d: folding in progress - ignoring first block flag\n", - block); - } - } - } - chain_length++; - if (rep_block == 0xffff) { - /* no more blocks after */ - s->ReplUnitTable[block] = BLOCK_NIL; - break; - } else if (rep_block >= s->nb_blocks) { - printk("Block %d: referencing invalid block %d\n", - block, rep_block); - do_format_chain = 1; - s->ReplUnitTable[block] = BLOCK_NIL; - break; - } else if (s->ReplUnitTable[rep_block] != BLOCK_NOTEXPLORED) { - /* same problem as previous 'is_first_block' test: - we accept that the last block of a chain has - the first_block flag set if folding is in - progress. We handle here the case where the - last block appeared first */ - if (s->ReplUnitTable[rep_block] == BLOCK_NIL && - s->EUNtable[first_logical_block] == rep_block && - get_fold_mark(s, first_block) == FOLD_MARK_IN_PROGRESS) { - /* EUNtable[] will be set after */ - printk("Block %d: folding in progress - ignoring first block flag\n", - rep_block); - s->ReplUnitTable[block] = rep_block; - s->EUNtable[first_logical_block] = BLOCK_NIL; - } else { - printk("Block %d: referencing block %d already in another chain\n", - block, rep_block); - /* XXX: should handle correctly fold in progress chains */ - do_format_chain = 1; - s->ReplUnitTable[block] = BLOCK_NIL; - } - break; - } else { - /* this is OK */ - s->ReplUnitTable[block] = rep_block; - block = rep_block; - } - } - - /* the chain was completely explored. Now we can decide - what to do with it */ - if (do_format_chain) { - /* invalid chain : format it */ - format_chain(s, first_block); - } else { - unsigned int first_block1, chain_to_format, chain_length1; - int fold_mark; - - /* valid chain : get foldmark */ - fold_mark = get_fold_mark(s, first_block); - if (fold_mark == 0) { - /* cannot get foldmark : format the chain */ - printk("Could read foldmark at block %d\n", first_block); - format_chain(s, first_block); - } else { - if (fold_mark == FOLD_MARK_IN_PROGRESS) - check_sectors_in_chain(s, first_block); - - /* now handle the case where we find two chains at the - same virtual address : we select the longer one, - because the shorter one is the one which was being - folded if the folding was not done in place */ - first_block1 = s->EUNtable[first_logical_block]; - if (first_block1 != BLOCK_NIL) { - /* XXX: what to do if same length ? */ - chain_length1 = calc_chain_length(s, first_block1); - printk("Two chains at blocks %d (len=%d) and %d (len=%d)\n", - first_block1, chain_length1, first_block, chain_length); - - if (chain_length >= chain_length1) { - chain_to_format = first_block1; - s->EUNtable[first_logical_block] = first_block; - } else { - chain_to_format = first_block; - } - format_chain(s, chain_to_format); - } else { - s->EUNtable[first_logical_block] = first_block; - } - } - } - } - examine_ReplUnitTable:; - } - - /* second pass to format unreferenced blocks and init free block count */ - s->numfreeEUNs = 0; - s->LastFreeEUN = le16_to_cpu(s->MediaHdr.FirstPhysicalEUN); - - for (block = 0; block < s->nb_blocks; block++) { - if (s->ReplUnitTable[block] == BLOCK_NOTEXPLORED) { - printk("Unreferenced block %d, formatting it\n", block); - if (NFTL_formatblock(s, block) < 0) - s->ReplUnitTable[block] = BLOCK_RESERVED; - else - s->ReplUnitTable[block] = BLOCK_FREE; - } - if (s->ReplUnitTable[block] == BLOCK_FREE) { - s->numfreeEUNs++; - s->LastFreeEUN = block; - } - } - - return 0; -}