public inbox for linux-mtd@lists.infradead.org
 help / color / mirror / Atom feed
* [PATCH]Add erase count tracking support in JFFS2
@ 2005-08-18  9:08 zhao forrest
  2005-08-18 11:34 ` Jörn Engel
  0 siblings, 1 reply; 9+ messages in thread
From: zhao forrest @ 2005-08-18  9:08 UTC (permalink / raw)
  To: tglx, dedekind; +Cc: linux-mtd

[-- Attachment #1: Type: text/plain, Size: 418 bytes --]

Hi,

This patch adds the "erase count tracking" support in JFFS2.
In particular it records the erase count of each erase block.
By knowing the erase count of each erase block, we can further
enhance the current wear-leveling algorithm of random nature
in JFFS2.
So this patch is also the basis of my future patch for
enhancing the wear-leveling algorithm in JFFS2.

Your comments are highly welcome.

Thanks,
Forrest


[-- Attachment #2: erase_count.patch --]
[-- Type: application/octet-stream, Size: 19536 bytes --]

diff -auNrp ./mtd/fs/jffs2/build.c ./mtd_new/fs/jffs2/build.c
--- ./mtd/fs/jffs2/build.c	2005-07-31 06:00:11.000000000 +0800
+++ ./mtd_new/fs/jffs2/build.c	2005-08-18 13:33:55.000000000 +0800
@@ -336,6 +336,10 @@ int jffs2_do_mount_fs(struct jffs2_sb_in
 		c->blocks[i].first_node = NULL;
 		c->blocks[i].last_node = NULL;
 		c->blocks[i].bad_count = 0;
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		c->blocks[i].has_erase_count_node = 0;
+		c->blocks[i].erase_count = 0;
+#endif
 	}
 
 	INIT_LIST_HEAD(&c->clean_list);
diff -auNrp ./mtd/fs/jffs2/erase.c ./mtd_new/fs/jffs2/erase.c
--- ./mtd/fs/jffs2/erase.c	2005-07-23 06:00:13.000000000 +0800
+++ ./mtd_new/fs/jffs2/erase.c	2005-08-18 13:32:45.000000000 +0800
@@ -209,7 +209,12 @@ static void jffs2_erase_callback(struct 
 		jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr);
 	} else {
 		jffs2_erase_succeeded(priv->c, priv->jeb);
-	}	
+	}
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	priv->jeb->erase_count++;
+	priv->c->total_erase_count++;
+#endif	
 	kfree(instr);
 }
 #endif /* !__ECOS */
@@ -352,6 +357,9 @@ fail:
 static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
 	struct jffs2_raw_node_ref *marker_ref = NULL;
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	struct jffs2_raw_node_ref *erase_count_ref = NULL;
+#endif
 	size_t retlen;
 	int ret;
 	uint32_t bad_offset;
@@ -367,18 +375,68 @@ static void jffs2_mark_erased_block(stru
 
 	/* Cleanmarker in oob area or no cleanmarker at all ? */
 	if (jffs2_cleanmarker_oob(c) || c->cleanmarker_size == 0) {
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		struct kvec vecs[1];
+		struct jffs2_erase_count_node erase_count_node = {
+			.magic =        cpu_to_je16(JFFS2_MAGIC_BITMASK),
+			.nodetype =     cpu_to_je16(JFFS2_NODETYPE_ERASE_COUNT),
+			.totlen =       cpu_to_je32(c->erasecount_size),
+			.erase_count =  cpu_to_je32(jeb->erase_count)
+		};
+#endif
 
 		if (jffs2_cleanmarker_oob(c)) {
 			if (jffs2_write_nand_cleanmarker(c, jeb))
 				goto filebad;
 		}
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		if (!jffs2_cleanmarker_oob(c)) {
+			erase_count_ref = jffs2_alloc_raw_node_ref();
+			if (!erase_count_ref) {
+				printk(KERN_WARNING "Failed to allocate raw node ref for erase count node. Refiling\n");
+				goto refile;
+			}
+			erase_count_node.hdr_crc = cpu_to_je32(crc32(0, &erase_count_node,
+							sizeof(struct jffs2_unknown_node)-4));
+			erase_count_node.node_crc = cpu_to_je32(crc32(0, &erase_count_node,
+							sizeof(struct jffs2_erase_count_node)-4));
+			vecs[0].iov_base = (unsigned char *) &erase_count_node;
+			vecs[0].iov_len = sizeof(erase_count_node);
+			ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen);
+
+			if (ret || retlen != sizeof(erase_count_node)) {
+				if (ret)
+					printk(KERN_WARNING "Write erase count to block at 0x%08x failed: %d\n",
+						jeb->offset, ret);
+				else
+					printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
+						jeb->offset, sizeof(erase_count_node), retlen);
+				jffs2_free_raw_node_ref(erase_count_ref);
+				goto filebad;
+			}
+		}
+#endif
+
 		jeb->first_node = jeb->last_node = NULL;
 		jeb->free_size = c->sector_size;
 		jeb->used_size = 0;
 		jeb->dirty_size = 0;
 		jeb->wasted_size = 0;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		if (!jffs2_cleanmarker_oob(c)) {
+			erase_count_ref->next_in_ino = NULL;
+			erase_count_ref->next_phys = NULL;
+			erase_count_ref->flash_offset = jeb->offset | REF_NORMAL;
+			erase_count_ref->__totlen = PAD(c->erasecount_size);
+
+			jeb->first_node = jeb->last_node = erase_count_ref;
+
+			jeb->free_size = c->sector_size - PAD(c->erasecount_size);
+			jeb->used_size = PAD(c->erasecount_size);
+		}
+#endif
 	} else {
 
 		struct kvec vecs[1];
@@ -388,12 +446,31 @@ static void jffs2_mark_erased_block(stru
 			.totlen =	cpu_to_je32(c->cleanmarker_size)
 		};
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		struct jffs2_erase_count_node erase_count_node = {
+			.magic =        cpu_to_je16(JFFS2_MAGIC_BITMASK),
+			.nodetype =     cpu_to_je16(JFFS2_NODETYPE_ERASE_COUNT),
+			.totlen =       cpu_to_je32(c->erasecount_size),
+			.erase_count =  cpu_to_je32(jeb->erase_count)
+		};
+
+#endif
+
 		marker_ref = jffs2_alloc_raw_node_ref();
 		if (!marker_ref) {
 			printk(KERN_WARNING "Failed to allocate raw node ref for clean marker. Refiling\n");
 			goto refile;
 		}
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		erase_count_ref = jffs2_alloc_raw_node_ref();
+		if (!erase_count_ref) {
+			printk(KERN_WARNING "Failed to allocate raw node ref for erase count node. Refiling\n");
+			jffs2_free_raw_node_ref(marker_ref);
+			goto refile;
+		}
+#endif
+
 		marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
 
 		vecs[0].iov_base = (unsigned char *) ▮
@@ -412,6 +489,29 @@ static void jffs2_mark_erased_block(stru
 			goto filebad;
 		}
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		erase_count_node.hdr_crc = cpu_to_je32(crc32(0, &erase_count_node,
+						sizeof(struct jffs2_unknown_node)-4));
+		erase_count_node.node_crc = cpu_to_je32(crc32(0, &erase_count_node,
+						sizeof(struct jffs2_erase_count_node)-4));
+
+		vecs[0].iov_base = (unsigned char *) &erase_count_node;
+		vecs[0].iov_len = sizeof(erase_count_node);
+		ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset + PAD(c->cleanmarker_size), &retlen);
+
+		if (ret || retlen != sizeof(erase_count_node)) {
+			if (ret)
+				printk(KERN_WARNING "Write erase count to block at 0x%08x failed: %d\n",
+					jeb->offset, ret);
+			else
+				printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
+					jeb->offset, sizeof(erase_count_node), retlen);
+			jffs2_free_raw_node_ref(marker_ref);
+			jffs2_free_raw_node_ref(erase_count_ref);
+			goto filebad;
+		}
+#endif
+
 		marker_ref->next_in_ino = NULL;
 		marker_ref->next_phys = NULL;
 		marker_ref->flash_offset = jeb->offset | REF_NORMAL;
@@ -423,6 +523,19 @@ static void jffs2_mark_erased_block(stru
 		jeb->used_size = c->cleanmarker_size;
 		jeb->dirty_size = 0;
 		jeb->wasted_size = 0;
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		erase_count_ref->next_in_ino = NULL;
+		erase_count_ref->next_phys = NULL;
+		erase_count_ref->flash_offset = (jeb->offset + PAD(c->cleanmarker_size)) | REF_NORMAL;
+		erase_count_ref->__totlen = PAD(c->erasecount_size);
+
+		jeb->last_node->next_phys = erase_count_ref;
+		jeb->last_node = erase_count_ref;
+
+		jeb->free_size -= PAD(c->erasecount_size);
+		jeb->used_size += PAD(c->erasecount_size);
+#endif
 	}
 
 	spin_lock(&c->erase_completion_lock);
diff -auNrp ./mtd/fs/jffs2/fs.c ./mtd_new/fs/jffs2/fs.c
--- ./mtd/fs/jffs2/fs.c	2005-08-07 06:00:16.000000000 +0800
+++ ./mtd_new/fs/jffs2/fs.c	2005-08-18 13:32:56.000000000 +0800
@@ -490,6 +490,10 @@ int jffs2_do_fill_super(struct super_blo
 	}
 
 	c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	c->erasecount_size = sizeof(struct jffs2_erase_count_node);
+#endif
 	/* Joern -- stick alignment for weird 8-byte-page flash here */
 
 	/* NAND (or other bizarre) flash... do setup accordingly */
diff -auNrp ./mtd/fs/jffs2/nodelist.h ./mtd_new/fs/jffs2/nodelist.h
--- ./mtd/fs/jffs2/nodelist.h	2005-08-18 06:00:12.000000000 +0800
+++ ./mtd_new/fs/jffs2/nodelist.h	2005-08-18 13:32:02.000000000 +0800
@@ -195,6 +195,11 @@ struct jffs2_eraseblock
 	struct jffs2_raw_node_ref *last_node;
 
 	struct jffs2_raw_node_ref *gc_node;	/* Next node to be garbage collected */
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	uint8_t has_erase_count_node;   /* Whether an erase_count_node is found for this erase block */
+	uint32_t erase_count;           /* Keep track of the erase count of this erase block */
+#endif
 };
 
 /* Calculate totlen from surrounding nodes or eraseblock */
diff -auNrp ./mtd/fs/jffs2/nodemgmt.c ./mtd_new/fs/jffs2/nodemgmt.c
--- ./mtd/fs/jffs2/nodemgmt.c	2005-07-21 06:00:12.000000000 +0800
+++ ./mtd_new/fs/jffs2/nodemgmt.c	2005-08-18 13:33:30.000000000 +0800
@@ -258,7 +258,12 @@ static int jffs2_do_reserve_space(struct
 		c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list);
 		c->nr_free_blocks--;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		if ((jeb->free_size != c->sector_size - c->cleanmarker_size) &&
+			(jeb->free_size != c->sector_size - c->cleanmarker_size - c->erasecount_size)){
+#else
 		if (jeb->free_size != c->sector_size - c->cleanmarker_size) {
+#endif
 			printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size);
 			goto restart;
 		}
diff -auNrp ./mtd/fs/jffs2/scan.c ./mtd_new/fs/jffs2/scan.c
--- ./mtd/fs/jffs2/scan.c	2005-07-21 06:00:12.000000000 +0800
+++ ./mtd_new/fs/jffs2/scan.c	2005-08-18 13:32:31.000000000 +0800
@@ -438,6 +438,14 @@ scan_more:	
 				return BLK_STATE_CLEANMARKER;
 			}
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+			if (jeb->has_erase_count_node && jeb->used_size == (PAD(c->cleanmarker_size)
+			    +PAD(c->erasecount_size)) && !jeb->dirty_size) {
+				D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size)));
+				return BLK_STATE_CLEANMARKER;
+			}
+#endif
+
 			/* See how much more there is to read in this eraseblock... */
 			buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
 			if (!buf_len) {
@@ -581,6 +589,61 @@ scan_more:	
 			}
 			break;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		case JFFS2_NODETYPE_ERASE_COUNT:
+			if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
+				buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+				D1(printk(KERN_DEBUG "Fewer than %d bytes (erase count node) left to end of buf. Reading 0x%x at 0x%08x\n",
+					  je32_to_cpu(node->totlen), buf_len, ofs));
+				err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
+				if (err)
+					return err;
+				buf_ofs = ofs;
+				node = (void *)buf;
+			}
+			if (je32_to_cpu(node->totlen) != c->erasecount_size) {
+				printk(KERN_NOTICE "ERASE_COUNT node found at 0x%08x has totlen 0x%x != normal 0x%x\n",
+				      ofs, je32_to_cpu(node->totlen), sizeof(struct jffs2_erase_count_node));
+				DIRTY_SPACE(PAD(sizeof(struct jffs2_erase_count_node)));
+				ofs += PAD(sizeof(struct jffs2_erase_count_node));
+			} else if (ofs != jeb->offset + PAD(c->cleanmarker_size)) {
+				printk(KERN_NOTICE "ERASE_COUNT node found at 0x%08x, not closely follow the CLEANMARKER node in block (0x%08x)\n", ofs, jeb->offset);
+				DIRTY_SPACE(PAD(sizeof(struct jffs2_erase_count_node)));
+				ofs += PAD(sizeof(struct jffs2_erase_count_node));
+			} else {
+				uint32_t crc;
+				struct jffs2_erase_count_node *erase_count_node = (struct jffs2_erase_count_node *)node;
+				struct jffs2_raw_node_ref *erase_count_ref = jffs2_alloc_raw_node_ref();
+				if (!erase_count_ref) {
+					printk(KERN_NOTICE "Failed to allocate node ref for erase count\n");
+					return -ENOMEM;
+				}
+				crc = crc32(0, erase_count_node, sizeof(*erase_count_node) - 4);
+				if (crc != je32_to_cpu(erase_count_node->node_crc)) {
+					printk(KERN_NOTICE "ERASE_COUNT node found at 0x%08x, but CRC failed: Read 0x%08x, calculated 0x%08x\n", ofs, je32_to_cpu(erase_count_node->node_crc), crc);
+					DIRTY_SPACE(PAD(sizeof(struct jffs2_erase_count_node)));
+					ofs += PAD(sizeof(struct jffs2_erase_count_node));
+					jffs2_free_raw_node_ref(erase_count_ref);
+					break;
+				}
+				jeb->has_erase_count_node = 1;
+				jeb->erase_count = je32_to_cpu(erase_count_node->erase_count);
+				c->total_erase_count += jeb->erase_count;
+				erase_count_ref->next_in_ino = NULL;
+				erase_count_ref->next_phys = NULL;
+				erase_count_ref->flash_offset = ofs | REF_NORMAL;
+				erase_count_ref->__totlen = PAD(c->erasecount_size);
+				if (!jeb->first_node)
+					jeb->first_node = erase_count_ref;
+				if (jeb->last_node)
+					jeb->last_node->next_phys = erase_count_ref;
+				jeb->last_node = erase_count_ref;
+				USED_SPACE(PAD(c->erasecount_size));
+				ofs += PAD(c->erasecount_size);
+			}
+			break;
+#endif
+
 		case JFFS2_NODETYPE_PADDING:
 			DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
 			ofs += PAD(je32_to_cpu(node->totlen));
diff -auNrp ./mtd/fs/jffs2/wbuf.c ./mtd_new/fs/jffs2/wbuf.c
--- ./mtd/fs/jffs2/wbuf.c	2005-08-07 06:00:16.000000000 +0800
+++ ./mtd_new/fs/jffs2/wbuf.c	2005-08-18 13:33:45.000000000 +0800
@@ -976,8 +976,28 @@ int jffs2_check_oob_empty( struct jffs2_
 		}
 	}
 
-	/* we know, we are aligned :) */	
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	if (jeb->has_erase_count_node) {
+		for (i = 0; i < oob_size ; i++) {
+			if (i >= c->fsdata_pos && i < c->fsdata_pos + c->fsdata_len)
+				continue;
+
+			if (buf[oob_size + i] != 0xFF) {
+				D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n",
+					buf[oob_size + i], oob_size + i, jeb->offset));
+				ret = 1;
+				goto out;
+			}
+		}
+	}
+#endif
+
+	/* we know, we are aligned :) */
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	for (page = (oob_size * 2); page < len; page += sizeof(long)) {
+#else	
 	for (page = oob_size; page < len; page += sizeof(long)) {
+#endif
 		unsigned long dat = *(unsigned long *)(&buf[page]);
 		if(dat != -1) {
 			ret = 1; 
@@ -1005,7 +1025,9 @@ int jffs2_check_nand_cleanmarker (struct
 	int ret, i, cnt, retval = 0;
 	size_t retlen, offset;
 	int oob_size;
-
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	struct jffs2_erase_count_node_oob *e;
+#endif
 	offset = jeb->offset;
 	oob_size = c->mtd->oobsize;
 
@@ -1052,6 +1074,15 @@ int jffs2_check_nand_cleanmarker (struct
 				}
 				printk("\n");
 			})
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+			e = (struct jffs2_erase_count_node_oob *) &buf[oob_size + c->fsdata_pos];
+			if (je16_to_cpu (e->magic) == JFFS2_MAGIC_BITMASK &&
+			    je16_to_cpu (e->nodetype) == JFFS2_NODETYPE_ERASE_COUNT) {
+				jeb->has_erase_count_node = 1;
+				jeb->erase_count = je32_to_cpu (e->erase_count);
+				c->total_erase_count += jeb->erase_count;
+			}
+#endif
 		}
 		offset += c->mtd->erasesize;
 	}
@@ -1061,6 +1092,9 @@ int jffs2_check_nand_cleanmarker (struct
 int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
 	struct 	jffs2_unknown_node n;
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	struct jffs2_erase_count_node_oob e;
+#endif
 	int 	ret;
 	size_t 	retlen;
 
@@ -1078,6 +1112,23 @@ int jffs2_write_nand_cleanmarker(struct 
 		D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len));
 		return ret;
 	}
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	e.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	e.nodetype = cpu_to_je16(JFFS2_NODETYPE_ERASE_COUNT);
+	e.erase_count = cpu_to_je32(jeb->erase_count);
+
+	ret = jffs2_flash_write_oob(c, jeb->offset + c->mtd->oobblock + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&e);
+
+	if (ret) {
+		D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
+		return ret;
+	}
+	if (retlen != c->fsdata_len) {
+		D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len));
+		return ret;
+	}
+#endif
 	return 0;
 }
 
@@ -1130,6 +1181,10 @@ static int jffs2_nand_set_oobinfo(struct
 	/* Cleanmarker is out-of-band, so inline size zero */
 	c->cleanmarker_size = 0;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	c->erasecount_size = 0;
+#endif
+
 	/* Should we use autoplacement ? */
 	if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) {
 		D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n"));
@@ -1218,6 +1273,9 @@ int jffs2_nor_ecc_flash_setup(struct jff
 	/* Cleanmarker is actually larger on the flashes */
 	c->cleanmarker_size = 16;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	c->erasecount_size = 24;
+#endif
 	/* Initialize write buffer */
 	init_rwsem(&c->wbuf_sem);
 	c->wbuf_pagesize = c->mtd->eccsize;
diff -auNrp ./mtd/fs/Kconfig ./mtd_new/fs/Kconfig
--- ./mtd/fs/Kconfig	2005-05-10 06:00:10.000000000 +0800
+++ ./mtd_new/fs/Kconfig	2005-08-18 13:45:11.000000000 +0800
@@ -64,6 +64,16 @@ config JFFS2_FS_WRITEBUFFER
 	    - NOR flash with transparent ECC
 	    - DataFlash
 
+config JFFS2_FS_ERASE_COUNT_TRACKING
+	bool "JFFS2 erase count tracking support"
+	depends on JFFS2_FS
+	default y
+	help
+	  This enables the erase count tracking support in JFFS2.
+
+	  This functionality provides the support to track the erase count of
+	  each erase block.
+
 config JFFS2_COMPRESSION_OPTIONS
 	bool "Advanced compression options for JFFS2"
 	default n
diff -auNrp ./mtd/include/linux/jffs2_fs_sb.h ./mtd_new/include/linux/jffs2_fs_sb.h
--- ./mtd/include/linux/jffs2_fs_sb.h	2005-05-20 06:00:14.000000000 +0800
+++ ./mtd_new/include/linux/jffs2_fs_sb.h	2005-08-18 13:28:36.000000000 +0800
@@ -40,7 +40,9 @@ struct jffs2_sb_info {
 					   out-of-order writing of nodes. And GC. */
 	uint32_t cleanmarker_size;	/* Size of an _inline_ CLEANMARKER
 					 (i.e. zero for OOB CLEANMARKER */
-
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	uint32_t erasecount_size;	/* size of an _inline_ erase-count node */
+#endif
 	uint32_t flash_size;
 	uint32_t used_size;
 	uint32_t dirty_size;
@@ -89,6 +91,10 @@ struct jffs2_sb_info {
 	wait_queue_head_t inocache_wq;
 	struct jffs2_inode_cache **inocache_list;
 	spinlock_t inocache_lock;
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	uint32_t total_erase_count;	/* The summary erase count of all erase blocks on a Flash  */
+#endif
 	
 	/* Sem to allow jffs2_garbage_collect_deletion_dirent to
 	   drop the erase_completion_lock while it's holding a pointer 
diff -auNrp ./mtd/include/linux/jffs2.h ./mtd_new/include/linux/jffs2.h
--- ./mtd/include/linux/jffs2.h	2005-07-27 06:00:09.000000000 +0800
+++ ./mtd_new/include/linux/jffs2.h	2005-08-18 13:28:01.000000000 +0800
@@ -60,6 +60,10 @@
 #define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
 #define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+#define JFFS2_NODETYPE_ERASE_COUNT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 5)
+#endif
+
 // Maybe later...
 //#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
 //#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
@@ -146,10 +150,33 @@ struct jffs2_raw_inode
 	uint8_t data[0];
 } __attribute__((packed));
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+struct jffs2_erase_count_node
+{
+	jint16_t magic;      /* A constant magic number.  */
+	jint16_t nodetype;   /* == JFFS2_NODETYPE_ERASE_COUNT */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t erase_count; /* used to track the erase counts of each erase block */
+	jint32_t node_crc;
+} __attribute__((packed));
+
+struct jffs2_erase_count_node_oob
+{
+	jint16_t magic;      /* A constant magic number.  */
+	jint16_t nodetype;   /* == JFFS2_NODETYPE_ERASE_COUNT */
+	jint32_t erase_count; /* used to track the erase counts of each erase block */
+} __attribute__((packed));
+
+#endif
+
 union jffs2_node_union {
 	struct jffs2_raw_inode i;
 	struct jffs2_raw_dirent d;
 	struct jffs2_unknown_node u;
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	struct jffs2_erase_count_node e;
+#endif
 };
 
 #endif /* __LINUX_JFFS2_H__ */

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH]Add erase count tracking support in JFFS2
  2005-08-18  9:08 zhao forrest
@ 2005-08-18 11:34 ` Jörn Engel
  0 siblings, 0 replies; 9+ messages in thread
