Skip to content

Commit

Permalink
[JFFS2] Add support for write-buffer verification.
Browse files Browse the repository at this point in the history
We've seen some evil corruption issues, where the corruption seems to be
introduced after the JFFS2 crc32 is calculated but before the NAND
controller calculates the ECC. So it's in RAM or in the PCI DMA
transfer; not on the flash. Attempt to catch it earlier by (optionally)
reading back from the flash immediately after writing it.

Signed-off-by: David Woodhouse <dwmw2@infradead.org>
  • Loading branch information
dwmw2 committed Jul 11, 2007
1 parent ef53cb0 commit a6bc432
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 10 deletions.
8 changes: 8 additions & 0 deletions fs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1228,6 +1228,14 @@ config JFFS2_FS_WRITEBUFFER
- NOR flash with transparent ECC
- DataFlash

config JFFS2_FS_WBUF_VERIFY
bool "Verify JFFS2 write-buffer reads"
depends on JFFS2_FS_WRITEBUFFER
default n
help
This causes JFFS2 to read back every page written through the
write-buffer, and check for errors.

config JFFS2_SUMMARY
bool "JFFS2 summary support (EXPERIMENTAL)"
depends on JFFS2_FS && EXPERIMENTAL
Expand Down
3 changes: 3 additions & 0 deletions fs/jffs2/jffs2_fs_sb.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ struct jffs2_sb_info {

uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */

#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
unsigned char *wbuf_verify; /* read-back buffer for verification */
#endif
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
unsigned char *wbuf; /* Write-behind buffer for NAND flash */
uint32_t wbuf_ofs;
Expand Down
73 changes: 63 additions & 10 deletions fs/jffs2/wbuf.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,47 @@ static struct jffs2_raw_node_ref **jffs2_incore_replace_raw(struct jffs2_sb_info
return NULL;
}

#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
static int jffs2_verify_write(struct jffs2_sb_info *c, unsigned char *buf,
uint32_t ofs)
{
int ret;
size_t retlen;
char *eccstr;

ret = c->mtd->read(c->mtd, ofs, c->wbuf_pagesize, &retlen, c->wbuf_verify);
if (ret && ret != -EUCLEAN && ret != -EBADMSG) {
printk(KERN_WARNING "jffs2_verify_write(): Read back of page at %08x failed: %d\n", c->wbuf_ofs, ret);
return ret;
} else if (retlen != c->wbuf_pagesize) {
printk(KERN_WARNING "jffs2_verify_write(): Read back of page at %08x gave short read: %zd not %d.\n", ofs, retlen, c->wbuf_pagesize);
return -EIO;
}
if (!memcmp(buf, c->wbuf_verify, c->wbuf_pagesize))
return 0;

if (ret == -EUCLEAN)
eccstr = "corrected";
else if (ret == -EBADMSG)
eccstr = "correction failed";
else
eccstr = "OK or unused";

printk(KERN_WARNING "Write verify error (ECC %s) at %08x. Wrote:\n",
eccstr, c->wbuf_ofs);
print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
c->wbuf, c->wbuf_pagesize, 0);

printk(KERN_WARNING "Read back:\n");
print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
c->wbuf_verify, c->wbuf_pagesize, 0);

return -EIO;
}
#else
#define jffs2_verify_write(c,b,o) (0)
#endif

/* Recover from failure to write wbuf. Recover the nodes up to the
* wbuf, not the one which we were starting to try to write. */

Expand Down Expand Up @@ -380,7 +421,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
ret = c->mtd->write(c->mtd, ofs, towrite, &retlen,
rewrite_buf);

if (ret || retlen != towrite) {
if (ret || retlen != towrite || jffs2_verify_write(c, rewrite_buf, ofs)) {
/* Argh. We tried. Really we did. */
printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n");
kfree(buf);
Expand Down Expand Up @@ -587,15 +628,16 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)

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_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;
}

if (ret) {
printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n", ret);
goto wfail;
} else if (retlen != c->wbuf_pagesize) {
printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n",
retlen, c->wbuf_pagesize);
ret = -EIO;
goto wfail;
} else if ((ret = jffs2_verify_write(c, c->wbuf, c->wbuf_ofs))) {
wfail:
jffs2_wbuf_recover(c);

return ret;
Expand Down Expand Up @@ -1138,11 +1180,22 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
return -ENOMEM;
}

#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
if (!c->wbuf_verify) {
kfree(c->oobbuf);
kfree(c->wbuf);
return -ENOMEM;
}
#endif
return 0;
}

void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
{
#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
kfree(c->wbuf_verify);
#endif
kfree(c->wbuf);
kfree(c->oobbuf);
}
Expand Down

0 comments on commit a6bc432

Please sign in to comment.