Skip to contentSkip to conversation

Allow any filesystem on MTD Nand when Read Only

PATCH In review Pantelis Antoniou 4 participants 7 messages May 5, 2004 – May 6, 2004

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Allow any filesystem on MTD Nand when Read Only

Hello there.

The following patch allows you to have any filesystem over NAND when mounted read only.

The main drive for this was that I wanted to use CRAMFS over NAND flash since it has a much higher compression ratio than JFFS2.

How it works is pretty simple.

It's pretty reasonable assumption that bad blocks are created only when writting to NAND. So in the read-only case it is possible to skip over the bad blocks and offer a bad-block free block device to the upper layers. This allows us to utilize any filesystem when read-only.

In order to speed the mount operation the bad block detection is performed lazilly.

Using the patch I was able to mount as read-only CRAMFS/JFFS2 partitions which contained simulated bad-blocks.

Awaiting your comments...

Regards

Pantelis

drivers/mtd/chips/amd_flash.c +3 -0
diff -ruN mtd-original/drivers/mtd/chips/amd_flash.c mtd-work/drivers/mtd/chips/amd_flash.c
@@ -760,6 +760,9 @@
 	mtd->lock = amd_flash_lock;
 	mtd->unlock = amd_flash_unlock;
 
+	mtd->block_read = mtd->read;
+	mtd->block_write = mtd->write;
+
 	private = kmalloc(sizeof(*private) + (sizeof(struct flchip) *
 					      temp.numchips), GFP_KERNEL);
 	if (!private) {
drivers/mtd/chips/cfi_cmdset_0001.c +4 -0
diff -ruN mtd-original/drivers/mtd/chips/cfi_cmdset_0001.c mtd-work/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -307,6 +307,10 @@
 	mtd->unlock = cfi_intelext_unlock;
 	mtd->suspend = cfi_intelext_suspend;
 	mtd->resume = cfi_intelext_resume;
+
+	mtd->block_read = mtd->read;
+	mtd->block_write = mtd->write;
+
 	mtd->flags = MTD_CAP_NORFLASH;
 	map->fldrv = &cfi_intelext_chipdrv;
 	mtd->name = map->name;
drivers/mtd/chips/cfi_cmdset_0002.c +4 -0
diff -ruN mtd-original/drivers/mtd/chips/cfi_cmdset_0002.c mtd-work/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -367,6 +367,10 @@
 	mtd->sync = cfi_amdstd_sync;
 	mtd->suspend = cfi_amdstd_suspend;
 	mtd->resume = cfi_amdstd_resume;
+
+	mtd->block_read = mtd->read;
+	mtd->block_write = mtd->write;
+
 	mtd->flags = MTD_CAP_NORFLASH;
 	map->fldrv = &cfi_amdstd_chipdrv;
 	mtd->name = map->name;
drivers/mtd/chips/cfi_cmdset_0020.c +4 -0
diff -ruN mtd-original/drivers/mtd/chips/cfi_cmdset_0020.c mtd-work/drivers/mtd/chips/cfi_cmdset_0020.c
@@ -226,6 +226,10 @@
 	mtd->unlock = cfi_staa_unlock;
 	mtd->suspend = cfi_staa_suspend;
 	mtd->resume = cfi_staa_resume;
+
+	mtd->block_read = mtd->read;
+	mtd->block_write = mtd->write;
+
 	mtd->flags = MTD_CAP_NORFLASH;
 	mtd->flags |= MTD_ECC; /* FIXME: Not all STMicro flashes have this */
 	mtd->eccsize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */
drivers/mtd/chips/map_ram.c +3 -0
diff -ruN mtd-original/drivers/mtd/chips/map_ram.c mtd-work/drivers/mtd/chips/map_ram.c
@@ -76,6 +76,9 @@
  	while(mtd->size & (mtd->erasesize - 1))
 		mtd->erasesize >>= 1;
 
+	mtd->block_read = mtd->read;
+	mtd->block_write = mtd->write;
+
 	__module_get(THIS_MODULE);
 	return mtd;
 }
drivers/mtd/chips/map_rom.c +4 -0
diff -ruN mtd-original/drivers/mtd/chips/map_rom.c mtd-work/drivers/mtd/chips/map_rom.c
@@ -46,6 +46,10 @@
 	mtd->read = maprom_read;
 	mtd->write = maprom_write;
 	mtd->sync = maprom_nop;
+
+	mtd->block_read = mtd->read;
+	mtd->block_write = mtd->write;
+
 	mtd->flags = MTD_CAP_ROM;
 	mtd->erasesize = 131072;
  	while(mtd->size & (mtd->erasesize - 1))