From: Jörn Engel @ 2005-08-18 11:34 UTC (permalink / raw)
  To: zhao forrest; +Cc: tglx, linux-mtd

On Thu, 18 August 2005 17:08:41 +0800, zhao forrest wrote:
> 
> This patch adds the "erase count tracking" support in JFFS2.
> In particular it records the erase count of each erase block.
> By knowing the erase count of each erase block, we can further
> enhance the current wear-leveling algorithm of random nature
> in JFFS2.
> So this patch is also the basis of my future patch for
> enhancing the wear-leveling algorithm in JFFS2.
> 
> Your comments are highly welcome.

Getting rid of the #ifdefs would help.  Will have a closer look
sometime later...

Jörn

-- 
tglx1 thinks that joern should get a (TM) for "Thinking Is Hard"
-- Thomas Gleixner

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH]Add erase count tracking support in JFFS2
@ 2005-08-24 10:43 zhao forrest
  2005-08-24 12:05 ` Jörn Engel
  0 siblings, 1 reply; 9+ messages in thread
From: zhao forrest @ 2005-08-24 10:43 UTC (permalink / raw)
  To: joern; +Cc: tglx, linux-mtd

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=gb2312; format=flowed, Size: 1162 bytes --]

Joern,

I refined the patch according to your comments. In particular
I removed some unnecessary #ifdef, reconstruct function 
jffs2_mark_erased_block() to make it shorter and more clean.

Thanks,
Forrest

>From: Jörn Engel <joern@wohnheim.fh-wedel.de>
>To: zhao forrest <zhao_fusheng@hotmail.com>
>CC: tglx@linutronix.de, 
dedekind@infradead.org,linux-mtd@lists.infradead.org
>Subject: Re: [PATCH]Add erase count tracking support in JFFS2
>Date: Thu, 18 Aug 2005 13:34:39 +0200
>
>On Thu, 18 August 2005 17:08:41 +0800, zhao forrest wrote:
> >
> > This patch adds the "erase count tracking" support in JFFS2.
> > In particular it records the erase count of each erase block.
> > By knowing the erase count of each erase block, we can further
> > enhance the current wear-leveling algorithm of random nature
> > in JFFS2.
> > So this patch is also the basis of my future patch for
> > enhancing the wear-leveling algorithm in JFFS2.
> >
> > Your comments are highly welcome.
>
>Getting rid of the #ifdefs would help.  Will have a closer look
>sometime later...
>
>Jörn
>
>--
>tglx1 thinks that joern should get a (TM) for "Thinking Is Hard"
>-- Thomas Gleixner


[-- Attachment #2: erase_count_r1.patch --]
[-- Type: application/octet-stream, Size: 20277 bytes --]

diff -auNrp ./mtd/fs/jffs2/build.c ./mtd_new/fs/jffs2/build.c
--- ./mtd/fs/jffs2/build.c	2005-07-31 06:00:11.000000000 +0800
+++ ./mtd_new/fs/jffs2/build.c	2005-08-18 13:33:55.000000000 +0800
@@ -336,6 +336,10 @@ int jffs2_do_mount_fs(struct jffs2_sb_in
 		c->blocks[i].first_node = NULL;
 		c->blocks[i].last_node = NULL;
 		c->blocks[i].bad_count = 0;
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		c->blocks[i].has_erase_count_node = 0;
+		c->blocks[i].erase_count = 0;
+#endif
 	}
 
 	INIT_LIST_HEAD(&c->clean_list);
diff -auNrp ./mtd/fs/jffs2/erase.c ./mtd_new/fs/jffs2/erase.c
--- ./mtd/fs/jffs2/erase.c	2005-07-23 06:00:13.000000000 +0800
+++ ./mtd_new/fs/jffs2/erase.c	2005-08-24 18:26:14.000000000 +0800
@@ -209,7 +209,12 @@ static void jffs2_erase_callback(struct 
 		jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr);
 	} else {
 		jffs2_erase_succeeded(priv->c, priv->jeb);
-	}	
+	}
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	priv->jeb->erase_count++;
+	priv->c->total_erase_count++;
+#endif	
 	kfree(instr);
 }
 #endif /* !__ECOS */
@@ -349,6 +354,188 @@ fail:
 	return ret;
 }
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+static int write_erase_count_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+	struct jffs2_raw_node_ref *erase_count_ref = NULL;
+	struct kvec vecs[1];
+	int ret;
+	size_t retlen;
+	struct jffs2_erase_count_node erase_count_node = {
+		.magic =        cpu_to_je16(JFFS2_MAGIC_BITMASK),
+		.nodetype =     cpu_to_je16(JFFS2_NODETYPE_ERASE_COUNT),
+		.totlen =       cpu_to_je32(c->erasecount_size),
+		.erase_count =  cpu_to_je32(jeb->erase_count)
+	};
+
+	erase_count_ref = jffs2_alloc_raw_node_ref();
+	if (!erase_count_ref) {
+		printk(KERN_WARNING "Failed to allocate raw node ref for erase count node.\n");
+		return ENOMEM;
+	}
+	erase_count_node.hdr_crc = cpu_to_je32(crc32(0, &erase_count_node,
+					sizeof(struct jffs2_unknown_node)-4));
+ 	erase_count_node.node_crc = cpu_to_je32(crc32(0, &erase_count_node,
+					sizeof(struct jffs2_erase_count_node)-4));
+	vecs[0].iov_base = (unsigned char *) &erase_count_node;
+	vecs[0].iov_len = sizeof(erase_count_node);
+	ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset + PAD(c->cleanmarker_size), &retlen);
+
+	if (ret || retlen != sizeof(erase_count_node)) {
+		if (ret)
+			printk(KERN_WARNING "Write erase count to block at 0x%08x failed: %d\n",
+					jeb->offset, ret);
+		else
+			printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
+					jeb->offset, sizeof(erase_count_node), retlen);
+		jffs2_free_raw_node_ref(erase_count_ref);
+		return ret;
+	}
+
+	erase_count_ref->next_in_ino = NULL;
+	erase_count_ref->next_phys = NULL;
+	erase_count_ref->flash_offset = (jeb->offset + PAD(c->cleanmarker_size)) | REF_NORMAL;
+	erase_count_ref->__totlen = PAD(c->erasecount_size);
+
+	if (!jeb->first_node)
+		jeb->first_node = erase_count_ref;
+	if (jeb->last_node)
+		jeb->last_node->next_phys = erase_count_ref;
+	jeb->last_node = erase_count_ref;
+
+	return 0;
+}
+
+static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+	struct jffs2_raw_node_ref *marker_ref = NULL;
+	size_t retlen;
+	int ret;
+	uint32_t bad_offset;
+
+	switch (jffs2_block_check_erase(c, jeb, &bad_offset)) {
+	case -EAGAIN:   goto refile;
+	case -EIO:      goto filebad;
+	}
+
+	/* Write the erase complete marker */
+	D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
+	bad_offset = jeb->offset;
+
+	/* Cleanmarker in oob area or no cleanmarker at all ? */
+	if (jffs2_cleanmarker_oob(c) || c->cleanmarker_size == 0) {
+		if (jffs2_cleanmarker_oob(c)) {
+			if (jffs2_write_nand_cleanmarker(c, jeb))
+				goto filebad;
+
+			jeb->first_node = jeb->last_node = NULL;
+			jeb->free_size = c->sector_size;
+			jeb->used_size = 0;
+			jeb->dirty_size = 0;
+			jeb->wasted_size = 0;
+		} else {
+			ret = write_erase_count_node(c, jeb);
+
+			if (ret == ENOMEM)
+				goto refile;
+			else if (ret)
+				goto filebad;
+
+			jeb->free_size = c->sector_size - PAD(c->erasecount_size);
+			jeb->used_size = PAD(c->erasecount_size);
+			jeb->dirty_size = 0;
+			jeb->wasted_size = 0;                	
+		}
+	} else {
+
+		struct kvec vecs[1];
+		struct jffs2_unknown_node marker = {
+			.magic =        cpu_to_je16(JFFS2_MAGIC_BITMASK),
+			.nodetype =     cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
+			.totlen =       cpu_to_je32(c->cleanmarker_size)
+		};
+
+		marker_ref = jffs2_alloc_raw_node_ref();
+		if (!marker_ref) {
+			printk(KERN_WARNING "Failed to allocate raw node ref for clean marker. Refiling\n");
+			goto refile;
+		}
+
+		marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
+
+		vecs[0].iov_base = (unsigned char *) &marker;
+		vecs[0].iov_len = sizeof(marker);
+		ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen);
+
+		if (ret || retlen != sizeof(marker)) {
+			if (ret)
+				printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
+					jeb->offset, ret);
+			else
+				printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
+					jeb->offset, sizeof(marker), retlen);
+
+			jffs2_free_raw_node_ref(marker_ref);
+			goto filebad;
+		}
+
+		marker_ref->next_in_ino = NULL;
+		marker_ref->next_phys = NULL;
+		marker_ref->flash_offset = jeb->offset | REF_NORMAL;
+		marker_ref->__totlen = c->cleanmarker_size;
+
+		jeb->first_node = jeb->last_node = marker_ref;
+
+		ret = write_erase_count_node(c, jeb);
+
+		if (ret) {
+			jffs2_free_raw_node_ref(marker_ref);
+			if (ret == ENOMEM)
+				goto refile;
+			else if (ret)
+				goto filebad;
+		}
+
+		jeb->free_size = c->sector_size - c->cleanmarker_size - PAD(c->erasecount_size);
+		jeb->used_size = c->cleanmarker_size + PAD(c->erasecount_size);
+		jeb->dirty_size = 0;
+		jeb->wasted_size = 0;
+	}
+
+	spin_lock(&c->erase_completion_lock);
+	c->erasing_size -= c->sector_size;
+	c->free_size += jeb->free_size;
+	c->used_size += jeb->used_size;
+
+	jffs2_dbg_acct_sanity_check_nolock(c,jeb);
+	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
+
+	list_add_tail(&jeb->list, &c->free_list);
+	c->nr_erasing_blocks--;
+	c->nr_free_blocks++;
+	spin_unlock(&c->erase_completion_lock);
+	wake_up(&c->erase_wait);
+	return;
+
+filebad:
+	spin_lock(&c->erase_completion_lock);
+	/* Stick it on a list (any list) so erase_failed can take it
+	   right off again.  Silly, but shouldn't happen often. */
+	list_add(&jeb->list, &c->erasing_list);
+	spin_unlock(&c->erase_completion_lock);
+	jffs2_erase_failed(c, jeb, bad_offset);
+	return;
+
+refile:
+	/* Stick it back on the list from whence it came and come back later */
+	jffs2_erase_pending_trigger(c);
+	spin_lock(&c->erase_completion_lock);
+	list_add(&jeb->list, &c->erase_complete_list);
+	spin_unlock(&c->erase_completion_lock);
+	return;
+}
+#else
+
 static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
 	struct jffs2_raw_node_ref *marker_ref = NULL;
@@ -367,7 +554,6 @@ static void jffs2_mark_erased_block(stru
 
 	/* Cleanmarker in oob area or no cleanmarker at all ? */
 	if (jffs2_cleanmarker_oob(c) || c->cleanmarker_size == 0) {
-
 		if (jffs2_cleanmarker_oob(c)) {
 			if (jffs2_write_nand_cleanmarker(c, jeb))
 				goto filebad;
@@ -378,7 +564,6 @@ static void jffs2_mark_erased_block(stru
 		jeb->used_size = 0;
 		jeb->dirty_size = 0;
 		jeb->wasted_size = 0;
-
 	} else {
 
 		struct kvec vecs[1];
@@ -457,3 +642,4 @@ refile:
 	spin_unlock(&c->erase_completion_lock);
 	return;
 }
+#endif
diff -auNrp ./mtd/fs/jffs2/fs.c ./mtd_new/fs/jffs2/fs.c
--- ./mtd/fs/jffs2/fs.c	2005-08-07 06:00:16.000000000 +0800
+++ ./mtd_new/fs/jffs2/fs.c	2005-08-18 13:32:56.000000000 +0800
@@ -490,6 +490,10 @@ int jffs2_do_fill_super(struct super_blo
 	}
 
 	c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	c->erasecount_size = sizeof(struct jffs2_erase_count_node);
+#endif
 	/* Joern -- stick alignment for weird 8-byte-page flash here */
 
 	/* NAND (or other bizarre) flash... do setup accordingly */
diff -auNrp ./mtd/fs/jffs2/nodelist.h ./mtd_new/fs/jffs2/nodelist.h
--- ./mtd/fs/jffs2/nodelist.h	2005-08-18 06:00:12.000000000 +0800
+++ ./mtd_new/fs/jffs2/nodelist.h	2005-08-18 13:32:02.000000000 +0800
@@ -195,6 +195,11 @@ struct jffs2_eraseblock
 	struct jffs2_raw_node_ref *last_node;
 
 	struct jffs2_raw_node_ref *gc_node;	/* Next node to be garbage collected */
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	uint8_t has_erase_count_node;   /* Whether an erase_count_node is found for this erase block */
+	uint32_t erase_count;           /* Keep track of the erase count of this erase block */
+#endif
 };
 
 /* Calculate totlen from surrounding nodes or eraseblock */
diff -auNrp ./mtd/fs/jffs2/nodemgmt.c ./mtd_new/fs/jffs2/nodemgmt.c
--- ./mtd/fs/jffs2/nodemgmt.c	2005-07-21 06:00:12.000000000 +0800
+++ ./mtd_new/fs/jffs2/nodemgmt.c	2005-08-18 13:33:30.000000000 +0800
@@ -258,7 +258,12 @@ static int jffs2_do_reserve_space(struct
 		c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list);
 		c->nr_free_blocks--;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		if ((jeb->free_size != c->sector_size - c->cleanmarker_size) &&
+			(jeb->free_size != c->sector_size - c->cleanmarker_size - c->erasecount_size)){
+#else
 		if (jeb->free_size != c->sector_size - c->cleanmarker_size) {
+#endif
 			printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size);
 			goto restart;
 		}
diff -auNrp ./mtd/fs/jffs2/scan.c ./mtd_new/fs/jffs2/scan.c
--- ./mtd/fs/jffs2/scan.c	2005-07-21 06:00:12.000000000 +0800
+++ ./mtd_new/fs/jffs2/scan.c	2005-08-18 13:32:31.000000000 +0800
@@ -438,6 +438,14 @@ scan_more:	
 				return BLK_STATE_CLEANMARKER;
 			}
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+			if (jeb->has_erase_count_node && jeb->used_size == (PAD(c->cleanmarker_size)
+			    +PAD(c->erasecount_size)) && !jeb->dirty_size) {
+				D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size)));
+				return BLK_STATE_CLEANMARKER;
+			}
+#endif
+
 			/* See how much more there is to read in this eraseblock... */
 			buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
 			if (!buf_len) {
@@ -581,6 +589,61 @@ scan_more:	
 			}
 			break;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		case JFFS2_NODETYPE_ERASE_COUNT:
+			if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
+				buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+				D1(printk(KERN_DEBUG "Fewer than %d bytes (erase count node) left to end of buf. Reading 0x%x at 0x%08x\n",
+					  je32_to_cpu(node->totlen), buf_len, ofs));
+				err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
+				if (err)
+					return err;
+				buf_ofs = ofs;
+				node = (void *)buf;
+			}
+			if (je32_to_cpu(node->totlen) != c->erasecount_size) {
+				printk(KERN_NOTICE "ERASE_COUNT node found at 0x%08x has totlen 0x%x != normal 0x%x\n",
+				      ofs, je32_to_cpu(node->totlen), sizeof(struct jffs2_erase_count_node));
+				DIRTY_SPACE(PAD(sizeof(struct jffs2_erase_count_node)));
+				ofs += PAD(sizeof(struct jffs2_erase_count_node));
+			} else if (ofs != jeb->offset + PAD(c->cleanmarker_size)) {
+				printk(KERN_NOTICE "ERASE_COUNT node found at 0x%08x, not closely follow the CLEANMARKER node in block (0x%08x)\n", ofs, jeb->offset);
+				DIRTY_SPACE(PAD(sizeof(struct jffs2_erase_count_node)));
+				ofs += PAD(sizeof(struct jffs2_erase_count_node));
+			} else {
+				uint32_t crc;
+				struct jffs2_erase_count_node *erase_count_node = (struct jffs2_erase_count_node *)node;
+				struct jffs2_raw_node_ref *erase_count_ref = jffs2_alloc_raw_node_ref();
+				if (!erase_count_ref) {
+					printk(KERN_NOTICE "Failed to allocate node ref for erase count\n");
+					return -ENOMEM;
+				}
+				crc = crc32(0, erase_count_node, sizeof(*erase_count_node) - 4);
+				if (crc != je32_to_cpu(erase_count_node->node_crc)) {
+					printk(KERN_NOTICE "ERASE_COUNT node found at 0x%08x, but CRC failed: Read 0x%08x, calculated 0x%08x\n", ofs, je32_to_cpu(erase_count_node->node_crc), crc);
+					DIRTY_SPACE(PAD(sizeof(struct jffs2_erase_count_node)));
+					ofs += PAD(sizeof(struct jffs2_erase_count_node));
+					jffs2_free_raw_node_ref(erase_count_ref);
+					break;
+				}
+				jeb->has_erase_count_node = 1;
+				jeb->erase_count = je32_to_cpu(erase_count_node->erase_count);
+				c->total_erase_count += jeb->erase_count;
+				erase_count_ref->next_in_ino = NULL;
+				erase_count_ref->next_phys = NULL;
+				erase_count_ref->flash_offset = ofs | REF_NORMAL;
+				erase_count_ref->__totlen = PAD(c->erasecount_size);
+				if (!jeb->first_node)
+					jeb->first_node = erase_count_ref;
+				if (jeb->last_node)
+					jeb->last_node->next_phys = erase_count_ref;
+				jeb->last_node = erase_count_ref;
+				USED_SPACE(PAD(c->erasecount_size));
+				ofs += PAD(c->erasecount_size);
+			}
+			break;
+#endif
+
 		case JFFS2_NODETYPE_PADDING:
 			DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
 			ofs += PAD(je32_to_cpu(node->totlen));
diff -auNrp ./mtd/fs/jffs2/wbuf.c ./mtd_new/fs/jffs2/wbuf.c
--- ./mtd/fs/jffs2/wbuf.c	2005-08-07 06:00:16.000000000 +0800
+++ ./mtd_new/fs/jffs2/wbuf.c	2005-08-24 18:26:50.000000000 +0800
@@ -976,8 +976,28 @@ int jffs2_check_oob_empty( struct jffs2_
 		}
 	}
 
-	/* we know, we are aligned :) */	
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	if (jeb->has_erase_count_node) {
+		for (i = 0; i < oob_size ; i++) {
+			if (i >= c->fsdata_pos && i < c->fsdata_pos + c->fsdata_len)
+				continue;
+
+			if (buf[oob_size + i] != 0xFF) {
+				D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n",
+					buf[oob_size + i], oob_size + i, jeb->offset));
+				ret = 1;
+				goto out;
+			}
+		}
+	}
+#endif
+
+	/* we know, we are aligned :) */
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	for (page = (oob_size * 2); page < len; page += sizeof(long)) {
+#else	
 	for (page = oob_size; page < len; page += sizeof(long)) {
+#endif
 		unsigned long dat = *(unsigned long *)(&buf[page]);
 		if(dat != -1) {
 			ret = 1; 
@@ -1005,7 +1025,9 @@ int jffs2_check_nand_cleanmarker (struct
 	int ret, i, cnt, retval = 0;
 	size_t retlen, offset;
 	int oob_size;
-
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	struct jffs2_erase_count_node_oob *e;
+#endif
 	offset = jeb->offset;
 	oob_size = c->mtd->oobsize;
 
@@ -1052,6 +1074,15 @@ int jffs2_check_nand_cleanmarker (struct
 				}
 				printk("\n");
 			})
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+			e = (struct jffs2_erase_count_node_oob *) &buf[oob_size + c->fsdata_pos];
+			if (je16_to_cpu (e->magic) == JFFS2_MAGIC_BITMASK &&
+			    je16_to_cpu (e->nodetype) == JFFS2_NODETYPE_ERASE_COUNT) {
+				jeb->has_erase_count_node = 1;
+				jeb->erase_count = je32_to_cpu (e->erase_count);
+				c->total_erase_count += jeb->erase_count;
+			}
+#endif
 		}
 		offset += c->mtd->erasesize;
 	}
@@ -1061,6 +1092,9 @@ int jffs2_check_nand_cleanmarker (struct
 int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
 	struct 	jffs2_unknown_node n;
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	struct jffs2_erase_count_node_oob ec;
+#endif
 	int 	ret;
 	size_t 	retlen;
 
@@ -1078,6 +1112,23 @@ int jffs2_write_nand_cleanmarker(struct 
 		D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len));
 		return ret;
 	}
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	ec.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	ec.nodetype = cpu_to_je16(JFFS2_NODETYPE_ERASE_COUNT);
+	ec.erase_count = cpu_to_je32(jeb->erase_count);
+
+	ret = jffs2_flash_write_oob(c, jeb->offset + c->mtd->oobblock + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&ec);
+
+	if (ret) {
+		D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
+		return ret;
+	}
+	if (retlen != c->fsdata_len) {
+		D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len));
+		return ret;
+	}
+#endif
 	return 0;
 }
 
@@ -1130,6 +1181,10 @@ static int jffs2_nand_set_oobinfo(struct
 	/* Cleanmarker is out-of-band, so inline size zero */
 	c->cleanmarker_size = 0;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	c->erasecount_size = 0;
+#endif
+
 	/* Should we use autoplacement ? */
 	if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) {
 		D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n"));
@@ -1218,6 +1273,9 @@ int jffs2_nor_ecc_flash_setup(struct jff
 	/* Cleanmarker is actually larger on the flashes */
 	c->cleanmarker_size = 16;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	c->erasecount_size = 24;
+#endif
 	/* Initialize write buffer */
 	init_rwsem(&c->wbuf_sem);
 	c->wbuf_pagesize = c->mtd->eccsize;
diff -auNrp ./mtd/fs/Kconfig ./mtd_new/fs/Kconfig
--- ./mtd/fs/Kconfig	2005-05-10 06:00:10.000000000 +0800
+++ ./mtd_new/fs/Kconfig	2005-08-18 13:45:11.000000000 +0800
@@ -64,6 +64,16 @@ config JFFS2_FS_WRITEBUFFER
 	    - NOR flash with transparent ECC
 	    - DataFlash
 
+config JFFS2_FS_ERASE_COUNT_TRACKING
+	bool "JFFS2 erase count tracking support"
+	depends on JFFS2_FS
+	default y
+	help
+	  This enables the erase count tracking support in JFFS2.
+
+	  This functionality provides the support to track the erase count of
+	  each erase block.
+
 config JFFS2_COMPRESSION_OPTIONS
 	bool "Advanced compression options for JFFS2"
 	default n
diff -auNrp ./mtd/include/linux/jffs2_fs_sb.h ./mtd_new/include/linux/jffs2_fs_sb.h
--- ./mtd/include/linux/jffs2_fs_sb.h	2005-05-20 06:00:14.000000000 +0800
+++ ./mtd_new/include/linux/jffs2_fs_sb.h	2005-08-18 13:28:36.000000000 +0800
@@ -40,7 +40,9 @@ struct jffs2_sb_info {
 					   out-of-order writing of nodes. And GC. */
 	uint32_t cleanmarker_size;	/* Size of an _inline_ CLEANMARKER
 					 (i.e. zero for OOB CLEANMARKER */
-
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	uint32_t erasecount_size;	/* size of an _inline_ erase-count node */
+#endif
 	uint32_t flash_size;
 	uint32_t used_size;
 	uint32_t dirty_size;
@@ -89,6 +91,10 @@ struct jffs2_sb_info {
 	wait_queue_head_t inocache_wq;
 	struct jffs2_inode_cache **inocache_list;
 	spinlock_t inocache_lock;
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	uint32_t total_erase_count;	/* The summary erase count of all erase blocks on a Flash  */
+#endif
 	
 	/* Sem to allow jffs2_garbage_collect_deletion_dirent to
 	   drop the erase_completion_lock while it's holding a pointer 
diff -auNrp ./mtd/include/linux/jffs2.h ./mtd_new/include/linux/jffs2.h
--- ./mtd/include/linux/jffs2.h	2005-07-27 06:00:09.000000000 +0800
+++ ./mtd_new/include/linux/jffs2.h	2005-08-24 18:25:46.000000000 +0800
@@ -60,6 +60,8 @@
 #define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
 #define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
 
+#define JFFS2_NODETYPE_ERASE_COUNT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 5)
+
 // Maybe later...
 //#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
 //#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
@@ -146,10 +148,28 @@ struct jffs2_raw_inode
 	uint8_t data[0];
 } __attribute__((packed));
 
+struct jffs2_erase_count_node
+{
+	jint16_t magic;      /* A constant magic number.  */
+	jint16_t nodetype;   /* == JFFS2_NODETYPE_ERASE_COUNT */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t erase_count; /* used to track the erase counts of each erase block */
+	jint32_t node_crc;
+} __attribute__((packed));
+
+struct jffs2_erase_count_node_oob
+{
+	jint16_t magic;      /* A constant magic number.  */
+	jint16_t nodetype;   /* == JFFS2_NODETYPE_ERASE_COUNT */
+	jint32_t erase_count; /* used to track the erase counts of each erase block */
+} __attribute__((packed));
+
 union jffs2_node_union {
 	struct jffs2_raw_inode i;
 	struct jffs2_raw_dirent d;
 	struct jffs2_unknown_node u;
+	struct jffs2_erase_count_node e;
 };
 
 #endif /* __LINUX_JFFS2_H__ */

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH]Add erase count tracking support in JFFS2
  2005-08-24 10:43 zhao forrest
@ 2005-08-24 12:05 ` Jörn Engel
  2005-08-24 12:45   ` zhao forrest
  0 siblings, 1 reply; 9+ messages in thread
From: Jörn Engel @ 2005-08-24 12:05 UTC (permalink / raw)
  To: zhao forrest; +Cc: tglx, linux-mtd

On Wed, 24 August 2005 18:43:00 +0800, zhao forrest wrote:
> 
> I refined the patch according to your comments. In particular
> I removed some unnecessary #ifdef, reconstruct function 
> jffs2_mark_erased_block() to make it shorter and more clean.

Thou shalt not toppost.

Anyway, let's have another look...

> diff -auNrp ./mtd/fs/jffs2/build.c ./mtd_new/fs/jffs2/build.c
> --- ./mtd/fs/jffs2/build.c	2005-07-31 06:00:11.000000000 +0800
> +++ ./mtd_new/fs/jffs2/build.c	2005-08-18 13:33:55.000000000 +0800
> @@ -336,6 +336,10 @@ int jffs2_do_mount_fs(struct jffs2_sb_in
>  		c->blocks[i].first_node = NULL;
>  		c->blocks[i].last_node = NULL;
>  		c->blocks[i].bad_count = 0;
> +#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
> +		c->blocks[i].has_erase_count_node = 0;
> +		c->blocks[i].erase_count = 0;
> +#endif

sizeof(struct jffs2_eraseblock) is growing with your code enabled.
That means we have yet another reason to use a fixed 1:1 physical
block : virtual block mapping.

Which means that your patch can't go in before someone has written the
necessary 1:1 mapping code.

Back to your patch, you should remove this #ifdef as well.  Either
spend the memory for the two variables unconditionally and always
initialize them here, or create a helper function.

>  	}
>  
>  	INIT_LIST_HEAD(&c->clean_list);
> diff -auNrp ./mtd/fs/jffs2/erase.c ./mtd_new/fs/jffs2/erase.c
> --- ./mtd/fs/jffs2/erase.c	2005-07-23 06:00:13.000000000 +0800
> +++ ./mtd_new/fs/jffs2/erase.c	2005-08-24 18:26:14.000000000 +0800
> @@ -209,7 +209,12 @@ static void jffs2_erase_callback(struct 
>  		jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr);
>  	} else {
>  		jffs2_erase_succeeded(priv->c, priv->jeb);
> -	}	
> +	}
> +
> +#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
> +	priv->jeb->erase_count++;
> +	priv->c->total_erase_count++;
> +#endif	

See above.  Helper function or unconditional code.

>  	kfree(instr);
>  }
>  #endif /* !__ECOS */
> @@ -349,6 +354,188 @@ fail:
>  	return ret;
>  }
>  
> +#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
> +static int write_erase_count_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
> +{
> +	struct jffs2_raw_node_ref *erase_count_ref = NULL;
> +	struct kvec vecs[1];
> +	int ret;
> +	size_t retlen;
> +	struct jffs2_erase_count_node erase_count_node = {
> +		.magic =        cpu_to_je16(JFFS2_MAGIC_BITMASK),
> +		.nodetype =     cpu_to_je16(JFFS2_NODETYPE_ERASE_COUNT),
> +		.totlen =       cpu_to_je32(c->erasecount_size),
> +		.erase_count =  cpu_to_je32(jeb->erase_count)
> +	};
> +
> +	erase_count_ref = jffs2_alloc_raw_node_ref();
> +	if (!erase_count_ref) {
> +		printk(KERN_WARNING "Failed to allocate raw node ref for erase count node.\n");
> +		return ENOMEM;
                      ^^
Looks like a bug.

> +	}
> +	erase_count_node.hdr_crc = cpu_to_je32(crc32(0, &erase_count_node,
> +					sizeof(struct jffs2_unknown_node)-4));
> + 	erase_count_node.node_crc = cpu_to_je32(crc32(0, &erase_count_node,
> +					sizeof(struct jffs2_erase_count_node)-4));
> +	vecs[0].iov_base = (unsigned char *) &erase_count_node;
> +	vecs[0].iov_len = sizeof(erase_count_node);
> +	ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset + PAD(c->cleanmarker_size), &retlen);

Ferenc did the same thing before, now he has created a helper function
for this.  I guess you should take a look at his patch.

Since the helper function would be useful for both patches, you might
want to add the helper function in a seperate patch which imo could go
right in.

> +
> +	if (ret || retlen != sizeof(erase_count_node)) {
> +		if (ret)
> +			printk(KERN_WARNING "Write erase count to block at 0x%08x failed: %d\n",
> +					jeb->offset, ret);
> +		else
> +			printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
> +					jeb->offset, sizeof(erase_count_node), retlen);

How much of this information is needed to debug problems? I think
jeb->offset, ret and retlen.  So combine the two branches into a
single printk.  Saves both code size and reading time.

> +		jffs2_free_raw_node_ref(erase_count_ref);
> +		return ret;
> +	}
> +
> +	erase_count_ref->next_in_ino = NULL;
> +	erase_count_ref->next_phys = NULL;
> +	erase_count_ref->flash_offset = (jeb->offset + PAD(c->cleanmarker_size)) | REF_NORMAL;
> +	erase_count_ref->__totlen = PAD(c->erasecount_size);
> +
> +	if (!jeb->first_node)
> +		jeb->first_node = erase_count_ref;
> +	if (jeb->last_node)
> +		jeb->last_node->next_phys = erase_count_ref;
> +	jeb->last_node = erase_count_ref;
> +
> +	return 0;
> +}
> +
> +static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
> +{
> +	struct jffs2_raw_node_ref *marker_ref = NULL;
> +	size_t retlen;
> +	int ret;
> +	uint32_t bad_offset;
> +
> +	switch (jffs2_block_check_erase(c, jeb, &bad_offset)) {
> +	case -EAGAIN:   goto refile;
> +	case -EIO:      goto filebad;
> +	}
> +
> +	/* Write the erase complete marker */
> +	D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
> +	bad_offset = jeb->offset;
> +
> +	/* Cleanmarker in oob area or no cleanmarker at all ? */
> +	if (jffs2_cleanmarker_oob(c) || c->cleanmarker_size == 0) {
> +		if (jffs2_cleanmarker_oob(c)) {
> +			if (jffs2_write_nand_cleanmarker(c, jeb))
> +				goto filebad;
> +
> +			jeb->first_node = jeb->last_node = NULL;
> +			jeb->free_size = c->sector_size;
> +			jeb->used_size = 0;
> +			jeb->dirty_size = 0;
> +			jeb->wasted_size = 0;
> +		} else {
> +			ret = write_erase_count_node(c, jeb);
> +
> +			if (ret == ENOMEM)
                                  ^^
Ok, maybe the thing above was not a but, but general convention is to
return -ENOMEM and friends.
       ^
> +				goto refile;
> +			else if (ret)
> +				goto filebad;
> +
> +			jeb->free_size = c->sector_size - PAD(c->erasecount_size);
> +			jeb->used_size = PAD(c->erasecount_size);
> +			jeb->dirty_size = 0;
> +			jeb->wasted_size = 0;                	
> +		}
> +	} else {
> +
> +		struct kvec vecs[1];
> +		struct jffs2_unknown_node marker = {
> +			.magic =        cpu_to_je16(JFFS2_MAGIC_BITMASK),
> +			.nodetype =     cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
> +			.totlen =       cpu_to_je32(c->cleanmarker_size)
> +		};
> +
> +		marker_ref = jffs2_alloc_raw_node_ref();
> +		if (!marker_ref) {
> +			printk(KERN_WARNING "Failed to allocate raw node ref for clean marker. Refiling\n");
> +			goto refile;
> +		}
> +
> +		marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
> +
> +		vecs[0].iov_base = (unsigned char *) &marker;
> +		vecs[0].iov_len = sizeof(marker);
> +		ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen);
> +
> +		if (ret || retlen != sizeof(marker)) {
> +			if (ret)
> +				printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
> +					jeb->offset, ret);
> +			else
> +				printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
> +					jeb->offset, sizeof(marker), retlen);
> +
> +			jffs2_free_raw_node_ref(marker_ref);
> +			goto filebad;
> +		}
> +
> +		marker_ref->next_in_ino = NULL;
> +		marker_ref->next_phys = NULL;
> +		marker_ref->flash_offset = jeb->offset | REF_NORMAL;
> +		marker_ref->__totlen = c->cleanmarker_size;
> +
> +		jeb->first_node = jeb->last_node = marker_ref;
> +
> +		ret = write_erase_count_node(c, jeb);
> +
> +		if (ret) {
> +			jffs2_free_raw_node_ref(marker_ref);
> +			if (ret == ENOMEM)
                                  ^^
> +				goto refile;
> +			else if (ret)
> +				goto filebad;
> +		}
> +
> +		jeb->free_size = c->sector_size - c->cleanmarker_size - PAD(c->erasecount_size);
> +		jeb->used_size = c->cleanmarker_size + PAD(c->erasecount_size);
> +		jeb->dirty_size = 0;
> +		jeb->wasted_size = 0;
> +	}
> +
> +	spin_lock(&c->erase_completion_lock);
> +	c->erasing_size -= c->sector_size;
> +	c->free_size += jeb->free_size;
> +	c->used_size += jeb->used_size;
> +
> +	jffs2_dbg_acct_sanity_check_nolock(c,jeb);
> +	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
> +
> +	list_add_tail(&jeb->list, &c->free_list);
> +	c->nr_erasing_blocks--;
> +	c->nr_free_blocks++;
> +	spin_unlock(&c->erase_completion_lock);
> +	wake_up(&c->erase_wait);
> +	return;
> +
> +filebad:
> +	spin_lock(&c->erase_completion_lock);
> +	/* Stick it on a list (any list) so erase_failed can take it
> +	   right off again.  Silly, but shouldn't happen often. */
> +	list_add(&jeb->list, &c->erasing_list);
> +	spin_unlock(&c->erase_completion_lock);
> +	jffs2_erase_failed(c, jeb, bad_offset);
> +	return;
> +
> +refile:
> +	/* Stick it back on the list from whence it came and come back later */
> +	jffs2_erase_pending_trigger(c);
> +	spin_lock(&c->erase_completion_lock);
> +	list_add(&jeb->list, &c->erase_complete_list);
> +	spin_unlock(&c->erase_completion_lock);
> +	return;
> +}
> +#else
> +
>  static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
>  {
>  	struct jffs2_raw_node_ref *marker_ref = NULL;
> @@ -367,7 +554,6 @@ static void jffs2_mark_erased_block(stru
>  
>  	/* Cleanmarker in oob area or no cleanmarker at all ? */
>  	if (jffs2_cleanmarker_oob(c) || c->cleanmarker_size == 0) {
> -

Can you create a seperate patch for the whitespace cleanups?

>  		if (jffs2_cleanmarker_oob(c)) {
>  			if (jffs2_write_nand_cleanmarker(c, jeb))
>  				goto filebad;
> @@ -378,7 +564,6 @@ static void jffs2_mark_erased_block(stru
>  		jeb->used_size = 0;
>  		jeb->dirty_size = 0;
>  		jeb->wasted_size = 0;
> -
>  	} else {
>  
>  		struct kvec vecs[1];
> @@ -457,3 +642,4 @@ refile:
>  	spin_unlock(&c->erase_completion_lock);
>  	return;
>  }
> +#endif

...

> diff -auNrp ./mtd/include/linux/jffs2_fs_sb.h ./mtd_new/include/linux/jffs2_fs_sb.h
> --- ./mtd/include/linux/jffs2_fs_sb.h	2005-05-20 06:00:14.000000000 +0800
> +++ ./mtd_new/include/linux/jffs2_fs_sb.h	2005-08-18 13:28:36.000000000 +0800
> @@ -40,7 +40,9 @@ struct jffs2_sb_info {
>  					   out-of-order writing of nodes. And GC. */
>  	uint32_t cleanmarker_size;	/* Size of an _inline_ CLEANMARKER
>  					 (i.e. zero for OOB CLEANMARKER */
> -
> +#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
> +	uint32_t erasecount_size;	/* size of an _inline_ erase-count node */
> +#endif
>  	uint32_t flash_size;
>  	uint32_t used_size;
>  	uint32_t dirty_size;
> @@ -89,6 +91,10 @@ struct jffs2_sb_info {
>  	wait_queue_head_t inocache_wq;
>  	struct jffs2_inode_cache **inocache_list;
>  	spinlock_t inocache_lock;
> +
> +#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
> +	uint32_t total_erase_count;	/* The summary erase count of all erase blocks on a Flash  */
> +#endif

This field is write-only?  What did you plan to do with it?

>  	
>  	/* Sem to allow jffs2_garbage_collect_deletion_dirent to
>  	   drop the erase_completion_lock while it's holding a pointer 
> diff -auNrp ./mtd/include/linux/jffs2.h ./mtd_new/include/linux/jffs2.h
> --- ./mtd/include/linux/jffs2.h	2005-07-27 06:00:09.000000000 +0800
> +++ ./mtd_new/include/linux/jffs2.h	2005-08-24 18:25:46.000000000 +0800
> @@ -60,6 +60,8 @@
>  #define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
>  #define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
>  
> +#define JFFS2_NODETYPE_ERASE_COUNT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 5)
> +
>  // Maybe later...
>  //#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
>  //#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
> @@ -146,10 +148,28 @@ struct jffs2_raw_inode
>  	uint8_t data[0];
>  } __attribute__((packed));
>  
> +struct jffs2_erase_count_node
> +{
> +	jint16_t magic;      /* A constant magic number.  */
> +	jint16_t nodetype;   /* == JFFS2_NODETYPE_ERASE_COUNT */
> +	jint32_t totlen;
> +	jint32_t hdr_crc;
> +	jint32_t erase_count; /* used to track the erase counts of each erase block */
> +	jint32_t node_crc;
> +} __attribute__((packed));

I hope Artem is peeking here.  What we could do is split this patch
into two parts:
1. data defitions
2. code to use that data

Then 1) could go in rather quickly and the existance of a struct
jffs2_erase_count_node on flash can be used as an indication of a 1:1
mapping.  We merge the code to deal with a 1:1 mapping, let it settle
for a while and then merge 2).

Comments?

> +struct jffs2_erase_count_node_oob
> +{
> +	jint16_t magic;      /* A constant magic number.  */
> +	jint16_t nodetype;   /* == JFFS2_NODETYPE_ERASE_COUNT */
> +	jint32_t erase_count; /* used to track the erase counts of each erase block */
> +} __attribute__((packed));
> +
>  union jffs2_node_union {
>  	struct jffs2_raw_inode i;
>  	struct jffs2_raw_dirent d;
>  	struct jffs2_unknown_node u;
> +	struct jffs2_erase_count_node e;
>  };
>  
>  #endif /* __LINUX_JFFS2_H__ */


Jörn

-- 
Public Domain  - Free as in Beer
General Public - Free as in Speech
BSD License    - Free as in Enterprise
Shared Source  - Free as in "Work will make you..."

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH]Add erase count tracking support in JFFS2
  2005-08-24 12:05 ` Jörn Engel
