mtd/fs/jffs2 wbuf.c,1.41,1.42

David Woodhouse dwmw2 at infradead.org
Thu Oct 2 12:31:51 EDT 2003


Update of /home/cvs/mtd/fs/jffs2
In directory phoenix.infradead.org:/tmp/cvs-serv16411/fs/jffs2

Modified Files:
	wbuf.c 
Log Message:
wbuf recovery. My head hurts

Index: wbuf.c
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/wbuf.c,v
retrieving revision 1.41
retrieving revision 1.42
diff -u -r1.41 -r1.42
--- wbuf.c	29 Aug 2003 15:31:44 -0000	1.41
+++ wbuf.c	2 Oct 2003 16:31:48 -0000	1.42
@@ -120,6 +120,197 @@
 	}
 }
 
+/* Recover from failure to write wbuf. Recover the nodes up to the
+ * wbuf, not the one which we were starting to try to write. */
+
+static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
+{
+	struct jffs2_eraseblock *jeb, *new_jeb;
+	struct jffs2_raw_node_ref **first_raw, **raw;
+	size_t retlen;
+	int ret;
+	unsigned char *buf;
+	uint32_t start, end, ofs, len;
+
+	spin_lock(&c->erase_completion_lock);
+
+	jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
+
+	/* File the existing block on the bad_used_list.... */
+	if (c->nextblock == jeb)
+		c->nextblock = NULL;
+	/* else BUG() I think but I need more coffee. */
+	list_del(&jeb->list);
+	list_add(&jeb->list, &c->bad_used_list);
+
+	/* Adjust its size counts accordingly */
+	jeb->wasted_size += jeb->free_size;
+	c->wasted_size += jeb->free_size;
+	c->free_size -= jeb->free_size; 
+	jeb->free_size = 0;
+       
+	/* Find the first node to be recovered */
+	first_raw = &jeb->first_node;
+	while (*first_raw && ref_offset(*first_raw) < c->wbuf_ofs && !ref_obsolete(*first_raw))
+		first_raw = &(*first_raw)->next_phys;
+
+	if (!*first_raw) {
+		/* All nodes were obsolete. Nothing to recover. */
+		D1(printk(KERN_DEBUG "No non-obsolete nodes to be recovered. Just filing block bad\n"));
+		spin_unlock(&c->erase_completion_lock);
+		return;
+	}
+
+	start = ref_offset(*first_raw);
+	end = ref_offset(*first_raw) + (*first_raw)->totlen;
+
+	/* Find the last node to be recovered */
+	raw = first_raw;
+	while ((*raw)->next_phys) {
+		if (!ref_obsolete(*raw))
+			end = ref_offset(*raw) + (*raw)->totlen;
+
+		raw = &(*raw)->next_phys;
+	}
+	spin_unlock(&c->erase_completion_lock);
+
+	D1(printk(KERN_DEBUG "wbuf recover %08x-%08x\n", start, end));
+
+	buf = NULL;
+	if (start < c->wbuf_ofs) {
+		/* First affected node was already partially written.
+		 * Attempt to reread the old data into our buffer. */
+
+		buf = kmalloc(end - start, GFP_KERNEL);
+		if (!buf) {
+			printk(KERN_CRIT "Malloc failure in wbuf recovery. Data loss ensues.\n");
+
+			goto read_failed;
+		}
+
+		/* Do the read... */
+		ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf);
+
+		if (ret || retlen != c->wbuf_ofs - start) {
+			printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n");
+
+			kfree(buf);
+			buf = NULL;
+		read_failed:
+			first_raw = &(*first_raw)->next_phys;
+			/* If this was the only node to be recovered, give up */
+			if (!(*first_raw))
+				return;
+
+			/* It wasn't. Go on and try to recover nodes complete in the wbuf */
+			start = ref_offset(*first_raw);
+		} else {
+			/* Read succeeded. Copy the remaining data from the wbuf */
+			memcpy(buf + (c->wbuf_ofs - start), c->wbuf, end - c->wbuf_ofs);
+		}
+	}
+	/* OK... we're to rewrite (end-start) bytes of data from first_raw onwards.
+	   Either 'buf' contains the data, or we find it in the wbuf */
+
+
+	/* ... and get an allocation of space from a shiny new block instead */
+	ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len);
+	if (ret) {
+		printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n");
+		if (buf)
+			kfree(buf);
+		return;
+	}
+	if (end-start >= c->wbuf_pagesize) {
+		/* Need to do another write immediately. This, btw,
+		 means that we'll be writing from 'buf' and not from
+		 the wbuf. Since if we're writing from the wbuf there
+		 won't be more than a wbuf full of data, now will
+		 there? :) */
+		ret = c->mtd->write_ecc(c->mtd, ofs, (end-start)%c->wbuf_pagesize,
+					&retlen, buf, NULL, c->oobinfo);
+		if (!ret || retlen != (end-start)%c->wbuf_pagesize) {
+			/* Argh. We tried. Really we did. */
+			printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n");
+			kfree(buf);
+
+			if (retlen) {
+				struct jffs2_raw_node_ref *raw2;
+
+				raw2 = jffs2_alloc_raw_node_ref();
+				if (!raw2)
+					return;
+
+				raw2->flash_offset = ofs | REF_OBSOLETE;
+				raw2->totlen = (*first_raw)->totlen;
+				raw2->next_phys = NULL;
+				raw2->next_in_ino = NULL;
+
+				jffs2_add_physical_node_ref(c, raw2);
+			}
+			return;
+		}
+		printk(KERN_NOTICE "Recovery of wbuf succeeded to %08x\n", ofs);
+
+		c->wbuf_len = (end - start) - retlen;
+		c->wbuf_ofs = ofs + retlen;
+		memcpy(c->wbuf, buf + retlen, c->wbuf_len);
+		/* Don't muck about with c->wbuf_inodes. False positives are harmless. */
+
+		spin_lock(&c->erase_completion_lock);
+	} else {
+		/* OK, now we're left with the dregs in the wbuf */
+		memmove(c->wbuf, c->wbuf + (start - c->wbuf_ofs), end - start);
+		c->wbuf_ofs = ofs;
+		c->wbuf_len = end - start;
+	}
+	/* Now sort out the jffs2_raw_node_refs, moving them from the old to the next block */
+	new_jeb = &c->blocks[ofs / c->sector_size];
+
+	/* Yeah right, we just started using it... */
+	BUG_ON(new_jeb->first_node);
+	
+	spin_lock(&c->erase_completion_lock);
+	if (new_jeb->first_node) {
+		/* Odd, but possible with ST flash later maybe */
+		new_jeb->last_node->next_phys = *first_raw;
+	} else {
+		new_jeb->first_node = *first_raw;
+	}
+	raw = first_raw;
+	while (*raw) {
+		if (ref_obsolete(*raw)) {
+			/* Shouldn't really happen much */
+			new_jeb->dirty_size += (*raw)->totlen;
+			c->dirty_size += (*raw)->totlen;
+		} else {
+			new_jeb->used_size += (*raw)->totlen;
+			jeb->used_size  -= (*raw)->totlen;
+			jeb->dirty_size += (*raw)->totlen;
+			c->dirty_size += (*raw)->totlen;
+		}
+		c->free_size -= (*raw)->totlen;
+		(*raw)->flash_offset = ofs | ref_flags(*raw);
+		new_jeb->last_node = *raw;
+	}
+
+	/* Fix up the original jeb now it's on the bad_list */
+	*first_raw = NULL;
+	if (first_raw == &jeb->first_node)
+		jeb->last_node = NULL;
+	else
+		jeb->last_node = container_of(first_raw, struct jffs2_raw_node_ref, next_phys);
+
+	ACCT_SANITY_CHECK(c,jeb);
+        D1(ACCT_PARANOIA_CHECK(jeb));
+
+	ACCT_SANITY_CHECK(c,new_jeb);
+        D1(ACCT_PARANOIA_CHECK(new_jeb));
+
+	spin_unlock(&c->erase_completion_lock);
+
+	D1(printk(KERN_DEBUG "wbuf recovery completed OK\n"));
+}
 
 /* Meaning of pad argument:
    0: Do not pad. Probably pointless - we only ever use this when we can't pad anyway.
@@ -169,24 +360,15 @@
 	
 	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: %zd instead of %d\n",
+			printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n",ret);
+		else {
+			printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd 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
-		 */
+			ret = -EIO;
+		}
+
+		jffs2_wbuf_recover(c);
 
