From patchwork Mon May 7 08:39:16 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thilo Fromm X-Patchwork-Id: 157281 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 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 82A82B6F9A for ; Mon, 7 May 2012 19:18:37 +1000 (EST) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1SRK3e-0004hV-Fr; Mon, 07 May 2012 09:16:42 +0000 Received: from thilo-fromm.de ([212.40.171.30]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1SRK3Z-0004gg-Ov for linux-mtd@lists.infradead.org; Mon, 07 May 2012 09:16:39 +0000 Received: from bfg9000.internal.dresearch-fe.de (pd95cb146.dip0.t-ipconnect.de [217.92.177.70]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) (Authenticated sender: t-lo) by thilo-fromm.de (Postfix) with ESMTPSA id 1ED762C8183; Mon, 7 May 2012 10:39:31 +0200 (CEST) From: Thilo Fromm To: linux-mtd@lists.infradead.org Subject: [PATCH 1/1] Blockrom: badblock-free RO MTD blockdev access Date: Mon, 7 May 2012 10:39:16 +0200 Message-Id: <1336379956-17052-1-git-send-email-github@thilo-fromm.de> X-Mailer: git-send-email 1.7.9.5 X-Spam-Note: CRM114 invocation failed X-Spam-Score: -1.9 (-) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-1.9 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 T_RP_MATCHES_RCVD Envelope sender domain matches handover relay domain 0.0 T_FRT_CONTACT BODY: ReplaceTags: Contact -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: Thilo Fromm X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-mtd-bounces@lists.infradead.org Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org The blockrom MTD driver provides bad block free read access to MTDs via /dev/blockromX device files. Bad blocks are automatically skipped upon read, and reading continues with the next good block. This allows for read-only filesystems to exist in MTDs with bad blocks: usually the baddies are skipped when writing the filesystem into MTD by the tool performing the write (e.g. nandwrite). To successfully mount these filesystems, however, you will need a translation layer that skips bad blocks upon read as well. blockrom is such a translation layer. Signed-off-by: Thilo Fromm --- drivers/mtd/Kconfig | 14 +++ drivers/mtd/Makefile | 1 + drivers/mtd/blockrom.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 249 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/blockrom.c diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 1e2cbf5..bbcbabc 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -226,6 +226,20 @@ config MTD_BLOCK_RO You do not need this option for use with the DiskOnChip devices. For those, enable NFTL support (CONFIG_NFTL) instead. +config MTD_BLOCK_ROM_BBFREE + tristate "Bad-block-skipiping readonly access to MTD devices" + depends on MTD_BLOCK!=y && MTD + help + The blockrom MTD driver provides bad block free read access to MTDs via + /dev/blockromX device files. Bad blocks are automatically skipped upon read, + and reading continues with the next block. + + This allows for read-only filesystems to exist in MTDs with bad blocks: + usually the baddies are skipped when writing the filesystem into MTD by + the tool performing the write (e.g. nandwrite). To successfully mount + these filesystems, however, you will need a translation layer that skips + bad blocks upon read as well. blockrom is such a translation layer. + config FTL tristate "FTL (Flash Translation Layer) support" depends on BLOCK diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 760abc5..2337097 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_MTD_CHAR) += mtdchar.o obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o obj-$(CONFIG_MTD_BLOCK) += mtdblock.o obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o +obj-$(CONFIG_MTD_BLOCK_ROM_BBFREE) += blockrom.o obj-$(CONFIG_FTL) += ftl.o obj-$(CONFIG_NFTL) += nftl.o obj-$(CONFIG_INFTL) += inftl.o diff --git a/drivers/mtd/blockrom.c b/drivers/mtd/blockrom.c new file mode 100644 index 0000000..b25496c --- /dev/null +++ b/drivers/mtd/blockrom.c @@ -0,0 +1,234 @@ +/* + * Readonly Block Device Layer Over MTD + * + * (C) 2006 Baydarov Konstantin + * Pantelis Antoniou + * David Woodhouse + * (C) 2012 DResearch Fahrzeugelektronik GmbH, + * Thilo Fromm + * + * 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. + * + * + * The blockrom MTD driver provides bad block free read access to MTDs via + * /dev/blockromX device files. Bad blocks are automatically skipped upon read, + * and reading continues with the next good block. + * + * This allows for read-only filesystems to exist in MTDs with bad blocks: + * usually the baddies are skipped when writing the filesystem into MTD by + * the tool performing the write (e.g. nandwrite). To successfully mount + * these filesystems, however, you will need a translation layer that skips + * bad blocks upon read as well. blockrom is such a translation layer. + */ + +#include +#include +#include +#include + +struct mtd_block_map { + struct mtd_blktrans_dev dev; + /* block map for RO */ + uint32_t *block_map; + uint32_t blocks_total; + uint32_t blocks_bad; + uint32_t blocks_spare; + uint32_t blocks_mgmt; +}; + +/* + * private functions + */ + +static size_t user_blocks(struct mtd_block_map * map) +{ + return map->blocks_total + - map->blocks_bad + - map->blocks_spare + - map->blocks_mgmt; +} + +static void free_map(struct mtd_block_map * map) +{ + if (map) { + if (map->block_map) + kfree (map->block_map); + kfree (map); + } +} + +static uint32_t map_block(struct mtd_block_map * map, int32_t block) +{ + uint32_t mapped_start = map->block_map[ block ], + mapped; + + for (mapped = mapped_start; mapped < map->blocks_total; mapped++) + if (0 == map->dev.mtd->block_isbad( + map->dev.mtd, mapped * map->dev.mtd->erasesize)) + break; + + /* store mapping of the current source block */ + map->block_map[ block ] = mapped; + + /* set next map entry so we can continue where we left */ + if (block + 1 < map->blocks_total) + map->block_map[ block + 1 ] = mapped + 1; + + return mapped; +} + +static void map_all_blocks(struct mtd_block_map * map, struct mtd_info * mtd) +{ + unsigned int block, + expect_mapped = 0; + + for (block=0; block < map->blocks_total; block++) { + uint32_t mapped; + + mapped = map_block (map, block); + map->blocks_bad += (mapped - expect_mapped); + + expect_mapped = mapped + 1; + } +} + +static struct mtd_block_map * init_map(struct mtd_info * mtd) +{ + struct mtd_block_map *map = kzalloc(sizeof(*map), GFP_KERNEL); + + if (map == NULL) + return NULL; + + map->dev.mtd = mtd; + map->dev.devnum = mtd->index; + + map->blocks_total = div_u64(mtd->size, mtd->erasesize); + map->block_map = kzalloc(sizeof(*map->block_map) * map->blocks_total, + GFP_KERNEL); + if (map->block_map == NULL){ + free_map(map); + return NULL; + } + + /* init first map entry, then fill mapping table */ + map->block_map[0] = 0; + map_all_blocks(map, mtd); + + map->dev.size = user_blocks(map) * (mtd->erasesize / 512); + map->dev.readonly = 1; + + return map; +} + +static void print_mtd_info(struct mtd_block_map * map) +{ + printk(KERN_INFO "blockrom%d: %6d KiB; EBs %6d user, " + "%6d spare, %6d mgmt, %6d bad, %6d toal\n", + map->dev.mtd->index, + user_blocks(map) * map->dev.mtd->erasesize / 1024, + user_blocks(map), + map->blocks_spare, map->blocks_mgmt, map->blocks_bad, map->blocks_total); +} + +/* + * mtd_blktrans_dev interface implementation + */ + +static int blockrom_readsect(struct mtd_blktrans_dev *dev, + unsigned long block, char *buf) +{ + size_t retlen; + uint32_t flash_block; + struct mtd_block_map *map = container_of(dev, struct mtd_block_map, dev); + loff_t addr, offs; + + /* convert HDD block no. to flash block no. */ + flash_block = (block * 512) / map->dev.mtd->erasesize; + + if (flash_block >= map->blocks_total) { + printk(KERN_ERR "blockrom%d: " + "trying to access beyond end of device.", + map->dev.devnum); + return -ENXIO; + } + + offs = map->block_map[ flash_block ]; + offs *= map->dev.mtd->erasesize; + + addr = offs | ((block * 512) & (map->dev.mtd->erasesize - 1)); + + return dev->mtd->read(dev->mtd, addr, 512, &retlen, buf); +} + +static int blockrom_writesect(struct mtd_blktrans_dev *dev, + unsigned long block, char *buf) +{ + size_t retlen; + + if (dev->mtd->write(dev->mtd, (block * 512), 512, &retlen, buf)) + return 1; + + return 0; +} + +static void blockrom_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) +{ + struct mtd_block_map * map; + + /* if no bad block checking is possible we don't handle the MTD */ + if (mtd->block_isbad == NULL) + return; + + map = init_map(mtd); + + if (NULL == map) + return; + + map->dev.tr = tr; + + if (add_mtd_blktrans_dev(&(map->dev))) + free_map(map); + else + print_mtd_info(map); +} + +static void blockrom_remove_dev(struct mtd_blktrans_dev *dev) +{ + struct mtd_block_map *map = container_of(dev, struct mtd_block_map, dev); + + del_mtd_blktrans_dev(dev); + free_map(map); +} + +static struct mtd_blktrans_ops blockrom_tr = { + .name = "blockrom", + .major = 258, + .part_bits = 0, + .blksize = 512, + .readsect = blockrom_readsect, + .writesect = blockrom_writesect, + .add_mtd = blockrom_add_mtd, + .remove_dev = blockrom_remove_dev, + .owner = THIS_MODULE, +}; + +static int __init blockrom_init(void) +{ + return register_mtd_blktrans(&blockrom_tr); +} + +static void __exit blockrom_exit(void) +{ + deregister_mtd_blktrans(&blockrom_tr); +} + +module_init(blockrom_init); +module_exit(blockrom_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Baydarov Konstantin "); +MODULE_AUTHOR("Thilo Fromm "); +MODULE_DESCRIPTION("Readonly Bad-Block Skipping Block Device Translation Layer");