@ 2005-08-24 12:45   ` zhao forrest
  0 siblings, 0 replies; 9+ messages in thread
From: zhao forrest @ 2005-08-24 12:45 UTC (permalink / raw)
  To: joern; +Cc: tglx, linux-mtd

>
> > diff -auNrp ./mtd/include/linux/jffs2_fs_sb.h 
./mtd_new/include/linux/jffs2_fs_sb.h
> > --- ./mtd/include/linux/jffs2_fs_sb.h	2005-05-20 06:00:14.000000000 
+0800
> > +++ ./mtd_new/include/linux/jffs2_fs_sb.h	2005-08-18 13:28:36.000000000 
+0800
> > @@ -40,7 +40,9 @@ struct jffs2_sb_info {
> >  					   out-of-order writing of nodes. And GC. */
> >  	uint32_t cleanmarker_size;	/* Size of an _inline_ CLEANMARKER
> >  					 (i.e. zero for OOB CLEANMARKER */
> > -
> > +#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
> > +	uint32_t erasecount_size;	/* size of an _inline_ erase-count node */
> > +#endif
> >  	uint32_t flash_size;
> >  	uint32_t used_size;
> >  	uint32_t dirty_size;
> > @@ -89,6 +91,10 @@ struct jffs2_sb_info {
> >  	wait_queue_head_t inocache_wq;
> >  	struct jffs2_inode_cache **inocache_list;
> >  	spinlock_t inocache_lock;
> > +
> > +#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
> > +	uint32_t total_erase_count;	/* The summary erase count of all erase 
blocks on a Flash  */
> > +#endif
>
>This field is write-only?  What did you plan to do with it?
>

total_erase_count will be used in my next patch for "enhanced 
wear-leveling" patch.
I'll send out another to fix other points soon.

Thanks,
Forrest

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH]Add erase count tracking support in JFFS2
@ 2005-08-25  7:44 zhao forrest
  2005-08-25  8:34 ` Jörn Engel
  0 siblings, 1 reply; 9+ messages in thread