-		/*  FIXME NAND
-		   If we were called from GC or fsync, there's no repair kit yet
-		*/
-		    
 		return ret; 
 	}
 
@@ -222,7 +404,6 @@
 	memset(c->wbuf,0xff,c->wbuf_pagesize);
 	/* adjust write buffer offset, else we get a non contiguous write bug */
 	c->wbuf_ofs += c->wbuf_pagesize;
-	c->wbuf_partnode_ofs = c->wbuf_ofs;
 	c->wbuf_len = 0;
 	return 0;
 }
@@ -385,12 +566,13 @@
 		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 zero to make sure our caller doesn't mark the space dirty.
+			   We've already done everything that's necessary */
 			*retlen = 0;
 			return ret;
 		}
 		outvec_to += donelen;
 		c->wbuf_ofs = outvec_to;
-		c->wbuf_partnode_ofs = to;
 
 		/* All invecs done ? */
 		if (invec == count)
@@ -448,7 +630,6 @@
 		
 		donelen += wbuf_retlen;
 		c->wbuf_ofs = PAGE_DIV(outvec_to) + PAGE_DIV(totlen);
-		c->wbuf_partnode_ofs = to;
 
 		if (remainder) {
 			outvecs[splitvec].iov_base += split_ofs;
@@ -833,7 +1014,6 @@
 	/* Initialise write buffer */
 	c->wbuf_pagesize = c->mtd->oobblock;
 	c->wbuf_ofs = 0xFFFFFFFF;
-	c->wbuf_partnode_ofs = c->wbuf_ofs;
 
 	/* FIXME: If we had a generic way of describing the hardware's
 	   use of OOB area, we could perhaps make this generic too. */




More information about the linux-mtd-cvs mailing list