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