From: zhao forrest @ 2005-08-25  7:44 UTC (permalink / raw)
  To: joern; +Cc: tglx, linux-mtd

[-- Attachment #1: Type: text/plain, Size: 925 bytes --]

Hi,

I refine the code again according to Joern's comments.
Here is an explanation of implementation details.

A new node type JFFS2_NODETYPE_ERASE_COUNT is introduced to store
the erase count of each erase block. So JFFS2_NODETYPE_ERASE_COUNT
is like JFFS2_NODETYPE_CLEANMARKER and is a per-erase-block node.
For NOR flash, JFFS2_NODETYPE_ERASE_COUNT tightly follow the
JFFS2_NODETYPE_CLEANMARKER; for NAND flash, JFFS2_NODETYPE_ERASE
_COUNT is stored in the OOB area of second page.

*How to work?

[FS mounting]
During JFFS2 mounting, the erase count is read out from 
JFFS2_NODETYPE_ERASE_COUNT and is stored in the new field
"erase_count" of struct jffs2_eraseblock.

[erase the block]
Each time the erasing operation of an erase block complete,
increase "erase_count" of struct jffs2_eraseblock. Then write
JFFS2_NODETYPE_ERASE_COUNT to erase block after JFFS2_NODETYPE_
CLEANMARKER has been written.

Thanks,
Forrest


[-- Attachment #2: erase_count_r2.patch --]
[-- Type: application/octet-stream, Size: 19822 bytes --]

diff -auNrp ./mtd/fs/jffs2/build.c ./mtd_new/fs/jffs2/build.c
--- ./mtd/fs/jffs2/build.c	2005-07-31 06:00:11.000000000 +0800
+++ ./mtd_new/fs/jffs2/build.c	2005-08-18 13:33:55.000000000 +0800
@@ -336,6 +336,10 @@ int jffs2_do_mount_fs(struct jffs2_sb_in
 		c->blocks[i].first_node = NULL;
 		c->blocks[i].last_node = NULL;
 		c->blocks[i].bad_count = 0;
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		c->blocks[i].has_erase_count_node = 0;
+		c->blocks[i].erase_count = 0;
+#endif
 	}
 
 	INIT_LIST_HEAD(&c->clean_list);
diff -auNrp ./mtd/fs/jffs2/erase.c ./mtd_new/fs/jffs2/erase.c
--- ./mtd/fs/jffs2/erase.c	2005-07-23 06:00:13.000000000 +0800
+++ ./mtd_new/fs/jffs2/erase.c	2005-08-25 15:14:51.000000000 +0800
@@ -209,7 +209,12 @@ static void jffs2_erase_callback(struct 
 		jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr);
 	} else {
 		jffs2_erase_succeeded(priv->c, priv->jeb);
-	}	
+	}
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	priv->jeb->erase_count++;
+	priv->c->total_erase_count++;
+#endif	
 	kfree(instr);
 }
 #endif /* !__ECOS */
@@ -349,6 +354,188 @@ fail:
 	return ret;
 }
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+static int write_erase_count_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+	struct jffs2_raw_node_ref *erase_count_ref = NULL;
+	struct kvec vecs[1];
+	int ret;
+	size_t retlen;
+	struct jffs2_erase_count_node erase_count_node = {
+		.magic =        cpu_to_je16(JFFS2_MAGIC_BITMASK),
+		.nodetype =     cpu_to_je16(JFFS2_NODETYPE_ERASE_COUNT),
+		.totlen =       cpu_to_je32(c->erasecount_size),
+		.erase_count =  cpu_to_je32(jeb->erase_count)
+	};
+
+	erase_count_ref = jffs2_alloc_raw_node_ref();
+	if (!erase_count_ref) {
+		printk(KERN_WARNING "Failed to allocate raw node ref for erase count node.\n");
+		return -ENOMEM;
+	}
+	erase_count_node.hdr_crc = cpu_to_je32(crc32(0, &erase_count_node,
+					sizeof(struct jffs2_unknown_node)-4));
+ 	erase_count_node.node_crc = cpu_to_je32(crc32(0, &erase_count_node,
+					sizeof(struct jffs2_erase_count_node)-4));
+	vecs[0].iov_base = (unsigned char *) &erase_count_node;
+	vecs[0].iov_len = sizeof(erase_count_node);
+	ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset + PAD(c->cleanmarker_size), &retlen);
+
+	if (ret || retlen != sizeof(erase_count_node)) {
+		if (ret)
+			printk(KERN_WARNING "Write erase count to block at 0x%08x failed: %d\n",
+					jeb->offset, ret);
+		else
+			printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
+					jeb->offset, sizeof(erase_count_node), retlen);
+		jffs2_free_raw_node_ref(erase_count_ref);
+		return ret;
+	}
+
+	erase_count_ref->next_in_ino = NULL;
+	erase_count_ref->next_phys = NULL;
+	erase_count_ref->flash_offset = (jeb->offset + PAD(c->cleanmarker_size)) | REF_NORMAL;
+	erase_count_ref->__totlen = PAD(c->erasecount_size);
+
+	if (!jeb->first_node)
+		jeb->first_node = erase_count_ref;
+	if (jeb->last_node)
+		jeb->last_node->next_phys = erase_count_ref;
+	jeb->last_node = erase_count_ref;
+
+	return 0;
+}
+
+static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+	struct jffs2_raw_node_ref *marker_ref = NULL;
+	size_t retlen;
+	int ret;
+	uint32_t bad_offset;
+
+	switch (jffs2_block_check_erase(c, jeb, &bad_offset)) {
+	case -EAGAIN:   goto refile;
+	case -EIO:      goto filebad;
+	}
+
+	/* Write the erase complete marker */
+	D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
+	bad_offset = jeb->offset;
+
+	/* Cleanmarker in oob area or no cleanmarker at all ? */
+	if (jffs2_cleanmarker_oob(c) || c->cleanmarker_size == 0) {
+		if (jffs2_cleanmarker_oob(c)) {
+			if (jffs2_write_nand_cleanmarker(c, jeb))
+				goto filebad;
+
+			jeb->first_node = jeb->last_node = NULL;
+			jeb->free_size = c->sector_size;
+			jeb->used_size = 0;
+			jeb->dirty_size = 0;
+			jeb->wasted_size = 0;
+		} else {
+			ret = write_erase_count_node(c, jeb);
+
+			if (ret == -ENOMEM)
+				goto refile;
+			else if (ret)
+				goto filebad;
+
+			jeb->free_size = c->sector_size - PAD(c->erasecount_size);
+			jeb->used_size = PAD(c->erasecount_size);
+			jeb->dirty_size = 0;
+			jeb->wasted_size = 0;                	
+		}
+	} else {
+
+		struct kvec vecs[1];
+		struct jffs2_unknown_node marker = {
+			.magic =        cpu_to_je16(JFFS2_MAGIC_BITMASK),
+			.nodetype =     cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
+			.totlen =       cpu_to_je32(c->cleanmarker_size)
+		};
+
+		marker_ref = jffs2_alloc_raw_node_ref();
+		if (!marker_ref) {
+			printk(KERN_WARNING "Failed to allocate raw node ref for clean marker. Refiling\n");
+			goto refile;
+		}
+
+		marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
+
+		vecs[0].iov_base = (unsigned char *) &marker;
+		vecs[0].iov_len = sizeof(marker);
+		ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen);
+
+		if (ret || retlen != sizeof(marker)) {
+			if (ret)
+				printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
+					jeb->offset, ret);
+			else
+				printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
+					jeb->offset, sizeof(marker), retlen);
+
+			jffs2_free_raw_node_ref(marker_ref);
+			goto filebad;
+		}
+
+		marker_ref->next_in_ino = NULL;
+		marker_ref->next_phys = NULL;
+		marker_ref->flash_offset = jeb->offset | REF_NORMAL;
+		marker_ref->__totlen = c->cleanmarker_size;
+
+		jeb->first_node = jeb->last_node = marker_ref;
+
+		ret = write_erase_count_node(c, jeb);
+
+		if (ret) {
+			jffs2_free_raw_node_ref(marker_ref);
+			if (ret == -ENOMEM)
+				goto refile;
+			else if (ret)
+				goto filebad;
+		}
+
+		jeb->free_size = c->sector_size - c->cleanmarker_size - PAD(c->erasecount_size);
+		jeb->used_size = c->cleanmarker_size + PAD(c->erasecount_size);
+		jeb->dirty_size = 0;
+		jeb->wasted_size = 0;
+	}
+
+	spin_lock(&c->erase_completion_lock);
+	c->erasing_size -= c->sector_size;
+	c->free_size += jeb->free_size;
+	c->used_size += jeb->used_size;
+
+	jffs2_dbg_acct_sanity_check_nolock(c,jeb);
+	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
+
+	list_add_tail(&jeb->list, &c->free_list);
+	c->nr_erasing_blocks--;
+	c->nr_free_blocks++;
+	spin_unlock(&c->erase_completion_lock);
+	wake_up(&c->erase_wait);
+	return;
+
+filebad:
+	spin_lock(&c->erase_completion_lock);
+	/* Stick it on a list (any list) so erase_failed can take it
+	   right off again.  Silly, but shouldn't happen often. */
+	list_add(&jeb->list, &c->erasing_list);
+	spin_unlock(&c->erase_completion_lock);
+	jffs2_erase_failed(c, jeb, bad_offset);
+	return;
+
+refile:
+	/* Stick it back on the list from whence it came and come back later */
+	jffs2_erase_pending_trigger(c);
+	spin_lock(&c->erase_completion_lock);
+	list_add(&jeb->list, &c->erase_complete_list);
+	spin_unlock(&c->erase_completion_lock);
+	return;
+}
+#else
+
 static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
 	struct jffs2_raw_node_ref *marker_ref = NULL;