drivers/mtd/chips/sharp.c +4 -0
diff -ruN mtd-original/drivers/mtd/chips/sharp.c mtd-work/drivers/mtd/chips/sharp.c
@@ -139,6 +139,10 @@
 	mtd->sync = sharp_sync;
 	mtd->suspend = sharp_suspend;
 	mtd->resume = sharp_resume;
+
+	mtd->block_read = mtd->read;
+	mtd->block_write = mtd->write;
+
 	mtd->flags = MTD_CAP_NORFLASH;
 	mtd->name = map->name;
 
drivers/mtd/devices/doc2000.c +3 -0
diff -ruN mtd-original/drivers/mtd/devices/doc2000.c mtd-work/drivers/mtd/devices/doc2000.c
@@ -605,6 +605,9 @@
 	mtd->write_oob = doc_write_oob;
 	mtd->sync = NULL;
 
+	mtd->block_read = mtd->read;
+	mtd->block_write = mtd->write;
+
 	this->totlen = 0;
 	this->numchips = 0;
 
drivers/mtd/devices/doc2001.c +3 -0
diff -ruN mtd-original/drivers/mtd/devices/doc2001.c mtd-work/drivers/mtd/devices/doc2001.c
@@ -380,6 +380,9 @@
 	mtd->write_oob = doc_write_oob;
 	mtd->sync = NULL;
 
+	mtd->block_read = mtd->read;
+	mtd->block_write = mtd->write;
+
 	this->totlen = 0;
 	this->numchips = 0;
 	this->curfloor = -1;
drivers/mtd/devices/doc2001plus.c +3 -0
diff -ruN mtd-original/drivers/mtd/devices/doc2001plus.c mtd-work/drivers/mtd/devices/doc2001plus.c
@@ -504,6 +504,9 @@
 	mtd->write_oob = doc_write_oob;
 	mtd->sync = NULL;
 
+	mtd->block_read = mtd->read;
+	mtd->block_write = mtd->write;
+
 	this->totlen = 0;
 	this->numchips = 0;
 	this->curfloor = -1;
drivers/mtd/devices/ms02-nv.c +3 -0
diff -ruN mtd-original/drivers/mtd/devices/ms02-nv.c mtd-work/drivers/mtd/devices/ms02-nv.c
@@ -226,6 +226,9 @@
 	mtd->read = ms02nv_read;
 	mtd->write = ms02nv_write;
 
