[patch 3] jffs2 for ecc nor

Jörn Engel joern at wohnheim.fh-wedel.de
Thu Jul 11 11:48:28 EDT 2002


Two things are needed:
- erase marker has to be n times 8 byte, which makes it 16.
- wbuf.c has to be used, with a tiny buffer of 8 byte.

The former is done in an ugly way. David already prepared the
infrastructure for a nicer one, but that is untested.

The latter pulls all necessary functionality from wbuf.c into
nor_wbuf.c and touches a couple of other spots. This is currently a
config option, but it would make some sense to make this generic, as
already two types of flashes use it.

Joern

diff -Naur jffs2/fs/jffs2/Makefile jffs2.new/fs/jffs2/Makefile
--- jffs2/fs/jffs2/Makefile	Wed Jun 19 18:56:23 2002
+++ jffs2.new/fs/jffs2/Makefile	Thu Jul 11 16:06:24 2002
@@ -19,6 +19,7 @@
 LINUX_OBJS-25	:= super.o
 
 NAND_OBJS-$(CONFIG_JFFS2_FS_NAND)	:= wbuf.o
+NAND_OBJS-$(CONFIG_JFFS2_FS_ECC_NOR)	:= nor_wbuf.o
 
 O_TARGET := jffs2.o
 
diff -Naur jffs2/fs/jffs2/erase.c jffs2.new/fs/jffs2/erase.c
--- jffs2/fs/jffs2/erase.c	Thu Jul 11 16:16:15 2002
+++ jffs2.new/fs/jffs2/erase.c	Thu Jul 11 16:11:08 2002
@@ -262,12 +262,21 @@
 
 void jffs2_mark_erased_blocks(struct jffs2_sb_info *c)
 {
+	static struct jffs2_cleanmarker marker = {
+		magic:		JFFS2_MAGIC_BITMASK,
+		nodetype:	JFFS2_NODETYPE_CLEANMARKER,
+		padding:	0x0add0add,
+		totlen:		sizeof(struct jffs2_cleanmarker)
+	};
 	struct jffs2_eraseblock *jeb;
 	struct jffs2_raw_node_ref *marker_ref = NULL;
 	unsigned char *ebuf;
 	size_t retlen;
 	int ret;
 
+	if (unlikely(!marker.hdr_crc))
+		marker.hdr_crc = crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4);
+
 	spin_lock_bh(&c->erase_completion_lock);
 	while (!list_empty(&c->erase_complete_list)) {
 		jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
@@ -345,14 +354,6 @@
 			jeb->used_size = 0;
 			jeb->dirty_size = 0;
 		} else {
-			struct jffs2_unknown_node marker = {
-				magic:		JFFS2_MAGIC_BITMASK,
-				nodetype:	JFFS2_NODETYPE_CLEANMARKER,
-				totlen:		c->cleanmarker_size
-			};
-
-			marker.hdr_crc = crc32(0, &marker, sizeof(marker) - 4);
-
 			ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
 			if (ret) {
 				printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
@@ -368,7 +369,7 @@
 			marker_ref->next_in_ino = NULL;
 			marker_ref->next_phys = NULL;
 			marker_ref->flash_offset = jeb->offset;
-			marker_ref->totlen = PAD(marker.totlen);
+			marker_ref->totlen = PAD(sizeof(marker));
 			
 			jeb->first_node = jeb->last_node = marker_ref;
 			
diff -Naur jffs2/fs/jffs2/fs.c jffs2.new/fs/jffs2/fs.c
--- jffs2/fs/jffs2/fs.c	Wed Jul  3 15:23:51 2002
+++ jffs2.new/fs/jffs2/fs.c	Thu Jul 11 16:11:30 2002
@@ -266,7 +266,6 @@
 {
 	struct jffs2_sb_info *c;
 	struct inode *root_i;
-	int ret;
 
 	c = JFFS2_SB_INFO(sb);
 
@@ -285,26 +284,18 @@
 		       c->flash_size / c->sector_size);
 		return -EINVAL;
 	}
-
-	c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
-
-	if (c->mtd->eccsize) {
-		c->cleanmarker_size = (c->cleanmarker_size-1) / c->mtd->eccsize
-			* c->mtd->eccsize + c->mtd->eccsize;
-	}
-
-	if (jffs2_cleanmarker_oob(c)) {
-		/* Cleanmarker is out-of-band, so inline size zero */
-		c->cleanmarker_size = 0;
-	}
-
+	
+	/*
+	 * TODO: Move the following code into a function, as it is
+	 * duplicated almost verbatim 
+	 */
 	if (c->mtd->type == MTD_NANDFLASH) {
 		/* Initialise write buffer */
 		c->wbuf_pagesize = c->mtd->oobblock;
 		c->wbuf_ofs = 0xFFFFFFFF;
 		c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
 		if (!c->wbuf)
-			return -ENOMEM;
+			goto out_mtd;
 
 		/* Initialize process for timed wbuf flush */
 		INIT_TQUEUE(&c->wbuf_task,(void*) jffs2_wbuf_process, (void *)c);
@@ -312,19 +303,24 @@
 		init_timer(&c->wbuf_timer);
 		c->wbuf_timer.function = jffs2_wbuf_timeout;
 		c->wbuf_timer.data = (unsigned long) c;
-	}
+	} else if ((c->mtd->type == MTD_NORFLASH) && (c->mtd->flags & MTD_ECC)) {
+		/* Initialise write buffer */
+		c->wbuf_pagesize = c->mtd->eccsize; /* FIXME: is this the correct field? */
+		c->wbuf_ofs = 0xFFFFFFFF;
+		c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
+		if (!c->wbuf)
+			goto out_mtd;
 
-	c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL);
-	if (!c->inocache_list) {
-		ret = -ENOMEM;
-		goto out_wbuf;
+		/* Initialize process for timed wbuf flush */
+		INIT_TQUEUE(&c->wbuf_task,(void*) jffs2_wbuf_process, (void *)c);
+		/* Initialize timer for timed wbuf flush */
+		init_timer(&c->wbuf_timer);
+		c->wbuf_timer.function = jffs2_wbuf_timeout;
+		c->wbuf_timer.data = (unsigned long) c;
 	}
