All of lore.kernel.org
 help / color / mirror / Atom feed
From: Thilo Fromm <github@thilo-fromm.de>
To: linux-mtd@lists.infradead.org
Cc: Thilo Fromm <github@thilo-fromm.de>
Subject: [PATCH 1/1] Blockrom: badblock-free RO MTD blockdev access
Date: Mon,  7 May 2012 10:39:16 +0200	[thread overview]
Message-ID: <1336379956-17052-1-git-send-email-github@thilo-fromm.de> (raw)

	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@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@dev.rtsoft.ru>
+ *           Pantelis Antoniou <panto@intracom.gr>
+ *           David Woodhouse <dwmw2@infradead.org>
+ *  (C) 2012 DResearch Fahrzeugelektronik GmbH,
+ *           Thilo Fromm <kontakt@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@dev.rtsoft.ru>");
+MODULE_AUTHOR("Thilo Fromm <kontakt@thilo-fromm.de>");
+MODULE_DESCRIPTION("Readonly Bad-Block Skipping Block Device Translation Layer");
-- 
1.7.7

             reply	other threads:[~2012-05-07  9:16 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-05-07  8:39 Thilo Fromm [this message]
2012-05-07  9:57 ` [PATCH 1/1] Blockrom: badblock-free RO MTD blockdev access Thilo Fromm

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1336379956-17052-1-git-send-email-github@thilo-fromm.de \
    --to=github@thilo-fromm.de \
    --cc=linux-mtd@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.