[PATCH 1/1] Blockrom: badblock-free RO MTD blockdev access

Thilo Fromm github at thilo-fromm.de
Mon May 7 04:39:16 EDT 2012


	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 <github at thilo-fromm.de>
---
 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 <kbaidarov at dev.rtsoft.ru>
+ *           Pantelis Antoniou <panto at intracom.gr>
+ *           David Woodhouse <dwmw2 at infradead.org>
+ *  (C) 2012 DResearch Fahrzeugelektronik GmbH,
+ *           Thilo Fromm <kontakt at thilo-fromm.de>
+ *
+ *         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 <linux/init.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/blktrans.h>
+
+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 <kbaidarov at dev.rtsoft.ru>");
+MODULE_AUTHOR("Thilo Fromm <kontakt at thilo-fromm.de>");
+MODULE_DESCRIPTION("Readonly Bad-Block Skipping Block Device Translation Layer");
-- 
1.7.7




More information about the linux-mtd mailing list