-	memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *));
-
-	if ((ret = jffs2_do_mount_fs(c)))
-		goto out_inohash;
 
-	ret = -EINVAL;
+	if (jffs2_do_mount_fs(c))
+		goto out_mtd;
 
 	D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n"));
 	root_i = iget(sb, 1);
@@ -354,10 +350,6 @@
 	jffs2_free_ino_caches(c);
 	jffs2_free_raw_node_refs(c);
 	kfree(c->blocks);
- out_inohash:
-	kfree(c->inocache_list);
- out_wbuf:
-	if (c->wbuf)
-		kfree(c->wbuf);
-	return ret;
+ out_mtd:
+	return -EINVAL;
 }
diff -Naur jffs2/fs/jffs2/nor_wbuf.c jffs2.new/fs/jffs2/nor_wbuf.c
--- jffs2/fs/jffs2/nor_wbuf.c	Thu Jan  1 01:00:00 1970
+++ jffs2.new/fs/jffs2/nor_wbuf.c	Thu Jul 11 16:15:10 2002
@@ -0,0 +1,477 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001, 2002 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2 at cambridge.redhat.com>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/interrupt.h>
+#include "crc32.h"
+#include <linux/mtd/nand.h>
+#include "nodelist.h"
+
+#if 0 /* toggle this for debugging */
+#define CHECKPOINT(n) printk(__FUNCTION__ ": %d\n", n)
+#else
+#define CHECKPOINT(n)
+#endif
+
+/* max. erase failures before we mark a block bad */
+#define MAX_ERASE_FAILURES 	5
+
+/* two seconds timeout for timed wbuf-flushing */
+#define WBUF_FLUSH_TIMEOUT	HZ / 4
+
+static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
+{
+	struct list_head *this, *next;
+	static int n;
+
+	CHECKPOINT(0);
+	if (list_empty(&c->erasable_pending_wbuf_list))
+		return;
+
+	list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) {
+		struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+
+		D1(printk(KERN_DEBUG "Removing eraseblock at 0x%08x from erasable_pending_wbuf_list...\n", jeb->offset));
+		list_del(this);
+		if ((jiffies + (n++)) & 127) {
+			/* Most of the time, we just erase it immediately. Otherwise we
+			   spend ages scanning it on mount, etc. */
+			D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n"));
+			list_add_tail(&jeb->list, &c->erase_pending_list);
+			c->nr_erasing_blocks++;
+			jffs2_erase_pending_trigger(c);
+		} else {
+			/* Sometimes, however, we leave it elsewhere so it doesn't get
+			   immediately reused, and we spread the load a bit. */
+			D1(printk(KERN_DEBUG "...and adding to erasable_list\n"));
+			list_add_tail(&jeb->list, &c->erasable_list);
+		}
+	}
+}
+
+/* 
+*	Timed flushing of wbuf. If we have no consecutive write to wbuf, within	
+*	the specified time, we flush the contents with padding !
+*/
+void jffs2_wbuf_timeout (unsigned long data)
+{
+	struct jffs2_sb_info *c = (struct jffs2_sb_info *) data;
+	CHECKPOINT(0);
+	/* 
+	* Wake up the flush process, we need process context to have the right 
+	* to sleep on flash write
+	*/
+	D1(printk(KERN_DEBUG "jffs2_wbuf_timeout(): timer expired\n"));
+	schedule_task(&c->wbuf_task);
+}
+
+/*
+*	Process for timed wbuf flush
+*
+*	FIXME What happens, if we have a write failure there ????
+*/
+void jffs2_wbuf_process (void *data)
+{
+	struct jffs2_sb_info *c = (struct jffs2_sb_info *) data;	
+	
+	CHECKPOINT(0);
+	D1(printk(KERN_DEBUG "jffs2_wbuf_process() entered\n"));
+
+	if (!down_trylock(&c->alloc_sem)) {
+		D1(printk (KERN_DEBUG "jffs2_wbuf_process() alloc_sem got\n"));
+	
+		if(!c->nextblock || (c->nextblock->free_size < (c->wbuf_pagesize - c->wbuf_len)))
+			jffs2_flush_wbuf(c, 1); /* pad only */
+		else			
+			jffs2_flush_wbuf(c, 2); /* pad and adjust nextblock */
+		up(&c->alloc_sem);
+	} else {
+		D1(printk (KERN_DEBUG "jffs2_wbuf_process() alloc_sem already occupied\n"));
+	}	
+}
+
+
+/* Meaning of pad argument:
+   0: Do not pad. Probably pointless - we only ever use this when we can't pad anyway.
+   1: Pad, do not adjust nextblock free_size
+   2: Pad, adjust nextblock free_size
+*/
+int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
+{
+	int ret;
+	size_t retlen;
+	
+	CHECKPOINT(0);
+	if (!down_trylock(&c->alloc_sem)) {
+		up(&c->alloc_sem);
+		printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n");
+		BUG();
+	}
+
+	/* delete a eventually started timed wbuf flush */
+	del_timer_sync(&c->wbuf_timer);
+
+	if(!c->wbuf || !c->wbuf_len)
+		return 0;
+
+	/* claim remaining space on the page
+	   this happens, if we have a change to a new block,
+	   or if fsync forces us to flush the writebuffer.
+	   if we have a switch to next page, we will not have
+	   enough remaining space for this. 
+	*/
+	if (pad) {
+		c->wbuf_len = PAD(c->wbuf_len);
+		
+		if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) {
+			struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len);
+			padnode->magic = JFFS2_MAGIC_BITMASK;
+			padnode->nodetype = JFFS2_NODETYPE_PADDING;
+			padnode->totlen = c->wbuf_pagesize - c->wbuf_len;
+			padnode->hdr_crc = crc32(0, padnode, sizeof(*padnode)-4);
+		}
+	}
+	/* else jffs2_flash_writev has actually filled in the rest of the
+	   buffer for us, and will deal with the node refs etc. later. */
+	
+	ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf);
+	
+	if (ret || retlen != c->wbuf_pagesize) {
+		if (ret)
+			printk(KERN_CRIT "jffs2_flush_wbuf(): Write failed with %d\n",ret);
+		else
+			printk(KERN_CRIT "jffs2_flush_wbuf(): Write was short %d instead of %d\n",retlen,c->wbuf_pagesize);
+			
+		ret = -EIO;		
+		/* CHECKME NAND 
+		   So that the caller knows what happened. If
+		   we were called from jffs2_flash_writev(), it'll
+		   know to return failure and _its_ caller will
+		   try again. writev gives back to jffs2_write_xxx 
+		   in write.c. There are the real fixme's
+		 */
+
+		/*  FIXME NAND
+		   If we were called from GC or fsync, there's no repair kit yet
+		*/
+		    
+		return ret; 
+	}
+
+	/* Adjusting free size of next block only, if it's called from fsync ! */
+	if (pad == 2) {
+		D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of c->nextblock\n"));
+		spin_lock_bh(&c->erase_completion_lock);
+		if (!c->nextblock)
+			BUG();
+		if (c->nextblock->free_size < (c->wbuf_pagesize - c->wbuf_len))
+			BUG();
+		c->nextblock->free_size -= (c->wbuf_pagesize - c->wbuf_len);
+		c->nextblock->dirty_size += (c->wbuf_pagesize - c->wbuf_len);
+		spin_unlock_bh(&c->erase_completion_lock);
+	}
+
+	/* Stick any now-obsoleted blocks on the erase_pending_list */
+	spin_lock_bh(&c->erase_completion_lock);
+	jffs2_refile_wbuf_blocks(c);
+	spin_unlock_bh(&c->erase_completion_lock);
+
+	memset(c->wbuf,0xff,c->wbuf_pagesize);
+	/* adjust write buffer offset, else we get a non contigous write bug */
+	c->wbuf_ofs+= c->wbuf_pagesize;
+	c->wbuf_len = 0;
+	return 0;
+}
+
+#define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) )
+#define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) )
+int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsigned long count, loff_t to, size_t *retlen)
+{
+	struct iovec outvecs[3];
+	uint32_t totlen = 0;
+	uint32_t split_ofs = 0;
+	uint32_t old_totlen;
+	int ret, splitvec = -1;
+	int invec, outvec;
+	size_t wbuf_retlen;
+	unsigned char *wbuf_ptr;
+	size_t donelen = 0;
+	uint32_t outvec_to = to;
+
+	CHECKPOINT(0);
+	/* If not NAND flash, don't bother */
+	if (!c->wbuf)
+		return jffs2_flash_direct_writev(c, invecs, count, to, retlen);
+	
+	CHECKPOINT(1);
+	/* If wbuf_ofs is not initialized, set it to target adress */
+	if (c->wbuf_ofs == 0xFFFFFFFF) {
+		c->wbuf_ofs = PAGE_DIV(to);
+		c->wbuf_len = PAGE_MOD(to);			
+		memset(c->wbuf,0xff,c->wbuf_pagesize);
+	}
+
+	CHECKPOINT(2);
+	/* Sanity checks on target address. 
+	   It's permitted to write at PAD(c->wbuf_len+c->wbuf_ofs), 
+	   and it's permitted to write at the beginning of a new 
+	   erase block. Anything else, and you die.
+	   New block starts at xxx000c (0-b = block header)
+	*/
+	if ( (to & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) {
+		/* It's a write to a new block */
+		if (c->wbuf_len) {
+			D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs));
+			ret = jffs2_flush_wbuf(c, 1);
+			if (ret) {
+				/* the underlying layer has to check wbuf_len to do the cleanup */
+				D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret));
+				*retlen = 0;
+				return ret;
+			}
+		}
+		/* set pointer to new block */
+		c->wbuf_ofs = PAGE_DIV(to);
+		c->wbuf_len = PAGE_MOD(to);			
+	} 
+
+	CHECKPOINT(3);
+	if (to != PAD(c->wbuf_ofs + c->wbuf_len)) {
+		/* We're not writing immediately after the writebuffer. Bad. */
+		printk(KERN_CRIT "jffs2_flash_writev(): Non-contiguous write to %08lx\n", (unsigned long)to);
+		if (c->wbuf_len)
+			printk(KERN_CRIT "wbuf was previously %08x-%08x\n",
+					  c->wbuf_ofs, c->wbuf_ofs+c->wbuf_len);
+		printk(KERN_CRIT "wbuf size is 0x%08x\n", c->wbuf_pagesize);
+		BUG();
+	}
+
+	CHECKPOINT(4);
+	/* Note outvecs[3] above. We know count is never greater than 2 */
+	if (count > 2) {
+		printk(KERN_CRIT "jffs2_flash_writev(): count is %ld\n", count);
+		BUG();
+	}
+
+	invec = 0;
+	outvec = 0;
+
+
+	CHECKPOINT(5);
+	/* Fill writebuffer first, if already in use */	
+	if (c->wbuf_len) {
+		uint32_t invec_ofs = 0;
+
+		/* adjust alignment offset */ 
+		if (c->wbuf_len != PAGE_MOD(to)) {
+			c->wbuf_len = PAGE_MOD(to);
+			/* take care of alignment to next page */
+			if (!c->wbuf_len)
+				c->wbuf_len = c->wbuf_pagesize;
+		}
+		
+		while(c->wbuf_len < c->wbuf_pagesize) {
+			uint32_t thislen;
+			
+			if (invec == count)
+				goto alldone;
+
+			thislen = c->wbuf_pagesize - c->wbuf_len;
+
+			if (thislen >= invecs[invec].iov_len)
+				thislen = invecs[invec].iov_len;
+	
+			invec_ofs = thislen;
+
+			memcpy(c->wbuf + c->wbuf_len, invecs[invec].iov_base, thislen);
+			c->wbuf_len += thislen;
+			donelen += thislen;
+			/* Get next invec, if actual did not fill the buffer */
+			if (c->wbuf_len < c->wbuf_pagesize) 
+				invec++;
+		}			
+		
+		/* write buffer is full, flush buffer */
+		ret = jffs2_flush_wbuf(c, 0);
+		if (ret) {
+			/* the underlying layer has to check wbuf_len to do the cleanup */
+			D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret));
+			*retlen = 0;
+			return ret;
+		}
+		outvec_to += donelen;
+		c->wbuf_ofs = outvec_to;
+		
+		/* All invecs done ? */
+		if (invec == count)
+			goto alldone;
+
+		/* Set up the first outvec, containing the remainder of the
+		   invec we partially used */
+		if (invecs[invec].iov_len > invec_ofs) {
+			outvecs[0].iov_base = invecs[invec].iov_base+invec_ofs;
+			totlen = outvecs[0].iov_len = invecs[invec].iov_len-invec_ofs;
+			if (totlen > c->wbuf_pagesize) {
+				splitvec = outvec;
+				split_ofs = outvecs[0].iov_len - PAGE_MOD(totlen);
+			}
+			outvec++;
+		}
+		invec++;
+	}
+
+	/* OK, now we've flushed the wbuf and the start of the bits
+	   we have been asked to write, now to write the rest.... */
+
+	CHECKPOINT(6);
+	/* totlen holds the amount of data still to be written */
+	old_totlen = totlen;
+	for ( ; invec < count; invec++,outvec++ ) {
+		outvecs[outvec].iov_base = invecs[invec].iov_base;
+		totlen += outvecs[outvec].iov_len = invecs[invec].iov_len;
+		if (PAGE_DIV(totlen) != PAGE_DIV(old_totlen)) {
+			splitvec = outvec;
+			split_ofs = outvecs[outvec].iov_len - PAGE_MOD(totlen);
+			old_totlen = totlen;
+		}
+	}
+
+	/* Now the outvecs array holds all the remaining data to write */
+	/* Up to splitvec,split_ofs is to be written immediately. The rest
+	   goes into the (now-empty) wbuf */
+
+	CHECKPOINT(7);
+	if (splitvec != -1) {
+		uint32_t remainder;
+		int ret;
+
+		remainder = outvecs[splitvec].iov_len - split_ofs;
+		outvecs[splitvec].iov_len = split_ofs;
+
+		/* We did cross a page boundary, so we write some now */
+		ret = jffs2_flash_direct_writev(c, outvecs, splitvec+1, outvec_to, &wbuf_retlen); 
+		if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) {
+			/* At this point we have no problem,
+			   c->wbuf is empty. 
+			*/
+			*retlen = donelen;
+			return ret;
+		}
+		
+		donelen += wbuf_retlen;
+		c->wbuf_ofs = PAGE_DIV(outvec_to) + PAGE_DIV(totlen);
+
+		if (remainder) {
+			outvecs[splitvec].iov_base += split_ofs;
+			outvecs[splitvec].iov_len = remainder;
+		} else {
+			splitvec++;
+		}
+
+	} else {
+		splitvec = 0;
+	}
+
+	/* Now splitvec points to the start of the bits we have to copy
+	   into the wbuf */
+	wbuf_ptr = c->wbuf;
+
+	CHECKPOINT(8);
+	for ( ; splitvec < outvec; splitvec++) {
+		/* Don't copy the wbuf into itself */
+		if (outvecs[splitvec].iov_base == c->wbuf)
+			continue;
+		memcpy(wbuf_ptr, outvecs[splitvec].iov_base, outvecs[splitvec].iov_len);
+		wbuf_ptr += outvecs[splitvec].iov_len;
+		donelen += outvecs[splitvec].iov_len;
+	}
+	c->wbuf_len = wbuf_ptr - c->wbuf;
+
+alldone:	
+	CHECKPOINT(9);
+	*retlen = donelen;
+	/* Setup timed wbuf flush, if buffer len != 0 */
+	if (c->wbuf_len) {
+		D1(printk (KERN_DEBUG "jffs2_flash_writev: mod wbuf_timer\n"));	
+		mod_timer(&c->wbuf_timer, jiffies + WBUF_FLUSH_TIMEOUT);
+	}
+	return 0;
+}
+
+/*
+	This is the entry for NOR-Flash. We use it also for NAND to flush wbuf
+*/
+int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf)
+{
+	CHECKPOINT(0);
+	return c->mtd->write(c->mtd, ofs, len, retlen, buf);
+}
+
+/*
+	Handle readback from writebuffer and ECC failure return
+*/
+int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf)
+{
+	loff_t	orbf = 0, owbf = 0, lwbf = 0;
+	int	ret;
+
+	/* Read flash */
+	ret = c->mtd->read(c->mtd, ofs, len, retlen, buf);
+
+	if (!jffs2_can_mark_obsolete(c) && (ret == -EIO) && (*retlen == len) ) {
+		printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%llx) returned ECC error\n", len, ofs);
+		/* 
+		 * We have the raw data without ECC correction in the buffer, maybe 
+		 * we are lucky and all data or parts are correct. We check the node.
+		 * If data are corrupted node check will sort it out.
+		 * We keep this block, it will fail on write or erase and the we
+		 * mark it bad. Or should we do that now? But we should give him a chance.
+		 * Maybe we had a system crash or power loss before the ecc write or  
+		 * a erase was completed.
+		 * So we return success. :)
+		 */
+		 ret = 0;	
+	}
+
+	/* if no writebuffer available or write buffer empty, return */
+	if (!c->wbuf_pagesize || !c->wbuf_len)
+		return ret;
+
+
+	/* if we read in a different block, return */
+	if ( (ofs & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) 
+		return ret;	
+
+	if (ofs >= c->wbuf_ofs) {
+		owbf = (ofs - c->wbuf_ofs);	/* offset in write buffer */
+		if (owbf > c->wbuf_len)		/* is read beyond write buffer ? */
+			return ret;
+		lwbf = c->wbuf_len - owbf;	/* number of bytes to copy */
+		if (lwbf > len)	
+			lwbf = len;
+	} else {	
+		orbf = (c->wbuf_ofs - ofs);	/* offset in read buffer */
+		if (orbf > len)			/* is write beyond write buffer ? */
+			return ret;
+		lwbf = len - orbf; 		/* number of bytes to copy */
+		if (lwbf > c->wbuf_len)	
+			lwbf = c->wbuf_len;
+	}	
+	if (lwbf > 0)
+		memcpy(buf+orbf,c->wbuf+owbf,lwbf);
+
+	return ret;
+}
+
diff -Naur jffs2/fs/jffs2/os-linux.h jffs2.new/fs/jffs2/os-linux.h
--- jffs2/fs/jffs2/os-linux.h	Wed Jun 19 18:56:25 2002
+++ jffs2.new/fs/jffs2/os-linux.h	Thu Jul 11 16:13:32 2002
@@ -67,6 +67,24 @@
 #define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
 
 #ifndef CONFIG_JFFS2_FS_NAND