+	mtd->block_read = mtd->read;
+	mtd->block_write = mtd->write;
+
 	ret = -EIO;
 	if (add_mtd_device(mtd)) {
 		printk(KERN_ERR
drivers/mtd/devices/mtdram.c +3 -0
diff -ruN mtd-original/drivers/mtd/devices/mtdram.c mtd-work/drivers/mtd/devices/mtdram.c
@@ -145,6 +145,9 @@
    mtd->read = ram_read;
    mtd->write = ram_write;
 
+   mtd->block_read = mtd->read;
+   mtd->block_write = mtd->write;
+
    if (add_mtd_device(mtd)) {
      return -EIO;
    }
drivers/mtd/mtdblock.c +5 -5
diff -ruN mtd-original/drivers/mtd/mtdblock.c mtd-work/drivers/mtd/mtdblock.c
@@ -84,7 +84,7 @@
 	 * Next, writhe data to flash.
 	 */
 
-	ret = MTD_WRITE (mtd, pos, len, &retlen, buf);
+	ret = MTD_BLOCK_WRITE (mtd, pos, len, &retlen, buf);
 	if (ret)
 		return ret;
 	if (retlen != len)
@@ -134,7 +134,7 @@
 		mtd->name, pos, len);
 	
 	if (!sect_size)
-		return MTD_WRITE (mtd, pos, len, &retlen, buf);
+		return MTD_BLOCK_WRITE (mtd, pos, len, &retlen, buf);
 
 	while (len > 0) {
 		unsigned long sect_start = (pos/sect_size)*sect_size;
@@ -166,7 +166,7 @@
 			    mtdblk->cache_offset != sect_start) {
 				/* fill the cache with the current sector */
 				mtdblk->cache_state = STATE_EMPTY;
-				ret = MTD_READ(mtd, sect_start, sect_size, &retlen, mtdblk->cache_data);
+				ret = MTD_BLOCK_READ(mtd, sect_start, sect_size, &retlen, mtdblk->cache_data);
 				if (ret)
 					return ret;
 				if (retlen != sect_size)
@@ -203,7 +203,7 @@
 			mtd->name, pos, len);
 	
 	if (!sect_size)
-		return MTD_READ (mtd, pos, len, &retlen, buf);
+		return MTD_BLOCK_READ (mtd, pos, len, &retlen, buf);
 
 	while (len > 0) {
 		unsigned long sect_start = (pos/sect_size)*sect_size;
@@ -222,7 +222,7 @@
 		    mtdblk->cache_offset == sect_start) {
 			memcpy (buf, mtdblk->cache_data + offset, size);
 		} else {
-			ret = MTD_READ (mtd, pos, size, &retlen, buf);
+			ret = MTD_BLOCK_READ (mtd, pos, size, &retlen, buf);
 			if (ret)
 				return ret;
 			if (retlen != size)
drivers/mtd/mtdcore.c +60 -0

72 lines not on this page · raw message

drivers/mtd/mtdpart.c +56 -0

68 lines not on this page · raw message

drivers/mtd/nand/nand.c +62 -5

121 lines not on this page · raw message

include/linux/mtd/mtd.h +13 -0
diff -ruN mtd-original/include/linux/mtd/mtd.h mtd-work/include/linux/mtd/mtd.h
@@ -166,6 +166,10 @@
 	u_int32_t ecctype;
 	u_int32_t eccsize;
 	
+	/* block map for RO */
+	int		*block_map;
+	int		 block_top;
+	int		 block_scantop;
 
 	// Kernel-only stuff starts here.
 	char *name;
@@ -240,6 +244,10 @@
 	int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
 	int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
 
+	/* block read */
+	int (*block_read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+	int (*block_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
+
 	void *priv;
 
 	struct module *owner;
@@ -273,6 +281,8 @@
 int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs,
 		      unsigned long count, loff_t from, size_t *retlen);
 
+loff_t map_over_bad_blocks(struct mtd_info *mtd, loff_t from);
+
 #define MTD_ERASE(mtd, args...) (*(mtd->erase))(mtd, args)
 #define MTD_POINT(mtd, a,b,c,d) (*(mtd->point))(mtd, a,b,c, (u_char **)(d))
 #define MTD_UNPOINT(mtd, arg) (*(mtd->unpoint))(mtd, (u_char *)arg)
@@ -286,6 +296,9 @@
 #define MTD_WRITEOOB(mtd, args...) (*(mtd->write_oob))(mtd, args)
 #define MTD_SYNC(mtd) do { if (mtd->sync) (*(mtd->sync))(mtd);  } while (0) 
 
+#define MTD_BLOCK_READ(mtd, args...) (*(mtd->block_read))(mtd, args)
+#define MTD_BLOCK_WRITE(mtd, args...) (*(mtd->block_write))(mtd, args)
+
 /*
  * Debugging macro and defines
  */
include/linux/mtd/nand.h +1 -0
diff -ruN mtd-original/include/linux/mtd/nand.h mtd-work/include/linux/mtd/nand.h
@@ -235,6 +235,7 @@
 	wait_queue_head_t wq;
 	nand_state_t 	state;
 	int 		page_shift;
+	int		erase_shift;
 	u_char 		*data_buf;
 	u_char		*oob_buf;
 	int		oobdirty;
 drivers/mtd/chips/amd_flash.c       |    3 +
 drivers/mtd/chips/cfi_cmdset_0001.c |    4 ++
 drivers/mtd/chips/cfi_cmdset_0002.c |    4 ++
 drivers/mtd/chips/cfi_cmdset_0020.c |    4 ++
 drivers/mtd/chips/map_ram.c         |    3 +
 drivers/mtd/chips/map_rom.c         |    4 ++
 drivers/mtd/chips/sharp.c           |    4 ++
 drivers/mtd/devices/doc2000.c       |    3 +
 drivers/mtd/devices/doc2001.c       |    3 +
 drivers/mtd/devices/doc2001plus.c   |    3 +
 drivers/mtd/devices/ms02-nv.c       |    3 +
 drivers/mtd/devices/mtdram.c        |    3 +
 drivers/mtd/mtdblock.c              |   10 ++---
 drivers/mtd/mtdcore.c               |   60 ++++++++++++++++++++++++++++++++
 drivers/mtd/mtdpart.c               |   56 ++++++++++++++++++++++++++++++
 drivers/mtd/nand/nand.c             |   67 +++++++++++++++++++++++++++++++++---
 include/linux/mtd/mtd.h             |   13 ++++++
 include/linux/mtd/nand.h            |    1 
 18 files changed, 238 insertions(+), 10 deletions(-)
#1 lore raw
Quoted 4 lines · Pantelis Antoniou

Hello there.

The following patch allows you to have any filesystem over NAND when mounted read only.

Please don't do it like this. Do it as a block device 'translation layer' instead. See nftl/ftl/mtdblock for examples.

Can you join irc.freenode.net #mtd ?

Signature · 1 line
dwmw2
#2 lore raw
Quoted 4 lines · David Woodhouse
Quoted 2 lines · Pantelis Antoniou

Hello there.

Quoted 10 lines
Quoted 3 lines

The following patch allows you to have any filesystem over NAND when mounted read only.

Please don't do it like this. Do it as a block device 'translation layer' instead. See nftl/ftl/mtdblock for examples.

Can you join irc.freenode.net #mtd ?

Sorry I'm at work now and IRC is blocked.

I'll try to get into it when I get home in about 2 hours.

I'll take a look at nftl/ftl/mtdblock.

The patch is very small and it works perfectly however. What are your main objections?

Regards

Pantelis

#3 lore raw
In reply to Pantelis Antoniou
Quoted 17 lines · Pantelis Antoniou

Hello there.

The following patch allows you to have any filesystem over NAND when mounted read only.

The main drive for this was that I wanted to use CRAMFS over NAND flash since it has a much higher compression ratio than JFFS2.

How it works is pretty simple.

It's pretty reasonable assumption that bad blocks are created only when writting to NAND. So in the read-only case it is possible to skip over the bad blocks and offer a bad-block free block device to the upper layers. This allows us to utilize any filesystem when read-only.

In order to speed the mount operation the bad block detection is performed lazilly.

Quoted 9 lines

Using the patch I was able to mount as read-only CRAMFS/JFFS2 partitions which contained simulated bad-blocks.

Awaiting your comments...

Regards

Pantelis

After more thought, I am in the process of doing something similar.

However my changes are not quite done yet. Roughly it goes like this:

Write a new read-only block driver that skips bad blocks and uses ECC reads. This allows any read only file system to use it (ie cramfs). The bad block map is kept in the first non-bad page in the partition, when the driver is installed it reads the bad block map.

To write the data into the mtd, you have a special version of nandwrite that checks for bad blocks while writing the image and finally writes the bad block map.

One difference between my approach and yours is that no existing mtd driver code is touched, it is simply another mtd block driver.

David Daney.

#4 lore raw
Quoted 6 lines · David Daney
Quoted 3 lines · Pantelis Antoniou

The following patch allows you to have any filesystem over NAND when mounted read only.

After more thought, I am in the process of doing something similar.

I have also been doing something similar to this - though I put the bad block handling into Squashfs (another RO compressed filesystem). It therefore seems as if all the possible approaches have been tried: putting the bad block code in the NAND layer; implementing a new RO block-driver; and putting the bad block code in the RO filesystem! It definately seems as if there is a requirement for this in the main source.

Pantelis have you looked at Squashfs? This filesystem gives much better compression than CRAMFS.

Phillip

#5 lore raw
Quoted 8 lines · Phillip Lougher
Quoted 6 lines · David Daney
Quoted 3 lines · Pantelis Antoniou

The following patch allows you to have any filesystem over NAND when mounted read only.

Quoted 6 lines
Quoted 2 lines

After more thought, I am in the process of doing something similar.

I have also been doing something similar to this - though I put the bad block handling into Squashfs (another RO compressed filesystem). It therefore seems as if all the possible approaches have been tried: putting the bad block code in the NAND layer; implementing a new RO block-driver; and putting the bad block code in the RO filesystem! It definately seems as if there is a requirement for this in the main source.

Pantelis have you looked at Squashfs? This filesystem gives much better compression than CRAMFS.

It seems that if people want to use more than one type of RO filesystem, that putting the badblock handling in the block driver instead of every filesystem makes more sense.

David Daney.

#6 lore raw
In reply to Phillip Lougher
Quoted 8 lines · Phillip Lougher
Quoted 6 lines · David Daney
Quoted 3 lines · Pantelis Antoniou

The following patch allows you to have any filesystem over NAND when mounted read only.

Quoted 7 lines
Quoted 2 lines

After more thought, I am in the process of doing something similar.

I have also been doing something similar to this - though I put the bad block handling into Squashfs (another RO compressed filesystem). It therefore seems as if all the possible approaches have been tried: putting the bad block code in the NAND layer; implementing a new RO block-driver; and putting the bad block code in the RO filesystem! It definately seems as if there is a requirement for this in the main source.

Pantelis have you looked at Squashfs? This filesystem gives much better compression than CRAMFS.

I've taken a look at squashfs. It does seems nicer than CRAMFS.

My main problem is not that it's not in the kernel proper, but it's not in by bootloader too. If you took a look at u-boot and implemented squashfs support I would probably switch.

Quoted 5 lines

Phillip

Regards

Pantelis