diff options
author | David Woodhouse <dwmw2@infradead.org> | 2007-07-11 09:23:54 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2007-07-11 09:23:54 -0400 |
commit | a6bc432e296dfa1f05d4b586ca5ca3085a2d42d7 (patch) | |
tree | db51669fcc74686efb4c65f98938907d76202ffa | |
parent | ef53cb02ffee8ceb44ea75f778f77eace6b9c89a (diff) |
[JFFS2] Add support for write-buffer verification.
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>
-rw-r--r-- | fs/Kconfig | 8 | ||||
-rw-r--r-- | fs/jffs2/jffs2_fs_sb.h | 3 | ||||
-rw-r--r-- | fs/jffs2/wbuf.c | 73 |
3 files changed, 74 insertions, 10 deletions
diff --git a/fs/Kconfig b/fs/Kconfig index e9c5b81dd665..e19423a2aa7d 100644 --- a/fs/Kconfig +++ b/fs/Kconfig | |||
@@ -1228,6 +1228,14 @@ config JFFS2_FS_WRITEBUFFER | |||
1228 | - NOR flash with transparent ECC | 1228 | - NOR flash with transparent ECC |
1229 | - DataFlash | 1229 | - DataFlash |
1230 | 1230 | ||
1231 | config JFFS2_FS_WBUF_VERIFY | ||
1232 | bool "Verify JFFS2 write-buffer reads" | ||
1233 | depends on JFFS2_FS_WRITEBUFFER | ||
1234 | default n | ||
1235 | help | ||
1236 | This causes JFFS2 to read back every page written through the | ||
1237 | write-buffer, and check for errors. | ||
1238 | |||
1231 | config JFFS2_SUMMARY | 1239 | config JFFS2_SUMMARY |
1232 | bool "JFFS2 summary support (EXPERIMENTAL)" | 1240 | bool "JFFS2 summary support (EXPERIMENTAL)" |
1233 | depends on JFFS2_FS && EXPERIMENTAL | 1241 | depends on JFFS2_FS && EXPERIMENTAL |
diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index b13298a824ed..ae99cd7fd43b 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h | |||
@@ -106,6 +106,9 @@ struct jffs2_sb_info { | |||
106 | 106 | ||
107 | uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ | 107 | uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ |
108 | 108 | ||
109 | #ifdef CONFIG_JFFS2_FS_WBUF_VERIFY | ||
110 | unsigned char *wbuf_verify; /* read-back buffer for verification */ | ||
111 | #endif | ||
109 | #ifdef CONFIG_JFFS2_FS_WRITEBUFFER | 112 | #ifdef CONFIG_JFFS2_FS_WRITEBUFFER |
110 | unsigned char *wbuf; /* Write-behind buffer for NAND flash */ | 113 | unsigned char *wbuf; /* Write-behind buffer for NAND flash */ |
111 | uint32_t wbuf_ofs; | 114 | uint32_t wbuf_ofs; |
diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 64f8d7add7e8..ec99c8ec83ae 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c | |||
@@ -220,6 +220,47 @@ static struct jffs2_raw_node_ref **jffs2_incore_replace_raw(struct jffs2_sb_info | |||
220 | return NULL; | 220 | return NULL; |
221 | } | 221 | } |
222 | 222 | ||
223 | #ifdef CONFIG_JFFS2_FS_WBUF_VERIFY | ||
224 | static int jffs2_verify_write(struct jffs2_sb_info *c, unsigned char *buf, | ||
225 | uint32_t ofs) | ||
226 | { | ||
227 | int ret; | ||
228 | size_t retlen; | ||
229 | char *eccstr; | ||
230 | |||
231 | ret = c->mtd->read(c->mtd, ofs, c->wbuf_pagesize, &retlen, c->wbuf_verify); | ||
232 | if (ret && ret != -EUCLEAN && ret != -EBADMSG) { | ||
233 | printk(KERN_WARNING "jffs2_verify_write(): Read back of page at %08x failed: %d\n", c->wbuf_ofs, ret); | ||
234 | return ret; | ||
235 | } else if (retlen != c->wbuf_pagesize) { | ||
236 | printk(KERN_WARNING "jffs2_verify_write(): Read back of page at %08x gave short read: %zd not %d.\n", ofs, retlen, c->wbuf_pagesize); | ||
237 | return -EIO; | ||
238 | } | ||
239 | if (!memcmp(buf, c->wbuf_verify, c->wbuf_pagesize)) | ||
240 | return 0; | ||
241 | |||
242 | if (ret == -EUCLEAN) | ||
243 | eccstr = "corrected"; | ||
244 | else if (ret == -EBADMSG) | ||
245 | eccstr = "correction failed"; | ||
246 | else | ||
247 | eccstr = "OK or unused"; | ||
248 | |||
249 | printk(KERN_WARNING "Write verify error (ECC %s) at %08x. Wrote:\n", | ||
250 | eccstr, c->wbuf_ofs); | ||
251 | print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1, | ||
252 | c->wbuf, c->wbuf_pagesize, 0); | ||
253 | |||
254 | printk(KERN_WARNING "Read back:\n"); | ||
255 | print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1, | ||
256 | c->wbuf_verify, c->wbuf_pagesize, 0); | ||
257 | |||
258 | return -EIO; | ||
259 | } | ||
260 | #else | ||
261 | #define jffs2_verify_write(c,b,o) (0) | ||
262 | #endif | ||
263 | |||
223 | /* Recover from failure to write wbuf. Recover the nodes up to the | 264 | /* Recover from failure to write wbuf. Recover the nodes up to the |
224 | * wbuf, not the one which we were starting to try to write. */ | 265 | * wbuf, not the one which we were starting to try to write. */ |
225 | 266 | ||
@@ -380,7 +421,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) | |||
380 | ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, | 421 | ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, |
381 | rewrite_buf); | 422 | rewrite_buf); |
382 | 423 | ||
383 | if (ret || retlen != towrite) { | 424 | if (ret || retlen != towrite || jffs2_verify_write(c, rewrite_buf, ofs)) { |
384 | /* Argh. We tried. Really we did. */ | 425 | /* Argh. We tried. Really we did. */ |
385 | printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n"); | 426 | printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n"); |
386 | kfree(buf); | 427 | kfree(buf); |
@@ -587,15 +628,16 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) | |||
587 | 628 | ||
588 | ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf); | 629 | ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf); |
589 | 630 | ||
590 | if (ret || retlen != c->wbuf_pagesize) { | 631 | if (ret) { |
591 | if (ret) | 632 | printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n", ret); |
592 | printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n",ret); | 633 | goto wfail; |
593 | else { | 634 | } else if (retlen != c->wbuf_pagesize) { |
594 | printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n", | 635 | printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n", |
595 | retlen, c->wbuf_pagesize); | 636 | retlen, c->wbuf_pagesize); |
596 | ret = -EIO; | 637 | ret = -EIO; |
597 | } | 638 | goto wfail; |
598 | 639 | } else if ((ret = jffs2_verify_write(c, c->wbuf, c->wbuf_ofs))) { | |
640 | wfail: | ||
599 | jffs2_wbuf_recover(c); | 641 | jffs2_wbuf_recover(c); |
600 | 642 | ||
601 | return ret; | 643 | return ret; |
@@ -1138,11 +1180,22 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c) | |||
1138 | return -ENOMEM; | 1180 | return -ENOMEM; |
1139 | } | 1181 | } |
1140 | 1182 | ||
1183 | #ifdef CONFIG_JFFS2_FS_WBUF_VERIFY | ||
1184 | c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL); | ||
1185 | if (!c->wbuf_verify) { | ||
1186 | kfree(c->oobbuf); | ||
1187 | kfree(c->wbuf); | ||
1188 | return -ENOMEM; | ||
1189 | } | ||
1190 | #endif | ||
1141 | return 0; | 1191 | return 0; |
1142 | } | 1192 | } |
1143 | 1193 | ||
1144 | void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) | 1194 | void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) |
1145 | { | 1195 | { |
1196 | #ifdef CONFIG_JFFS2_FS_WBUF_VERIFY | ||
1197 | kfree(c->wbuf_verify); | ||
1198 | #endif | ||
1146 | kfree(c->wbuf); | 1199 | kfree(c->wbuf); |
1147 | kfree(c->oobbuf); | 1200 | kfree(c->oobbuf); |
1148 | } | 1201 | } |