+#ifdef CONFIG_JFFS2_FS_ECC_NOR /* ECC'd NOR flash */
+
+#define jffs2_can_mark_obsolete(c) (0)
+#define jffs2_cleanmarker_oob(c) (0)
+#define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
+
+#define jffs2_nand_read_failcnt(c,jeb) do { ; } while(0)
+#define jffs2_write_nand_badblock(c,jeb) do { ; } while(0)
+
+/* nor_wbuf.c */
+int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen);
+int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf);
+int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf);
+void jffs2_wbuf_timeout(unsigned long data);
+void jffs2_wbuf_process(void *data);
+
+#else /* normal NOR flash */
+
 #define jffs2_can_mark_obsolete(c) (1)
 #define jffs2_cleanmarker_oob(c) (0)
 #define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
@@ -80,6 +98,7 @@
 #define jffs2_wbuf_timeout NULL
 #define jffs2_wbuf_process NULL
 
+#endif
 #else /* NAND support present */
 
 #define jffs2_can_mark_obsolete(c) (c->mtd->type == MTD_NORFLASH || c->mtd->type == MTD_RAM)
diff -Naur jffs2/fs/jffs2/scan.c jffs2.new/fs/jffs2/scan.c
--- jffs2/fs/jffs2/scan.c	Wed Jul  3 15:36:02 2002
+++ jffs2.new/fs/jffs2/scan.c	Thu Jul 11 16:14:19 2002
@@ -342,14 +342,14 @@
 			break;
 
 		case JFFS2_NODETYPE_CLEANMARKER:
-			if (node.totlen != c->cleanmarker_size) {
+			if (node.totlen != sizeof(struct jffs2_cleanmarker)) {
 				printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n", 
-				       ofs, node.totlen, c->cleanmarker_size);
-				DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
+				       ofs, node.totlen, sizeof(struct jffs2_cleanmarker));
+				DIRTY_SPACE(PAD(sizeof(struct jffs2_cleanmarker)));
 			} else if (jeb->first_node) {
 				printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset);
-				DIRTY_SPACE(PAD(c->cleanmarker_size));
-				ofs += PAD(c->cleanmarker_size);
+				DIRTY_SPACE(PAD(sizeof(struct jffs2_cleanmarker)));
+				ofs += PAD(sizeof(struct jffs2_cleanmarker));
 				continue;
 			} else {
 				struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref();
@@ -360,12 +360,12 @@
 				marker_ref->next_in_ino = NULL;
 				marker_ref->next_phys = NULL;
 				marker_ref->flash_offset = ofs;
-				marker_ref->totlen = c->cleanmarker_size;
+				marker_ref->totlen = sizeof(struct jffs2_cleanmarker);
 				jeb->first_node = jeb->last_node = marker_ref;
 			     
-				USED_SPACE(PAD(c->cleanmarker_size));
+				USED_SPACE(PAD(sizeof(struct jffs2_cleanmarker)));
 			}