@@ -457,3 +644,4 @@ refile:
 	spin_unlock(&c->erase_completion_lock);
 	return;
 }
+#endif
diff -auNrp ./mtd/fs/jffs2/fs.c ./mtd_new/fs/jffs2/fs.c
--- ./mtd/fs/jffs2/fs.c	2005-08-07 06:00:16.000000000 +0800
+++ ./mtd_new/fs/jffs2/fs.c	2005-08-18 13:32:56.000000000 +0800
@@ -490,6 +490,10 @@ int jffs2_do_fill_super(struct super_blo
 	}
 
 	c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	c->erasecount_size = sizeof(struct jffs2_erase_count_node);
+#endif
 	/* Joern -- stick alignment for weird 8-byte-page flash here */
 
 	/* NAND (or other bizarre) flash... do setup accordingly */
diff -auNrp ./mtd/fs/jffs2/nodelist.h ./mtd_new/fs/jffs2/nodelist.h
--- ./mtd/fs/jffs2/nodelist.h	2005-08-18 06:00:12.000000000 +0800
+++ ./mtd_new/fs/jffs2/nodelist.h	2005-08-18 13:32:02.000000000 +0800
@@ -195,6 +195,11 @@ struct jffs2_eraseblock
 	struct jffs2_raw_node_ref *last_node;
 
 	struct jffs2_raw_node_ref *gc_node;	/* Next node to be garbage collected */
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	uint8_t has_erase_count_node;   /* Whether an erase_count_node is found for this erase block */
+	uint32_t erase_count;           /* Keep track of the erase count of this erase block */
+#endif
 };
 
 /* Calculate totlen from surrounding nodes or eraseblock */
diff -auNrp ./mtd/fs/jffs2/nodemgmt.c ./mtd_new/fs/jffs2/nodemgmt.c
--- ./mtd/fs/jffs2/nodemgmt.c	2005-07-21 06:00:12.000000000 +0800
+++ ./mtd_new/fs/jffs2/nodemgmt.c	2005-08-18 13:33:30.000000000 +0800
@@ -258,7 +258,12 @@ static int jffs2_do_reserve_space(struct
 		c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list);
 		c->nr_free_blocks--;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		if ((jeb->free_size != c->sector_size - c->cleanmarker_size) &&
+			(jeb->free_size != c->sector_size - c->cleanmarker_size - c->erasecount_size)){
+#else
 		if (jeb->free_size != c->sector_size - c->cleanmarker_size) {
+#endif
 			printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size);
 			goto restart;
 		}
diff -auNrp ./mtd/fs/jffs2/scan.c ./mtd_new/fs/jffs2/scan.c
--- ./mtd/fs/jffs2/scan.c	2005-07-21 06:00:12.000000000 +0800
+++ ./mtd_new/fs/jffs2/scan.c	2005-08-18 13:32:31.000000000 +0800
@@ -438,6 +438,14 @@ scan_more:	
 				return BLK_STATE_CLEANMARKER;
 			}
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+			if (jeb->has_erase_count_node && jeb->used_size == (PAD(c->cleanmarker_size)
+			    +PAD(c->erasecount_size)) && !jeb->dirty_size) {
+				D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size)));
+				return BLK_STATE_CLEANMARKER;
+			}
+#endif
+
 			/* See how much more there is to read in this eraseblock... */
 			buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
 			if (!buf_len) {
@@ -581,6 +589,61 @@ scan_more:	
 			}
 			break;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		case JFFS2_NODETYPE_ERASE_COUNT:
+			if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
+				buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+				D1(printk(KERN_DEBUG "Fewer than %d bytes (erase count node) left to end of buf. Reading 0x%x at 0x%08x\n",
+					  je32_to_cpu(node->totlen), buf_len, ofs));
+				err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
+				if (err)
+					return err;
+				buf_ofs = ofs;
+				node = (void *)buf;
+			}
+			if (je32_to_cpu(node->totlen) != c->erasecount_size) {
+				printk(KERN_NOTICE "ERASE_COUNT node found at 0x%08x has totlen 0x%x != normal 0x%x\n",
+				      ofs, je32_to_cpu(node->totlen), sizeof(struct jffs2_erase_count_node));
+				DIRTY_SPACE(PAD(sizeof(struct jffs2_erase_count_node)));
+				ofs += PAD(sizeof(struct jffs2_erase_count_node));
+			} else if (ofs != jeb->offset + PAD(c->cleanmarker_size)) {
+				printk(KERN_NOTICE "ERASE_COUNT node found at 0x%08x, not closely follow the CLEANMARKER node in block (0x%08x)\n", ofs, jeb->offset);
+				DIRTY_SPACE(PAD(sizeof(struct jffs2_erase_count_node)));
+				ofs += PAD(sizeof(struct jffs2_erase_count_node));
+			} else {
+				uint32_t crc;
+				struct jffs2_erase_count_node *erase_count_node = (struct jffs2_erase_count_node *)node;
+				struct jffs2_raw_node_ref *erase_count_ref = jffs2_alloc_raw_node_ref();
+				if (!erase_count_ref) {
+					printk(KERN_NOTICE "Failed to allocate node ref for erase count\n");
+					return -ENOMEM;
+				}
+				crc = crc32(0, erase_count_node, sizeof(*erase_count_node) - 4);
+				if (crc != je32_to_cpu(erase_count_node->node_crc)) {
+					printk(KERN_NOTICE "ERASE_COUNT node found at 0x%08x, but CRC failed: Read 0x%08x, calculated 0x%08x\n", ofs, je32_to_cpu(erase_count_node->node_crc), crc);
+					DIRTY_SPACE(PAD(sizeof(struct jffs2_erase_count_node)));
+					ofs += PAD(sizeof(struct jffs2_erase_count_node));
+					jffs2_free_raw_node_ref(erase_count_ref);
+					break;
+				}
+				jeb->has_erase_count_node = 1;
+				jeb->erase_count = je32_to_cpu(erase_count_node->erase_count);
+				c->total_erase_count += jeb->erase_count;
+				erase_count_ref->next_in_ino = NULL;
+				erase_count_ref->next_phys = NULL;
+				erase_count_ref->flash_offset = ofs | REF_NORMAL;
+				erase_count_ref->__totlen = PAD(c->erasecount_size);
+				if (!jeb->first_node)
+					jeb->first_node = erase_count_ref;
+				if (jeb->last_node)
+					jeb->last_node->next_phys = erase_count_ref;
+				jeb->last_node = erase_count_ref;
+				USED_SPACE(PAD(c->erasecount_size));
+				ofs += PAD(c->erasecount_size);
+			}
+			break;
+#endif
+
 		case JFFS2_NODETYPE_PADDING:
 			DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
 			ofs += PAD(je32_to_cpu(node->totlen));
diff -auNrp ./mtd/fs/jffs2/wbuf.c ./mtd_new/fs/jffs2/wbuf.c
--- ./mtd/fs/jffs2/wbuf.c	2005-08-07 06:00:16.000000000 +0800
+++ ./mtd_new/fs/jffs2/wbuf.c	2005-08-24 18:26:50.000000000 +0800
@@ -976,8 +976,28 @@ int jffs2_check_oob_empty( struct jffs2_
 		}
 	}
 
-	/* we know, we are aligned :) */	
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	if (jeb->has_erase_count_node) {
+		for (i = 0; i < oob_size ; i++) {
+			if (i >= c->fsdata_pos && i < c->fsdata_pos + c->fsdata_len)
+				continue;
+
+			if (buf[oob_size + i] != 0xFF) {
+				D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n",
+					buf[oob_size + i], oob_size + i, jeb->offset));
+				ret = 1;
+				goto out;
+			}
+		}
+	}
+#endif
+
+	/* we know, we are aligned :) */
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	for (page = (oob_size * 2); page < len; page += sizeof(long)) {
+#else	
 	for (page = oob_size; page < len; page += sizeof(long)) {
+#endif
 		unsigned long dat = *(unsigned long *)(&buf[page]);
 		if(dat != -1) {
 			ret = 1; 
@@ -1005,7 +1025,9 @@ int jffs2_check_nand_cleanmarker (struct
 	int ret, i, cnt, retval = 0;
 	size_t retlen, offset;
 	int oob_size;
-
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	struct jffs2_erase_count_node_oob *e;
+#endif
 	offset = jeb->offset;
 	oob_size = c->mtd->oobsize;
 
@@ -1052,6 +1074,15 @@ int jffs2_check_nand_cleanmarker (struct
 				}
 				printk("\n");
 			})
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+			e = (struct jffs2_erase_count_node_oob *) &buf[oob_size + c->fsdata_pos];
+			if (je16_to_cpu (e->magic) == JFFS2_MAGIC_BITMASK &&
+			    je16_to_cpu (e->nodetype) == JFFS2_NODETYPE_ERASE_COUNT) {
+				jeb->has_erase_count_node = 1;
+				jeb->erase_count = je32_to_cpu (e->erase_count);
+				c->total_erase_count += jeb->erase_count;
+			}
+#endif
 		}
 		offset += c->mtd->erasesize;
 	}
@@ -1061,6 +1092,9 @@ int jffs2_check_nand_cleanmarker (struct
 int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
 	struct 	jffs2_unknown_node n;
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	struct jffs2_erase_count_node_oob ec;
+#endif
 	int 	ret;
 	size_t 	retlen;
 
@@ -1078,6 +1112,23 @@ int jffs2_write_nand_cleanmarker(struct 
 		D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len));
 		return ret;
 	}
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	ec.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	ec.nodetype = cpu_to_je16(JFFS2_NODETYPE_ERASE_COUNT);
+	ec.erase_count = cpu_to_je32(jeb->erase_count);
+
+	ret = jffs2_flash_write_oob(c, jeb->offset + c->mtd->oobblock + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&ec);
+
+	if (ret) {
+		D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
+		return ret;
+	}
+	if (retlen != c->fsdata_len) {
+		D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len));
+		return ret;
+	}
+#endif
 	return 0;
 }
 