-			ofs += PAD(c->cleanmarker_size);
+			ofs += PAD(sizeof(struct jffs2_cleanmarker));
 			break;
 
 		case JFFS2_NODETYPE_PADDING:
@@ -407,7 +407,7 @@
 	D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, 
 		  jeb->free_size, jeb->dirty_size, jeb->used_size));
 
-	if (jeb->used_size == PAD(c->cleanmarker_size) && 
+	if (jeb->used_size == PAD(sizeof(struct jffs2_cleanmarker)) && 
 	    !jeb->first_node->next_in_ino && !jeb->dirty_size)
 		return BLK_STATE_CLEANMARKER;
 	else if (jeb->used_size > c->sector_size - (2*sizeof(struct jffs2_raw_inode)))
@@ -628,7 +628,7 @@
 		if (!jeb->used_size) {
 			D1(printk(KERN_DEBUG "No valid nodes yet found in this eraseblock 0x%08x, so obsoleting the new instance at 0x%08x\n", 
 				  jeb->offset, raw->flash_offset & ~3));
- 			ri.nodetype &= ~JFFS2_NODE_ACCURATE;
+			ri.nodetype &= ~JFFS2_NODE_ACCURATE;
 			/* Perhaps we could also mark it as such on the medium. Maybe later */
 		}
 		break;
@@ -654,11 +654,7 @@
 		tn->version = ri.version;
 
 		USED_SPACE(PAD(ri.totlen));
-
-		/* No need to scan from the beginning of the list again. 
-		   We can start from tn_list instead (Thanks Jocke) */
-		jffs2_add_tn_to_list(tn, tn_list);
-
+		jffs2_add_tn_to_list(tn, &ic->scan->tmpnodes);
 		/* Make sure the one we just added is the _last_ in the list
 		   with this version number, so the older ones get obsoleted */
 		while (tn->next && tn->next->version == tn->version) {
diff -Naur jffs2/include/linux/jffs2.h jffs2.new/include/linux/jffs2.h
--- jffs2/include/linux/jffs2.h	Wed Jun 19 18:56:27 2002
+++ jffs2.new/include/linux/jffs2.h	Thu Jul 11 16:03:37 2002
@@ -77,6 +77,15 @@
 	uint32_t hdr_crc;
 } __attribute__((packed));
 
+struct jffs2_cleanmarker
+{
+	uint16_t magic;
+	uint16_t nodetype;
+	uint32_t totlen;
+	uint32_t hdr_crc;
+	uint32_t padding; /* Necessary for some STMicro flashes */
+} __attribute__((packed));
+
 struct jffs2_raw_dirent
 {
 	uint16_t magic;
@@ -130,6 +139,7 @@
 union jffs2_node_union {
 	struct jffs2_raw_inode i;
 	struct jffs2_raw_dirent d;
+	struct jffs2_cleanmarker c;
 	struct jffs2_unknown_node u;
 };
 




More information about the linux-mtd mailing list