@@ -1130,6 +1181,10 @@ static int jffs2_nand_set_oobinfo(struct
 	/* Cleanmarker is out-of-band, so inline size zero */
 	c->cleanmarker_size = 0;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	c->erasecount_size = 0;
+#endif
+
 	/* Should we use autoplacement ? */
 	if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) {
 		D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n"));
@@ -1218,6 +1273,9 @@ int jffs2_nor_ecc_flash_setup(struct jff
 	/* Cleanmarker is actually larger on the flashes */
 	c->cleanmarker_size = 16;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	c->erasecount_size = 24;
+#endif
 	/* Initialize write buffer */
 	init_rwsem(&c->wbuf_sem);
 	c->wbuf_pagesize = c->mtd->eccsize;
diff -auNrp ./mtd/fs/Kconfig ./mtd_new/fs/Kconfig
--- ./mtd/fs/Kconfig	2005-05-10 06:00:10.000000000 +0800
+++ ./mtd_new/fs/Kconfig	2005-08-18 13:45:11.000000000 +0800
@@ -64,6 +64,16 @@ config JFFS2_FS_WRITEBUFFER
 	    - NOR flash with transparent ECC
 	    - DataFlash
 
+config JFFS2_FS_ERASE_COUNT_TRACKING
+	bool "JFFS2 erase count tracking support"
+	depends on JFFS2_FS
+	default y
+	help
+	  This enables the erase count tracking support in JFFS2.
+
+	  This functionality provides the support to track the erase count of
+	  each erase block.
+
 config JFFS2_COMPRESSION_OPTIONS
 	bool "Advanced compression options for JFFS2"
 	default n
diff -auNrp ./mtd/include/linux/jffs2_fs_sb.h ./mtd_new/include/linux/jffs2_fs_sb.h
--- ./mtd/include/linux/jffs2_fs_sb.h	2005-05-20 06:00:14.000000000 +0800
+++ ./mtd_new/include/linux/jffs2_fs_sb.h	2005-08-18 13:28:36.000000000 +0800
@@ -40,7 +40,9 @@ struct jffs2_sb_info {
 					   out-of-order writing of nodes. And GC. */
 	uint32_t cleanmarker_size;	/* Size of an _inline_ CLEANMARKER
 					 (i.e. zero for OOB CLEANMARKER */
-
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	uint32_t erasecount_size;	/* size of an _inline_ erase-count node */
+#endif
 	uint32_t flash_size;
 	uint32_t used_size;
 	uint32_t dirty_size;
@@ -89,6 +91,10 @@ struct jffs2_sb_info {
 	wait_queue_head_t inocache_wq;
 	struct jffs2_inode_cache **inocache_list;
 	spinlock_t inocache_lock;
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	uint32_t total_erase_count;	/* The summary erase count of all erase blocks on a Flash  */
+#endif
 	
 	/* Sem to allow jffs2_garbage_collect_deletion_dirent to
 	   drop the erase_completion_lock while it's holding a pointer 
diff -auNrp ./mtd/include/linux/jffs2.h ./mtd_new/include/linux/jffs2.h
--- ./mtd/include/linux/jffs2.h	2005-07-27 06:00:09.000000000 +0800
+++ ./mtd_new/include/linux/jffs2.h	2005-08-24 18:25:46.000000000 +0800
@@ -60,6 +60,8 @@
 #define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
 #define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
 
+#define JFFS2_NODETYPE_ERASE_COUNT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 5)
+
 // Maybe later...
 //#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
 //#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
@@ -146,10 +148,28 @@ struct jffs2_raw_inode
 	uint8_t data[0];
 } __attribute__((packed));
 
+struct jffs2_erase_count_node
+{
+	jint16_t magic;      /* A constant magic number.  */
+	jint16_t nodetype;   /* == JFFS2_NODETYPE_ERASE_COUNT */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t erase_count; /* used to track the erase counts of each erase block */
+	jint32_t node_crc;
+} __attribute__((packed));
+
+struct jffs2_erase_count_node_oob
+{
+	jint16_t magic;      /* A constant magic number.  */
+	jint16_t nodetype;   /* == JFFS2_NODETYPE_ERASE_COUNT */
+	jint32_t erase_count; /* used to track the erase counts of each erase block */
+} __attribute__((packed));
+
 union jffs2_node_union {
 	struct jffs2_raw_inode i;
 	struct jffs2_raw_dirent d;
 	struct jffs2_unknown_node u;
+	struct jffs2_erase_count_node e;
 };
 
 #endif /* __LINUX_JFFS2_H__ */

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH]Add erase count tracking support in JFFS2
  2005-08-25  7:44 [PATCH]Add erase count tracking support in JFFS2 zhao forrest
@ 2005-08-25  8:34 ` Jörn Engel
  0 siblings, 0 replies; 9+ messages in thread
From: Jörn Engel @ 2005-08-25  8:34 UTC (permalink / raw)
  To: zhao forrest; +Cc: tglx, linux-mtd

On Thu, 25 August 2005 15:44:37 +0800, zhao forrest wrote:
> 
> I refine the code again according to Joern's comments.
> Here is an explanation of implementation details.
> 
> A new node type JFFS2_NODETYPE_ERASE_COUNT is introduced to store
> the erase count of each erase block. So JFFS2_NODETYPE_ERASE_COUNT
> is like JFFS2_NODETYPE_CLEANMARKER and is a per-erase-block node.
> For NOR flash, JFFS2_NODETYPE_ERASE_COUNT tightly follow the
> JFFS2_NODETYPE_CLEANMARKER; for NAND flash, JFFS2_NODETYPE_ERASE
> _COUNT is stored in the OOB area of second page.
> 
> *How to work?
> 
> [FS mounting]
> During JFFS2 mounting, the erase count is read out from 
> JFFS2_NODETYPE_ERASE_COUNT and is stored in the new field
> "erase_count" of struct jffs2_eraseblock.
> 
> [erase the block]
> Each time the erasing operation of an erase block complete,
> increase "erase_count" of struct jffs2_eraseblock. Then write
> JFFS2_NODETYPE_ERASE_COUNT to erase block after JFFS2_NODETYPE_
> CLEANMARKER has been written.

Nice explanation, thanks.  How are you going to deal with the race
condition during block erase?  If the system fails during that time,
the erase count is lost.

Some options:
o Pick the average of all existing erase counts.
o Pick zero, if no block contains erase counts.  This is the average,
  in a way.
o Pick the average plus "a bit".  With this policy, the block won't be
  erased for a while, so it can't continuously hit the same race.
o Arrange erase blocks in pairs and store the erase count for the
  block itself and its buddy.  The two buddies in a pair are
  alternately erased, so we always have a backup of the current erase
  count.

> diff -auNrp ./mtd/fs/jffs2/build.c ./mtd_new/fs/jffs2/build.c
> --- ./mtd/fs/jffs2/build.c	2005-07-31 06:00:11.000000000 +0800
> +++ ./mtd_new/fs/jffs2/build.c	2005-08-18 13:33:55.000000000 +0800
> @@ -336,6 +336,10 @@ int jffs2_do_mount_fs(struct jffs2_sb_in
>  		c->blocks[i].first_node = NULL;
>  		c->blocks[i].last_node = NULL;
>  		c->blocks[i].bad_count = 0;
> +#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
> +		c->blocks[i].has_erase_count_node = 0;
> +		c->blocks[i].erase_count = 0;
> +#endif

This is still unchanged. *grumble*

>  	}
>  
>  	INIT_LIST_HEAD(&c->clean_list);
> diff -auNrp ./mtd/fs/jffs2/erase.c ./mtd_new/fs/jffs2/erase.c
> --- ./mtd/fs/jffs2/erase.c	2005-07-23 06:00:13.000000000 +0800
> +++ ./mtd_new/fs/jffs2/erase.c	2005-08-25 15:14:51.000000000 +0800
> @@ -209,7 +209,12 @@ static void jffs2_erase_callback(struct 
>  		jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr);
>  	} else {
>  		jffs2_erase_succeeded(priv->c, priv->jeb);
> -	}	
> +	}
> +
> +#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
> +	priv->jeb->erase_count++;
> +	priv->c->total_erase_count++;
> +#endif	

*grumble*

>  	kfree(instr);
>  }
>  #endif /* !__ECOS */
> @@ -349,6 +354,188 @@ fail:
>  	return ret;
>  }
>  
> +#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
> +static int write_erase_count_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
> +{
> +	struct jffs2_raw_node_ref *erase_count_ref = NULL;
> +	struct kvec vecs[1];
> +	int ret;
> +	size_t retlen;
> +	struct jffs2_erase_count_node erase_count_node = {
> +		.magic =        cpu_to_je16(JFFS2_MAGIC_BITMASK),
> +		.nodetype =     cpu_to_je16(JFFS2_NODETYPE_ERASE_COUNT),
> +		.totlen =       cpu_to_je32(c->erasecount_size),
> +		.erase_count =  cpu_to_je32(jeb->erase_count)
> +	};
> +
> +	erase_count_ref = jffs2_alloc_raw_node_ref();
> +	if (!erase_count_ref) {
> +		printk(KERN_WARNING "Failed to allocate raw node ref for erase count node.\n");
> +		return -ENOMEM;
> +	}
> +	erase_count_node.hdr_crc = cpu_to_je32(crc32(0, &erase_count_node,
> +					sizeof(struct jffs2_unknown_node)-4));
> + 	erase_count_node.node_crc = cpu_to_je32(crc32(0, &erase_count_node,
> +					sizeof(struct jffs2_erase_count_node)-4));
> +	vecs[0].iov_base = (unsigned char *) &erase_count_node;
> +	vecs[0].iov_len = sizeof(erase_count_node);
> +	ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset + PAD(c->cleanmarker_size), &retlen);
> +
> +	if (ret || retlen != sizeof(erase_count_node)) {
> +		if (ret)
> +			printk(KERN_WARNING "Write erase count to block at 0x%08x failed: %d\n",
> +					jeb->offset, ret);
> +		else
> +			printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
> +					jeb->offset, sizeof(erase_count_node), retlen);

*grumble*

I will stop the review here.  You still have work left from the last
round.

Jörn

-- 
Why do musicians compose symphonies and poets write poems?
They do it because life wouldn't have any meaning for them if they didn't.
That's why I draw cartoons.  It's my life.
-- Charles Shultz

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH]Add erase count tracking support in JFFS2
@ 2005-08-25  9:59 zhao forrest
  0 siblings, 0 replies; 9+ messages in thread
From: zhao forrest @ 2005-08-25  9:59 UTC (permalink / raw)
  To: joern; +Cc: tglx, linux-mtd

[-- Attachment #1: Type: text/plain, Size: 1638 bytes --]

>
>Nice explanation, thanks.  How are you going to deal with the race
>condition during block erase?  If the system fails during that time,
>the erase count is lost.
>
>Some options:
>o Pick the average of all existing erase counts.
>o Pick zero, if no block contains erase counts.  This is the average,
>   in a way.
>o Pick the average plus "a bit".  With this policy, the block won't be
>   erased for a while, so it can't continuously hit the same race.
>o Arrange erase blocks in pairs and store the erase count for the
>   block itself and its buddy.  The two buddies in a pair are
>   alternately erased, so we always have a backup of the current erase
>   count.

I choose the first option. Because after we implement the enhanced
wear-leveling algorithm based on erase count in the future, the average
of total erase count would make more sense.


> > diff -auNrp ./mtd/fs/jffs2/build.c ./mtd_new/fs/jffs2/build.c
> > --- ./mtd/fs/jffs2/build.c	2005-07-31 06:00:11.000000000 +0800
> > +++ ./mtd_new/fs/jffs2/build.c	2005-08-18 13:33:55.000000000 +0800
> > @@ -336,6 +336,10 @@ int jffs2_do_mount_fs(struct jffs2_sb_in
> >  		c->blocks[i].first_node = NULL;
> >  		c->blocks[i].last_node = NULL;
> >  		c->blocks[i].bad_count = 0;
> > +#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
> > +		c->blocks[i].has_erase_count_node = 0;
> > +		c->blocks[i].erase_count = 0;
> > +#endif
>
>This is still unchanged. *grumble*
>

mmmh...Gleixner suggest I include all the patch code in #ifdef. Then
after a period of practical using, we'll remove all #ifdef if we
think the patch is good.
I have added additional comment for it in fs/Kconfig.


[-- Attachment #2: erase_count_r3.patch --]
[-- Type: application/octet-stream, Size: 19934 bytes --]

diff -auNrp ./mtd/fs/jffs2/build.c ./mtd_new/fs/jffs2/build.c
--- ./mtd/fs/jffs2/build.c	2005-07-31 06:00:11.000000000 +0800
+++ ./mtd_new/fs/jffs2/build.c	2005-08-18 13:33:55.000000000 +0800
@@ -336,6 +336,10 @@ int jffs2_do_mount_fs(struct jffs2_sb_in
 		c->blocks[i].first_node = NULL;
 		c->blocks[i].last_node = NULL;
 		c->blocks[i].bad_count = 0;
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		c->blocks[i].has_erase_count_node = 0;
+		c->blocks[i].erase_count = 0;
+#endif
 	}
 
 	INIT_LIST_HEAD(&c->clean_list);
diff -auNrp ./mtd/fs/jffs2/erase.c ./mtd_new/fs/jffs2/erase.c
--- ./mtd/fs/jffs2/erase.c	2005-07-23 06:00:13.000000000 +0800
+++ ./mtd_new/fs/jffs2/erase.c	2005-08-25 17:51:32.000000000 +0800
@@ -209,7 +209,16 @@ static void jffs2_erase_callback(struct 
 		jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr);
 	} else {
 		jffs2_erase_succeeded(priv->c, priv->jeb);
-	}	
+	}
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	if (!priv->jeb->has_erase_count_node) {
+		priv->jeb->erase_count = priv->c->total_erase_count/priv->c->nr_blocks;
+		priv->jeb->has_erase_count_node = 1;
+	}
+	priv->jeb->erase_count++;
+	priv->c->total_erase_count++;
+#endif	
 	kfree(instr);
 }
 #endif /* !__ECOS */
@@ -349,6 +358,184 @@ fail:
 	return ret;
 }
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+static int write_erase_count_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+	struct jffs2_raw_node_ref *erase_count_ref = NULL;
+	struct kvec vecs[1];
+	int ret;
+	size_t retlen;
+	struct jffs2_erase_count_node erase_count_node = {
+		.magic =        cpu_to_je16(JFFS2_MAGIC_BITMASK),
+		.nodetype =     cpu_to_je16(JFFS2_NODETYPE_ERASE_COUNT),
+		.totlen =       cpu_to_je32(c->erasecount_size),
+		.erase_count =  cpu_to_je32(jeb->erase_count)
+	};
+
+	erase_count_ref = jffs2_alloc_raw_node_ref();
+	if (!erase_count_ref) {
+		printk(KERN_WARNING "Failed to allocate raw node ref for erase count node.\n");
+		return -ENOMEM;
+	}
+	erase_count_node.hdr_crc = cpu_to_je32(crc32(0, &erase_count_node,
+					sizeof(struct jffs2_unknown_node)-4));
+ 	erase_count_node.node_crc = cpu_to_je32(crc32(0, &erase_count_node,
+					sizeof(struct jffs2_erase_count_node)-4));
+	vecs[0].iov_base = (unsigned char *) &erase_count_node;
+	vecs[0].iov_len = sizeof(erase_count_node);
+	ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset + PAD(c->cleanmarker_size), &retlen);
+
+	if (ret || retlen != sizeof(erase_count_node)) {
+		if (ret)
+			printk(KERN_WARNING "Write erase count to block at 0x%08x failed: %d\n",
+					jeb->offset, ret);
+		else
+			printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
+					jeb->offset, sizeof(erase_count_node), retlen);
+		jffs2_free_raw_node_ref(erase_count_ref);
+		return ret;
+	}
+
+	erase_count_ref->next_in_ino = NULL;
+	erase_count_ref->next_phys = NULL;
+	erase_count_ref->flash_offset = (jeb->offset + PAD(c->cleanmarker_size)) | REF_NORMAL;
+	erase_count_ref->__totlen = PAD(c->erasecount_size);
+
+	if (!jeb->first_node)
+		jeb->first_node = erase_count_ref;
+	if (jeb->last_node)
+		jeb->last_node->next_phys = erase_count_ref;
+	jeb->last_node = erase_count_ref;
+
+	return 0;
+}
+
+static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+	struct jffs2_raw_node_ref *marker_ref = NULL;
+	size_t retlen;
+	int ret;
+	uint32_t bad_offset;
+
+	switch (jffs2_block_check_erase(c, jeb, &bad_offset)) {
+	case -EAGAIN:   goto refile;
+	case -EIO:      goto filebad;
+	}
+
+	/* Write the erase complete marker */
+	D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
+	bad_offset = jeb->offset;
+
+	/* Cleanmarker in oob area or no cleanmarker at all ? */
+	if (jffs2_cleanmarker_oob(c) || c->cleanmarker_size == 0) {
+		if (jffs2_cleanmarker_oob(c)) {
+			if (jffs2_write_nand_cleanmarker(c, jeb))
+				goto filebad;
+
+			jeb->first_node = jeb->last_node = NULL;
+			jeb->free_size = c->sector_size;
+			jeb->used_size = 0;
+			jeb->dirty_size = 0;
+			jeb->wasted_size = 0;
+		} else {
+			ret = write_erase_count_node(c, jeb);
+
+			if (ret == -ENOMEM)
+				goto refile;
+			else if (ret)
+				goto filebad;
+
+			jeb->free_size = c->sector_size - PAD(c->erasecount_size);
+			jeb->used_size = PAD(c->erasecount_size);
+			jeb->dirty_size = 0;
+			jeb->wasted_size = 0;                	
+		}
+	} else {
+
+		struct kvec vecs[1];
+		struct jffs2_unknown_node marker = {
+			.magic =        cpu_to_je16(JFFS2_MAGIC_BITMASK),
+			.nodetype =     cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
+			.totlen =       cpu_to_je32(c->cleanmarker_size)
+		};
+
+		marker_ref = jffs2_alloc_raw_node_ref();
+		if (!marker_ref) {
+			printk(KERN_WARNING "Failed to allocate raw node ref for clean marker. Refiling\n");
+			goto refile;
+		}
+
+		marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
+
+		vecs[0].iov_base = (unsigned char *) &marker;
+		vecs[0].iov_len = sizeof(marker);
+		ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen);
+
+		if (ret || retlen != sizeof(marker)) {
+			printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
+					jeb->offset, ret);
+
+			jffs2_free_raw_node_ref(marker_ref);
+			goto filebad;
+		}
+
+		marker_ref->next_in_ino = NULL;
+		marker_ref->next_phys = NULL;
+		marker_ref->flash_offset = jeb->offset | REF_NORMAL;
+		marker_ref->__totlen = c->cleanmarker_size;
+
+		jeb->first_node = jeb->last_node = marker_ref;
+
+		ret = write_erase_count_node(c, jeb);
+
+		if (ret) {
+			jffs2_free_raw_node_ref(marker_ref);
+			if (ret == -ENOMEM)
+				goto refile;
+			else if (ret)
+				goto filebad;
+		}
+
+		jeb->free_size = c->sector_size - c->cleanmarker_size - PAD(c->erasecount_size);
+		jeb->used_size = c->cleanmarker_size + PAD(c->erasecount_size);
+		jeb->dirty_size = 0;
+		jeb->wasted_size = 0;
+	}
+
+	spin_lock(&c->erase_completion_lock);
+	c->erasing_size -= c->sector_size;
+	c->free_size += jeb->free_size;
+	c->used_size += jeb->used_size;
+
+	jffs2_dbg_acct_sanity_check_nolock(c,jeb);
+	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
+
+	list_add_tail(&jeb->list, &c->free_list);
+	c->nr_erasing_blocks--;
+	c->nr_free_blocks++;
+	spin_unlock(&c->erase_completion_lock);
+	wake_up(&c->erase_wait);
+	return;
+
+filebad:
+	spin_lock(&c->erase_completion_lock);
+	/* Stick it on a list (any list) so erase_failed can take it
+	   right off again.  Silly, but shouldn't happen often. */
+	list_add(&jeb->list, &c->erasing_list);
+	spin_unlock(&c->erase_completion_lock);
+	jffs2_erase_failed(c, jeb, bad_offset);
+	return;
+
+refile:
+	/* Stick it back on the list from whence it came and come back later */
+	jffs2_erase_pending_trigger(c);
+	spin_lock(&c->erase_completion_lock);
+	list_add(&jeb->list, &c->erase_complete_list);
+	spin_unlock(&c->erase_completion_lock);
+	return;
+}
+#else
+
 static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
 	struct jffs2_raw_node_ref *marker_ref = NULL;
@@ -457,3 +644,4 @@ refile:
 	spin_unlock(&c->erase_completion_lock);
 	return;
 }
+#endif
diff -auNrp ./mtd/fs/jffs2/fs.c ./mtd_new/fs/jffs2/fs.c
--- ./mtd/fs/jffs2/fs.c	2005-08-07 06:00:16.000000000 +0800
+++ ./mtd_new/fs/jffs2/fs.c	2005-08-18 13:32:56.000000000 +0800
@@ -490,6 +490,10 @@ int jffs2_do_fill_super(struct super_blo
 	}
 
 	c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	c->erasecount_size = sizeof(struct jffs2_erase_count_node);
+#endif
 	/* Joern -- stick alignment for weird 8-byte-page flash here */
 
 	/* NAND (or other bizarre) flash... do setup accordingly */
diff -auNrp ./mtd/fs/jffs2/nodelist.h ./mtd_new/fs/jffs2/nodelist.h
--- ./mtd/fs/jffs2/nodelist.h	2005-08-18 06:00:12.000000000 +0800
+++ ./mtd_new/fs/jffs2/nodelist.h	2005-08-18 13:32:02.000000000 +0800
@@ -195,6 +195,11 @@ struct jffs2_eraseblock
 	struct jffs2_raw_node_ref *last_node;
 
 	struct jffs2_raw_node_ref *gc_node;	/* Next node to be garbage collected */
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	uint8_t has_erase_count_node;   /* Whether an erase_count_node is found for this erase block */
+	uint32_t erase_count;           /* Keep track of the erase count of this erase block */
+#endif
 };
 
 /* Calculate totlen from surrounding nodes or eraseblock */
diff -auNrp ./mtd/fs/jffs2/nodemgmt.c ./mtd_new/fs/jffs2/nodemgmt.c
--- ./mtd/fs/jffs2/nodemgmt.c	2005-07-21 06:00:12.000000000 +0800
+++ ./mtd_new/fs/jffs2/nodemgmt.c	2005-08-18 13:33:30.000000000 +0800
@@ -258,7 +258,12 @@ static int jffs2_do_reserve_space(struct
 		c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list);
 		c->nr_free_blocks--;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		if ((jeb->free_size != c->sector_size - c->cleanmarker_size) &&
+			(jeb->free_size != c->sector_size - c->cleanmarker_size - c->erasecount_size)){
+#else
 		if (jeb->free_size != c->sector_size - c->cleanmarker_size) {
+#endif
 			printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size);
 			goto restart;
 		}
diff -auNrp ./mtd/fs/jffs2/scan.c ./mtd_new/fs/jffs2/scan.c
--- ./mtd/fs/jffs2/scan.c	2005-07-21 06:00:12.000000000 +0800
+++ ./mtd_new/fs/jffs2/scan.c	2005-08-18 13:32:31.000000000 +0800
@@ -438,6 +438,14 @@ scan_more:	
 				return BLK_STATE_CLEANMARKER;
 			}
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+			if (jeb->has_erase_count_node && jeb->used_size == (PAD(c->cleanmarker_size)
+			    +PAD(c->erasecount_size)) && !jeb->dirty_size) {
+				D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size)));
+				return BLK_STATE_CLEANMARKER;
+			}
+#endif
+
 			/* See how much more there is to read in this eraseblock... */
 			buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
 			if (!buf_len) {
@@ -581,6 +589,61 @@ scan_more:	
 			}
 			break;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		case JFFS2_NODETYPE_ERASE_COUNT:
+			if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
+				buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+				D1(printk(KERN_DEBUG "Fewer than %d bytes (erase count node) left to end of buf. Reading 0x%x at 0x%08x\n",
+					  je32_to_cpu(node->totlen), buf_len, ofs));
+				err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
+				if (err)
+					return err;
+				buf_ofs = ofs;
+				node = (void *)buf;
+			}
+			if (je32_to_cpu(node->totlen) != c->erasecount_size) {
+				printk(KERN_NOTICE "ERASE_COUNT node found at 0x%08x has totlen 0x%x != normal 0x%x\n",
+				      ofs, je32_to_cpu(node->totlen), sizeof(struct jffs2_erase_count_node));
+				DIRTY_SPACE(PAD(sizeof(struct jffs2_erase_count_node)));
+				ofs += PAD(sizeof(struct jffs2_erase_count_node));
+			} else if (ofs != jeb->offset + PAD(c->cleanmarker_size)) {
+				printk(KERN_NOTICE "ERASE_COUNT node found at 0x%08x, not closely follow the CLEANMARKER node in block (0x%08x)\n", ofs, jeb->offset);
+				DIRTY_SPACE(PAD(sizeof(struct jffs2_erase_count_node)));
+				ofs += PAD(sizeof(struct jffs2_erase_count_node));
+			} else {
+				uint32_t crc;
+				struct jffs2_erase_count_node *erase_count_node = (struct jffs2_erase_count_node *)node;
+				struct jffs2_raw_node_ref *erase_count_ref = jffs2_alloc_raw_node_ref();
+				if (!erase_count_ref) {
+					printk(KERN_NOTICE "Failed to allocate node ref for erase count\n");
+					return -ENOMEM;
+				}
+				crc = crc32(0, erase_count_node, sizeof(*erase_count_node) - 4);
+				if (crc != je32_to_cpu(erase_count_node->node_crc)) {
+					printk(KERN_NOTICE "ERASE_COUNT node found at 0x%08x, but CRC failed: Read 0x%08x, calculated 0x%08x\n", ofs, je32_to_cpu(erase_count_node->node_crc), crc);
+					DIRTY_SPACE(PAD(sizeof(struct jffs2_erase_count_node)));
+					ofs += PAD(sizeof(struct jffs2_erase_count_node));
+					jffs2_free_raw_node_ref(erase_count_ref);
+					break;
+				}
+				jeb->has_erase_count_node = 1;
+				jeb->erase_count = je32_to_cpu(erase_count_node->erase_count);
+				c->total_erase_count += jeb->erase_count;
+				erase_count_ref->next_in_ino = NULL;
+				erase_count_ref->next_phys = NULL;
+				erase_count_ref->flash_offset = ofs | REF_NORMAL;
+				erase_count_ref->__totlen = PAD(c->erasecount_size);
+				if (!jeb->first_node)
+					jeb->first_node = erase_count_ref;
+				if (jeb->last_node)
+					jeb->last_node->next_phys = erase_count_ref;
+				jeb->last_node = erase_count_ref;
+				USED_SPACE(PAD(c->erasecount_size));
+				ofs += PAD(c->erasecount_size);
+			}
+			break;
+#endif
+
 		case JFFS2_NODETYPE_PADDING:
 			DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
 			ofs += PAD(je32_to_cpu(node->totlen));
diff -auNrp ./mtd/fs/jffs2/wbuf.c ./mtd_new/fs/jffs2/wbuf.c
--- ./mtd/fs/jffs2/wbuf.c	2005-08-07 06:00:16.000000000 +0800
+++ ./mtd_new/fs/jffs2/wbuf.c	2005-08-24 18:26:50.000000000 +0800
@@ -976,8 +976,28 @@ int jffs2_check_oob_empty( struct jffs2_
 		}
 	}
 
-	/* we know, we are aligned :) */	
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	if (jeb->has_erase_count_node) {
+		for (i = 0; i < oob_size ; i++) {
+			if (i >= c->fsdata_pos && i < c->fsdata_pos + c->fsdata_len)
+				continue;
+
+			if (buf[oob_size + i] != 0xFF) {
+				D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n",
+					buf[oob_size + i], oob_size + i, jeb->offset));
+				ret = 1;
+				goto out;
+			}
+		}
+	}
+#endif
+
+	/* we know, we are aligned :) */
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	for (page = (oob_size * 2); page < len; page += sizeof(long)) {
+#else	
 	for (page = oob_size; page < len; page += sizeof(long)) {
+#endif
 		unsigned long dat = *(unsigned long *)(&buf[page]);
 		if(dat != -1) {
 			ret = 1; 
@@ -1005,7 +1025,9 @@ int jffs2_check_nand_cleanmarker (struct
 	int ret, i, cnt, retval = 0;
 	size_t retlen, offset;
 	int oob_size;
-
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	struct jffs2_erase_count_node_oob *e;
+#endif
 	offset = jeb->offset;
 	oob_size = c->mtd->oobsize;
 
@@ -1052,6 +1074,15 @@ int jffs2_check_nand_cleanmarker (struct
 				}
 				printk("\n");
 			})
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+			e = (struct jffs2_erase_count_node_oob *) &buf[oob_size + c->fsdata_pos];
+			if (je16_to_cpu (e->magic) == JFFS2_MAGIC_BITMASK &&
+			    je16_to_cpu (e->nodetype) == JFFS2_NODETYPE_ERASE_COUNT) {
+				jeb->has_erase_count_node = 1;
+				jeb->erase_count = je32_to_cpu (e->erase_count);
+				c->total_erase_count += jeb->erase_count;
+			}
+#endif
 		}
 		offset += c->mtd->erasesize;
 	}
@@ -1061,6 +1092,9 @@ int jffs2_check_nand_cleanmarker (struct
 int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
 	struct 	jffs2_unknown_node n;
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	struct jffs2_erase_count_node_oob ec;
+#endif
 	int 	ret;
 	size_t 	retlen;
 
@@ -1078,6 +1112,23 @@ int jffs2_write_nand_cleanmarker(struct 
 		D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len));
 		return ret;
 	}
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	ec.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	ec.nodetype = cpu_to_je16(JFFS2_NODETYPE_ERASE_COUNT);
+	ec.erase_count = cpu_to_je32(jeb->erase_count);
+
+	ret = jffs2_flash_write_oob(c, jeb->offset + c->mtd->oobblock + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&ec);
+
+	if (ret) {
+		D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
+		return ret;
+	}
+	if (retlen != c->fsdata_len) {
+		D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len));
+		return ret;
+	}
+#endif
 	return 0;
 }
 
@@ -1130,6 +1181,10 @@ static int jffs2_nand_set_oobinfo(struct
 	/* Cleanmarker is out-of-band, so inline size zero */
 	c->cleanmarker_size = 0;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	c->erasecount_size = 0;
+#endif
+
 	/* Should we use autoplacement ? */
 	if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) {
 		D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n"));
@@ -1218,6 +1273,9 @@ int jffs2_nor_ecc_flash_setup(struct jff
 	/* Cleanmarker is actually larger on the flashes */
 	c->cleanmarker_size = 16;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	c->erasecount_size = 24;
+#endif
 	/* Initialize write buffer */
 	init_rwsem(&c->wbuf_sem);
 	c->wbuf_pagesize = c->mtd->eccsize;
diff -auNrp ./mtd/fs/Kconfig ./mtd_new/fs/Kconfig
--- ./mtd/fs/Kconfig	2005-05-10 06:00:10.000000000 +0800
+++ ./mtd_new/fs/Kconfig	2005-08-25 17:33:55.000000000 +0800
@@ -64,6 +64,18 @@ config JFFS2_FS_WRITEBUFFER
 	    - NOR flash with transparent ECC
 	    - DataFlash
 
+config JFFS2_FS_ERASE_COUNT_TRACKING
+        bool "JFFS2 erase count tracking support"
+        depends on JFFS2_FS
+        default y
+        help
+          This enables the erase count tracking support in JFFS2.
+
+          This functionality provides the support to track the erase count of
+          each erase block.
+
+          The "#ifdef" for this feature should go by 2.6.20.
+
 config JFFS2_COMPRESSION_OPTIONS
 	bool "Advanced compression options for JFFS2"
 	default n
diff -auNrp ./mtd/include/linux/jffs2_fs_sb.h ./mtd_new/include/linux/jffs2_fs_sb.h
--- ./mtd/include/linux/jffs2_fs_sb.h	2005-05-20 06:00:14.000000000 +0800
+++ ./mtd_new/include/linux/jffs2_fs_sb.h	2005-08-18 13:28:36.000000000 +0800
@@ -40,7 +40,9 @@ struct jffs2_sb_info {
 					   out-of-order writing of nodes. And GC. */
 	uint32_t cleanmarker_size;	/* Size of an _inline_ CLEANMARKER
 					 (i.e. zero for OOB CLEANMARKER */
-
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	uint32_t erasecount_size;	/* size of an _inline_ erase-count node */
+#endif
 	uint32_t flash_size;
 	uint32_t used_size;
 	uint32_t dirty_size;
@@ -89,6 +91,10 @@ struct jffs2_sb_info {
 	wait_queue_head_t inocache_wq;
 	struct jffs2_inode_cache **inocache_list;
 	spinlock_t inocache_lock;
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	uint32_t total_erase_count;	/* The summary erase count of all erase blocks on a Flash  */
+#endif
 	
 	/* Sem to allow jffs2_garbage_collect_deletion_dirent to
 	   drop the erase_completion_lock while it's holding a pointer 
diff -auNrp ./mtd/include/linux/jffs2.h ./mtd_new/include/linux/jffs2.h
--- ./mtd/include/linux/jffs2.h	2005-07-27 06:00:09.000000000 +0800
+++ ./mtd_new/include/linux/jffs2.h	2005-08-24 18:25:46.000000000 +0800
@@ -60,6 +60,8 @@
 #define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
 #define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
 
+#define JFFS2_NODETYPE_ERASE_COUNT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 5)
+
 // Maybe later...
 //#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
 //#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
@@ -146,10 +148,28 @@ struct jffs2_raw_inode
 	uint8_t data[0];
 } __attribute__((packed));
 
+struct jffs2_erase_count_node
+{
+	jint16_t magic;      /* A constant magic number.  */
+	jint16_t nodetype;   /* == JFFS2_NODETYPE_ERASE_COUNT */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t erase_count; /* used to track the erase counts of each erase block */
+	jint32_t node_crc;
+} __attribute__((packed));
+
+struct jffs2_erase_count_node_oob
+{
+	jint16_t magic;      /* A constant magic number.  */
+	jint16_t nodetype;   /* == JFFS2_NODETYPE_ERASE_COUNT */
+	jint32_t erase_count; /* used to track the erase counts of each erase block */
+} __attribute__((packed));
+
 union jffs2_node_union {
 	struct jffs2_raw_inode i;
 	struct jffs2_raw_dirent d;
 	struct jffs2_unknown_node u;
+	struct jffs2_erase_count_node e;
 };
 
 #endif /* __LINUX_JFFS2_H__ */

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH]Add erase count tracking support in JFFS2
@ 2005-09-06  8:53 zhao forrest
  0 siblings, 0 replies; 9+ messages in thread
From: zhao forrest @ 2005-09-06  8:53 UTC (permalink / raw)
  To: joern; +Cc: tglx, linux-mtd

[-- Attachment #1: Type: text/plain, Size: 294 bytes --]

Hi,

When writing the patch for wear-leveling, I found some points
needed by wear-leveling are missing in erase count tracking
patch. So I added these missing points and refined the patch
according to the previous comments. Here I resend it to mail
list for further comments.

Thanks,
Forrest


[-- Attachment #2: erase_count_r4.patch --]
[-- Type: application/octet-stream, Size: 20558 bytes --]

diff -auNrp ./mtd/fs/jffs2/build.c ./mtd_new/fs/jffs2/build.c
--- ./mtd/fs/jffs2/build.c	2005-07-31 06:00:11.000000000 +0800
+++ ./mtd_new/fs/jffs2/build.c	2005-08-18 13:33:55.000000000 +0800
@@ -336,6 +336,10 @@ int jffs2_do_mount_fs(struct jffs2_sb_in
 		c->blocks[i].first_node = NULL;
 		c->blocks[i].last_node = NULL;
 		c->blocks[i].bad_count = 0;
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		c->blocks[i].has_erase_count_node = 0;
+		c->blocks[i].erase_count = 0;
+#endif
 	}
 
 	INIT_LIST_HEAD(&c->clean_list);
diff -auNrp ./mtd/fs/jffs2/erase.c ./mtd_new/fs/jffs2/erase.c
--- ./mtd/fs/jffs2/erase.c	2005-09-06 16:15:56.000000000 +0800
+++ ./mtd_new/fs/jffs2/erase.c	2005-09-05 13:31:38.000000000 +0800
@@ -210,6 +210,24 @@ static void jffs2_erase_callback(struct 
 	} else {
 		jffs2_erase_succeeded(priv->c, priv->jeb);
 	}
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+        if (!priv->jeb->has_erase_count_node) {
+		if (priv->c->nr_blocks_with_erase_count != 0) {
+			priv->jeb->erase_count = priv->c->total_erase_count/priv->c->nr_blocks_with_erase_count;
+		} else {
+			priv->jeb->erase_count = 0;
+		}
+                priv->jeb->has_erase_count_node = 1;
+		priv->c->nr_blocks_with_erase_count++;
+		priv->c->total_erase_count += priv->jeb->erase_count;
+        }
+	priv->jeb->erase_count++;
+	if (priv->jeb->erase_count > priv->c->max_erase_count) {
+		priv->c->max_erase_count = priv->jeb->erase_count;
+	}
+	priv->c->total_erase_count++;
+#endif	
 	kfree(instr);
 }
 #endif /* !__ECOS */
@@ -349,6 +367,184 @@ fail:
 	return ret;
 }
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+static int write_erase_count_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+	struct jffs2_raw_node_ref *erase_count_ref = NULL;
+	struct kvec vecs[1];
+	int ret;
+	size_t retlen;
+	struct jffs2_erase_count_node erase_count_node = {
+		.magic =        cpu_to_je16(JFFS2_MAGIC_BITMASK),
+		.nodetype =     cpu_to_je16(JFFS2_NODETYPE_ERASE_COUNT),
+		.totlen =       cpu_to_je32(c->erasecount_size),
+		.erase_count =  cpu_to_je32(jeb->erase_count)
+	};
+
+	erase_count_ref = jffs2_alloc_raw_node_ref();
+	if (!erase_count_ref) {
+		printk(KERN_WARNING "Failed to allocate raw node ref for erase count node.\n");
+		return -ENOMEM;
+	}
+	erase_count_node.hdr_crc = cpu_to_je32(crc32(0, &erase_count_node,
+					sizeof(struct jffs2_unknown_node)-4));
+ 	erase_count_node.node_crc = cpu_to_je32(crc32(0, &erase_count_node,
+					sizeof(struct jffs2_erase_count_node)-4));
+	vecs[0].iov_base = (unsigned char *) &erase_count_node;
+	vecs[0].iov_len = sizeof(erase_count_node);
+	ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset + PAD(c->cleanmarker_size), &retlen);
+
+	if (ret || retlen != sizeof(erase_count_node)) {
+		if (ret)
+			printk(KERN_WARNING "Write erase count to block at 0x%08x failed: %d\n",
+                                                jeb->offset, ret);
+		else
+			printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
+                                                jeb->offset, sizeof(erase_count_node), retlen);
+		jffs2_free_raw_node_ref(erase_count_ref);
+		return ret;
+	}
+
+	erase_count_ref->next_in_ino = NULL;
+	erase_count_ref->next_phys = NULL;
+	erase_count_ref->flash_offset = (jeb->offset + PAD(c->cleanmarker_size)) | REF_NORMAL;
+	erase_count_ref->__totlen = PAD(c->erasecount_size);
+
+	if (!jeb->first_node)
+		jeb->first_node = erase_count_ref;
+	if (jeb->last_node)
+		jeb->last_node->next_phys = erase_count_ref;
+	jeb->last_node = erase_count_ref;
+
+	return 0;
+}
+
+static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+	struct jffs2_raw_node_ref *marker_ref = NULL;
+	size_t retlen;
+	int ret;
+	uint32_t bad_offset;
+
+	switch (jffs2_block_check_erase(c, jeb, &bad_offset)) {
+	case -EAGAIN:   goto refile;
+	case -EIO:      goto filebad;
+	}
+
+	/* Write the erase complete marker */
+	D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
+	bad_offset = jeb->offset;
+
+	/* Cleanmarker in oob area or no cleanmarker at all ? */
+	if (jffs2_cleanmarker_oob(c) || c->cleanmarker_size == 0) {
+		if (jffs2_cleanmarker_oob(c)) {
+			if (jffs2_write_nand_cleanmarker(c, jeb))
+				goto filebad;
+
+			jeb->first_node = jeb->last_node = NULL;
+			jeb->free_size = c->sector_size;
+			jeb->used_size = 0;
+			jeb->dirty_size = 0;
+			jeb->wasted_size = 0;
+		} else {
+			ret = write_erase_count_node(c, jeb);
+
+			if (ret == -ENOMEM)
+				goto refile;
+			else if (ret)
+				goto filebad;
+
+			jeb->free_size = c->sector_size - PAD(c->erasecount_size);
+			jeb->used_size = PAD(c->erasecount_size);
+			jeb->dirty_size = 0;
+			jeb->wasted_size = 0;                	
+		}
+	} else {
+
+		struct kvec vecs[1];
+		struct jffs2_unknown_node marker = {
+			.magic =        cpu_to_je16(JFFS2_MAGIC_BITMASK),
+			.nodetype =     cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
+			.totlen =       cpu_to_je32(c->cleanmarker_size)
+		};
+
+		marker_ref = jffs2_alloc_raw_node_ref();
+		if (!marker_ref) {
+			printk(KERN_WARNING "Failed to allocate raw node ref for clean marker. Refiling\n");
+			goto refile;
+		}
+
+		marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
+
+		vecs[0].iov_base = (unsigned char *) &marker;
+		vecs[0].iov_len = sizeof(marker);
+		ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen);
+
+		if (ret || retlen != sizeof(marker)) {
+			printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
+					jeb->offset, ret);
+
+			jffs2_free_raw_node_ref(marker_ref);
+			goto filebad;
+		}
+
+		marker_ref->next_in_ino = NULL;
+		marker_ref->next_phys = NULL;
+		marker_ref->flash_offset = jeb->offset | REF_NORMAL;
+		marker_ref->__totlen = c->cleanmarker_size;
+
+		jeb->first_node = jeb->last_node = marker_ref;
+
+		ret = write_erase_count_node(c, jeb);
+
+		if (ret) {
+			jffs2_free_raw_node_ref(marker_ref);
+			if (ret == -ENOMEM)
+				goto refile;
+			else if (ret)
+				goto filebad;
+		}
+
+		jeb->free_size = c->sector_size - c->cleanmarker_size - PAD(c->erasecount_size);
+		jeb->used_size = c->cleanmarker_size + PAD(c->erasecount_size);
+		jeb->dirty_size = 0;
+		jeb->wasted_size = 0;
+	}
+
+	spin_lock(&c->erase_completion_lock);
+	c->erasing_size -= c->sector_size;
+	c->free_size += jeb->free_size;
+	c->used_size += jeb->used_size;
+
+	jffs2_dbg_acct_sanity_check_nolock(c,jeb);
+	jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
+
+	list_add_tail(&jeb->list, &c->free_list);
+	c->nr_erasing_blocks--;
+	c->nr_free_blocks++;
+	spin_unlock(&c->erase_completion_lock);
+	wake_up(&c->erase_wait);
+	return;
+
+filebad:
+	spin_lock(&c->erase_completion_lock);
+	/* Stick it on a list (any list) so erase_failed can take it
+	   right off again.  Silly, but shouldn't happen often. */
+	list_add(&jeb->list, &c->erasing_list);
+	spin_unlock(&c->erase_completion_lock);
+	jffs2_erase_failed(c, jeb, bad_offset);
+	return;
+
+refile:
+	/* Stick it back on the list from whence it came and come back later */
+	jffs2_erase_pending_trigger(c);
+	spin_lock(&c->erase_completion_lock);
+	list_add(&jeb->list, &c->erase_complete_list);
+	spin_unlock(&c->erase_completion_lock);
+	return;
+}
+#else
+
 static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
 	struct jffs2_raw_node_ref *marker_ref = NULL;
@@ -457,3 +653,4 @@ refile:
 	spin_unlock(&c->erase_completion_lock);
 	return;
 }
+#endif
diff -auNrp ./mtd/fs/jffs2/fs.c ./mtd_new/fs/jffs2/fs.c
--- ./mtd/fs/jffs2/fs.c	2005-08-07 06:00:16.000000000 +0800
+++ ./mtd_new/fs/jffs2/fs.c	2005-08-18 13:32:56.000000000 +0800
@@ -490,6 +490,10 @@ int jffs2_do_fill_super(struct super_blo
 	}
 
 	c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	c->erasecount_size = sizeof(struct jffs2_erase_count_node);
+#endif
 	/* Joern -- stick alignment for weird 8-byte-page flash here */
 
 	/* NAND (or other bizarre) flash... do setup accordingly */
diff -auNrp ./mtd/fs/jffs2/nodelist.h ./mtd_new/fs/jffs2/nodelist.h
--- ./mtd/fs/jffs2/nodelist.h	2005-08-18 06:00:12.000000000 +0800
+++ ./mtd_new/fs/jffs2/nodelist.h	2005-08-18 13:32:02.000000000 +0800
@@ -195,6 +195,11 @@ struct jffs2_eraseblock
 	struct jffs2_raw_node_ref *last_node;
 
 	struct jffs2_raw_node_ref *gc_node;	/* Next node to be garbage collected */
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	uint8_t has_erase_count_node;   /* Whether an erase_count_node is found for this erase block */
+	uint32_t erase_count;           /* Keep track of the erase count of this erase block */
+#endif
 };
 
 /* Calculate totlen from surrounding nodes or eraseblock */
diff -auNrp ./mtd/fs/jffs2/nodemgmt.c ./mtd_new/fs/jffs2/nodemgmt.c
--- ./mtd/fs/jffs2/nodemgmt.c	2005-07-21 06:00:12.000000000 +0800
+++ ./mtd_new/fs/jffs2/nodemgmt.c	2005-08-18 13:33:30.000000000 +0800
@@ -258,7 +258,12 @@ static int jffs2_do_reserve_space(struct
 		c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list);
 		c->nr_free_blocks--;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		if ((jeb->free_size != c->sector_size - c->cleanmarker_size) &&
+			(jeb->free_size != c->sector_size - c->cleanmarker_size - c->erasecount_size)){
+#else
 		if (jeb->free_size != c->sector_size - c->cleanmarker_size) {
+#endif
 			printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size);
 			goto restart;
 		}
diff -auNrp ./mtd/fs/jffs2/scan.c ./mtd_new/fs/jffs2/scan.c
--- ./mtd/fs/jffs2/scan.c	2005-07-21 06:00:12.000000000 +0800
+++ ./mtd_new/fs/jffs2/scan.c	2005-09-02 14:04:58.000000000 +0800
@@ -438,6 +438,14 @@ scan_more:	
 				return BLK_STATE_CLEANMARKER;
 			}
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+			if (jeb->has_erase_count_node && jeb->used_size == (PAD(c->cleanmarker_size)
+			    +PAD(c->erasecount_size)) && !jeb->dirty_size) {
+				D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size)));
+				return BLK_STATE_CLEANMARKER;
+			}
+#endif
+
 			/* See how much more there is to read in this eraseblock... */
 			buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
 			if (!buf_len) {
@@ -581,6 +589,65 @@ scan_more:	
 			}
 			break;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+		case JFFS2_NODETYPE_ERASE_COUNT:
+			if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
+				buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+				D1(printk(KERN_DEBUG "Fewer than %d bytes (erase count node) left to end of buf. Reading 0x%x at 0x%08x\n",
+					  je32_to_cpu(node->totlen), buf_len, ofs));
+				err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
+				if (err)
+					return err;
+				buf_ofs = ofs;
+				node = (void *)buf;
+			}
+			if (je32_to_cpu(node->totlen) != c->erasecount_size) {
+				printk(KERN_NOTICE "ERASE_COUNT node found at 0x%08x has totlen 0x%x != normal 0x%x\n",
+				      ofs, je32_to_cpu(node->totlen), sizeof(struct jffs2_erase_count_node));
+				DIRTY_SPACE(PAD(sizeof(struct jffs2_erase_count_node)));
+				ofs += PAD(sizeof(struct jffs2_erase_count_node));
+			} else if (ofs != jeb->offset + PAD(c->cleanmarker_size)) {
+				printk(KERN_NOTICE "ERASE_COUNT node found at 0x%08x, not closely follow the CLEANMARKER node in block (0x%08x)\n", ofs, jeb->offset);
+				DIRTY_SPACE(PAD(sizeof(struct jffs2_erase_count_node)));
+				ofs += PAD(sizeof(struct jffs2_erase_count_node));
+			} else {
+				uint32_t crc;
+				struct jffs2_erase_count_node *erase_count_node = (struct jffs2_erase_count_node *)node;
+				struct jffs2_raw_node_ref *erase_count_ref = jffs2_alloc_raw_node_ref();
+				if (!erase_count_ref) {
+					printk(KERN_NOTICE "Failed to allocate node ref for erase count\n");
+					return -ENOMEM;
+				}
+				crc = crc32(0, erase_count_node, sizeof(*erase_count_node) - 4);
+				if (crc != je32_to_cpu(erase_count_node->node_crc)) {
+					printk(KERN_NOTICE "ERASE_COUNT node found at 0x%08x, but CRC failed: Read 0x%08x, calculated 0x%08x\n", ofs, je32_to_cpu(erase_count_node->node_crc), crc);
+					DIRTY_SPACE(PAD(sizeof(struct jffs2_erase_count_node)));
+					ofs += PAD(sizeof(struct jffs2_erase_count_node));
+					jffs2_free_raw_node_ref(erase_count_ref);
+					break;
+				}
+				jeb->has_erase_count_node = 1;
+				jeb->erase_count = je32_to_cpu(erase_count_node->erase_count);
+				c->total_erase_count += jeb->erase_count;
+				c->nr_blocks_with_erase_count++;
+				if (jeb->erase_count > c->max_erase_count) {
+					c->max_erase_count = jeb->erase_count;
+				}
+				erase_count_ref->next_in_ino = NULL;
+				erase_count_ref->next_phys = NULL;
+				erase_count_ref->flash_offset = ofs | REF_NORMAL;
+				erase_count_ref->__totlen = PAD(c->erasecount_size);
+				if (!jeb->first_node)
+					jeb->first_node = erase_count_ref;
+				if (jeb->last_node)
+					jeb->last_node->next_phys = erase_count_ref;
+				jeb->last_node = erase_count_ref;
+				USED_SPACE(PAD(c->erasecount_size));
+				ofs += PAD(c->erasecount_size);
+			}
+			break;
+#endif
+
 		case JFFS2_NODETYPE_PADDING:
 			DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
 			ofs += PAD(je32_to_cpu(node->totlen));
diff -auNrp ./mtd/fs/jffs2/wbuf.c ./mtd_new/fs/jffs2/wbuf.c
--- ./mtd/fs/jffs2/wbuf.c	2005-08-07 06:00:16.000000000 +0800
+++ ./mtd_new/fs/jffs2/wbuf.c	2005-09-06 16:22:03.000000000 +0800
@@ -976,8 +976,28 @@ int jffs2_check_oob_empty( struct jffs2_
 		}
 	}
 
-	/* we know, we are aligned :) */	
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	if (jeb->has_erase_count_node) {
+		for (i = 0; i < oob_size ; i++) {
+			if (i >= c->fsdata_pos && i < c->fsdata_pos + c->fsdata_len)
+				continue;
+
+			if (buf[oob_size + i] != 0xFF) {
+				D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n",
+					buf[oob_size + i], oob_size + i, jeb->offset));
+				ret = 1;
+				goto out;
+			}
+		}
+	}
+#endif
+
+	/* we know, we are aligned :) */
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	for (page = (oob_size * 2); page < len; page += sizeof(long)) {
+#else	
 	for (page = oob_size; page < len; page += sizeof(long)) {
+#endif
 		unsigned long dat = *(unsigned long *)(&buf[page]);
 		if(dat != -1) {
 			ret = 1; 
@@ -1006,6 +1026,9 @@ int jffs2_check_nand_cleanmarker (struct
 	size_t retlen, offset;
 	int oob_size;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	struct jffs2_erase_count_node_oob *e;
+#endif
 	offset = jeb->offset;
 	oob_size = c->mtd->oobsize;
 
@@ -1052,6 +1075,15 @@ int jffs2_check_nand_cleanmarker (struct
 				}
 				printk("\n");
 			})
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+			e = (struct jffs2_erase_count_node_oob *) &buf[oob_size + c->fsdata_pos];
+			if (je16_to_cpu (e->magic) == JFFS2_MAGIC_BITMASK &&
+			    je16_to_cpu (e->nodetype) == JFFS2_NODETYPE_ERASE_COUNT) {
+				jeb->has_erase_count_node = 1;
+				jeb->erase_count = je32_to_cpu (e->erase_count);
+				c->total_erase_count += jeb->erase_count;
+			}
+#endif
 		}
 		offset += c->mtd->erasesize;
 	}
@@ -1061,6 +1093,9 @@ int jffs2_check_nand_cleanmarker (struct
 int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
 	struct 	jffs2_unknown_node n;
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	struct jffs2_erase_count_node_oob ec;
+#endif
 	int 	ret;
 	size_t 	retlen;
 
@@ -1078,6 +1113,23 @@ int jffs2_write_nand_cleanmarker(struct 
 		D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len));
 		return ret;
 	}
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	ec.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	ec.nodetype = cpu_to_je16(JFFS2_NODETYPE_ERASE_COUNT);
+	ec.erase_count = cpu_to_je32(jeb->erase_count);
+
+	ret = jffs2_flash_write_oob(c, jeb->offset + c->mtd->oobblock + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&ec);
+
+	if (ret) {
+		D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
+		return ret;
+	}
+	if (retlen != c->fsdata_len) {
+		D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len));
+		return ret;
+	}
+#endif
 	return 0;
 }
 
@@ -1130,6 +1182,10 @@ static int jffs2_nand_set_oobinfo(struct
 	/* Cleanmarker is out-of-band, so inline size zero */
 	c->cleanmarker_size = 0;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	c->erasecount_size = 0;
+#endif
+
 	/* Should we use autoplacement ? */
 	if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) {
 		D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n"));
@@ -1218,6 +1274,9 @@ int jffs2_nor_ecc_flash_setup(struct jff
 	/* Cleanmarker is actually larger on the flashes */
 	c->cleanmarker_size = 16;
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	c->erasecount_size = 24;
+#endif
 	/* Initialize write buffer */
 	init_rwsem(&c->wbuf_sem);
 	c->wbuf_pagesize = c->mtd->eccsize;
diff -auNrp ./mtd/fs/Kconfig ./mtd_new/fs/Kconfig
--- ./mtd/fs/Kconfig	2005-05-10 06:00:10.000000000 +0800
+++ ./mtd_new/fs/Kconfig	2005-08-25 17:33:55.000000000 +0800
@@ -64,6 +64,18 @@ config JFFS2_FS_WRITEBUFFER
 	    - NOR flash with transparent ECC
 	    - DataFlash
 
+config JFFS2_FS_ERASE_COUNT_TRACKING
+        bool "JFFS2 erase count tracking support"
+        depends on JFFS2_FS
+        default y
+        help
+          This enables the erase count tracking support in JFFS2.
+
+          This functionality provides the support to track the erase count of
+          each erase block.
+
+          The "#ifdef" for this feature should go by 2.6.20.
+
 config JFFS2_COMPRESSION_OPTIONS
 	bool "Advanced compression options for JFFS2"
 	default n
diff -auNrp ./mtd/include/linux/jffs2_fs_sb.h ./mtd_new/include/linux/jffs2_fs_sb.h
--- ./mtd/include/linux/jffs2_fs_sb.h	2005-05-20 06:00:14.000000000 +0800
+++ ./mtd_new/include/linux/jffs2_fs_sb.h	2005-09-06 16:24:16.000000000 +0800
@@ -41,6 +41,9 @@ struct jffs2_sb_info {
 	uint32_t cleanmarker_size;	/* Size of an _inline_ CLEANMARKER
 					 (i.e. zero for OOB CLEANMARKER */
 
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	uint32_t erasecount_size;	/* size of an _inline_ erase-count node */
+#endif
 	uint32_t flash_size;
 	uint32_t used_size;
 	uint32_t dirty_size;
@@ -89,6 +92,12 @@ struct jffs2_sb_info {
 	wait_queue_head_t inocache_wq;
 	struct jffs2_inode_cache **inocache_list;
 	spinlock_t inocache_lock;
+
+#ifdef CONFIG_JFFS2_FS_ERASE_COUNT_TRACKING
+	uint32_t total_erase_count;	/* The summary erase count of all erase blocks on a Flash */
+	uint32_t nr_blocks_with_erase_count;    /* The number of erase blocks, which has erase_count_node on it */
+	uint32_t max_erase_count;	/* The maximum of all erase count on a Flash */
+#endif
 	
 	/* Sem to allow jffs2_garbage_collect_deletion_dirent to
 	   drop the erase_completion_lock while it's holding a pointer 
diff -auNrp ./mtd/include/linux/jffs2.h ./mtd_new/include/linux/jffs2.h
--- ./mtd/include/linux/jffs2.h	2005-07-27 06:00:09.000000000 +0800
+++ ./mtd_new/include/linux/jffs2.h	2005-08-24 18:25:46.000000000 +0800
@@ -60,6 +60,8 @@
 #define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
 #define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
 
+#define JFFS2_NODETYPE_ERASE_COUNT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 5)
+
 // Maybe later...
 //#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
 //#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
@@ -146,10 +148,28 @@ struct jffs2_raw_inode
 	uint8_t data[0];
 } __attribute__((packed));
 
+struct jffs2_erase_count_node
+{
+	jint16_t magic;      /* A constant magic number.  */
+	jint16_t nodetype;   /* == JFFS2_NODETYPE_ERASE_COUNT */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t erase_count; /* used to track the erase counts of each erase block */
+	jint32_t node_crc;
+} __attribute__((packed));
+
+struct jffs2_erase_count_node_oob
+{
+	jint16_t magic;      /* A constant magic number.  */
+	jint16_t nodetype;   /* == JFFS2_NODETYPE_ERASE_COUNT */
+	jint32_t erase_count; /* used to track the erase counts of each erase block */
+} __attribute__((packed));
+
 union jffs2_node_union {
 	struct jffs2_raw_inode i;
 	struct jffs2_raw_dirent d;
 	struct jffs2_unknown_node u;
+	struct jffs2_erase_count_node e;
 };
 
 #endif /* __LINUX_JFFS2_H__ */

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2005-09-06  9:13 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-08-25  7:44 [PATCH]Add erase count tracking support in JFFS2 zhao forrest
2005-08-25  8:34 ` Jörn Engel
  -- strict thread matches above, loose matches on Subject: below --
2005-09-06  8:53 zhao forrest
2005-08-25  9:59 zhao forrest
2005-08-24 10:43 zhao forrest
2005-08-24 12:05 ` Jörn Engel
2005-08-24 12:45   ` zhao forrest
2005-08-18  9:08 zhao forrest
2005-08-18 11:34 ` Jörn Engel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox