diff options
author | Thomas Gleixner <tglx@cruncher.tec.linutronix.de> | 2006-05-29 08:56:39 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@cruncher.tec.linutronix.de> | 2006-05-29 09:06:51 -0400 |
commit | 9a1fcdfd4bee27c418424cac47abf7c049541297 (patch) | |
tree | 5baa5f1e1d1a296a319bf6a5a4b636668c107e00 | |
parent | 8593fbc68b0df1168995de76d1af38eb62fd6b62 (diff) |
[MTD] NAND Signal that a bitflip was corrected by ECC
Return -EUCLEAN on read when a bitflip was detected and corrected, so the
clients can react and eventually copy the affected block to a spare one.
Make all in kernel users aware of the change.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | drivers/mtd/inftlcore.c | 7 | ||||
-rw-r--r-- | drivers/mtd/mtdchar.c | 5 | ||||
-rw-r--r-- | drivers/mtd/mtdconcat.c | 15 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 5 | ||||
-rw-r--r-- | drivers/mtd/nftlcore.c | 6 | ||||
-rw-r--r-- | fs/jffs2/wbuf.c | 32 |
6 files changed, 46 insertions, 24 deletions
diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index efb1a95aa0a0..1e21a2c3dd29 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c | |||
@@ -355,7 +355,7 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned | |||
355 | ret = mtd->read(mtd, (inftl->EraseSize * BlockMap[block]) + | 355 | ret = mtd->read(mtd, (inftl->EraseSize * BlockMap[block]) + |
356 | (block * SECTORSIZE), SECTORSIZE, &retlen, | 356 | (block * SECTORSIZE), SECTORSIZE, &retlen, |
357 | movebuf); | 357 | movebuf); |
358 | if (ret < 0) { | 358 | if (ret < 0 && ret != -EUCLEAN) { |
359 | ret = mtd->read(mtd, | 359 | ret = mtd->read(mtd, |
360 | (inftl->EraseSize * BlockMap[block]) + | 360 | (inftl->EraseSize * BlockMap[block]) + |
361 | (block * SECTORSIZE), SECTORSIZE, | 361 | (block * SECTORSIZE), SECTORSIZE, |
@@ -922,7 +922,10 @@ foundit: | |||
922 | } else { | 922 | } else { |
923 | size_t retlen; | 923 | size_t retlen; |
924 | loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; | 924 | loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; |
925 | if (mtd->read(mtd, ptr, SECTORSIZE, &retlen, buffer)) | 925 | int ret = mtd->read(mtd, ptr, SECTORSIZE, &retlen, buffer); |
926 | |||
927 | /* Handle corrected bit flips gracefully */ | ||
928 | if (ret < 0 && ret != -EUCLEAN) | ||
926 | return -EIO; | 929 | return -EIO; |
927 | } | 930 | } |
928 | return 0; | 931 | return 0; |
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 7522fc3a2827..a48210d58b92 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c | |||
@@ -199,10 +199,13 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t | |||
199 | /* Nand returns -EBADMSG on ecc errors, but it returns | 199 | /* Nand returns -EBADMSG on ecc errors, but it returns |
200 | * the data. For our userspace tools it is important | 200 | * the data. For our userspace tools it is important |
201 | * to dump areas with ecc errors ! | 201 | * to dump areas with ecc errors ! |
202 | * For kernel internal usage it also might return -EUCLEAN | ||
203 | * to signal the caller that a bitflip has occured and has | ||
204 | * been corrected by the ECC algorithm. | ||
202 | * Userspace software which accesses NAND this way | 205 | * Userspace software which accesses NAND this way |
203 | * must be aware of the fact that it deals with NAND | 206 | * must be aware of the fact that it deals with NAND |
204 | */ | 207 | */ |
205 | if (!ret || (ret == -EBADMSG)) { | 208 | if (!ret || (ret == -EUCLEAN) || (ret == -EBADMSG)) { |
206 | *ppos += retlen; | 209 | *ppos += retlen; |
207 | if (copy_to_user(buf, kbuf, retlen)) { | 210 | if (copy_to_user(buf, kbuf, retlen)) { |
208 | kfree(kbuf); | 211 | kfree(kbuf); |
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 38151b8e6631..3c8d5e6fa010 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c | |||
@@ -56,7 +56,7 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
56 | size_t * retlen, u_char * buf) | 56 | size_t * retlen, u_char * buf) |
57 | { | 57 | { |
58 | struct mtd_concat *concat = CONCAT(mtd); | 58 | struct mtd_concat *concat = CONCAT(mtd); |
59 | int err = -EINVAL; | 59 | int ret = 0, err = -EINVAL; |
60 | int i; | 60 | int i; |
61 | 61 | ||
62 | *retlen = 0; | 62 | *retlen = 0; |
@@ -80,9 +80,18 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
80 | 80 | ||
81 | err = subdev->read(subdev, from, size, &retsize, buf); | 81 | err = subdev->read(subdev, from, size, &retsize, buf); |
82 | 82 | ||
83 | if (err) | 83 | if (err && (err != -EBADMSG) && (err != -EUCLEAN)) |
84 | break; | 84 | break; |
85 | 85 | ||
86 | /* Save information about bitflips! */ | ||
87 | if (err) { | ||
88 | if (err == -EBADMSG) | ||
89 | ret = err; | ||
90 | else if (!ret) | ||
91 | ret = err; | ||
92 | err = 0; | ||
93 | } | ||
94 | |||
86 | *retlen += retsize; | 95 | *retlen += retsize; |
87 | len -= size; | 96 | len -= size; |
88 | if (len == 0) | 97 | if (len == 0) |
@@ -92,7 +101,7 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
92 | buf += size; | 101 | buf += size; |
93 | from = 0; | 102 | from = 0; |
94 | } | 103 | } |
95 | return err; | 104 | return err ? err : ret; |
96 | } | 105 | } |
97 | 106 | ||
98 | static int | 107 | static int |
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index b8e6e1579cf1..7a3a44907715 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c | |||
@@ -1035,7 +1035,10 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, | |||
1035 | if (ret) | 1035 | if (ret) |
1036 | return ret; | 1036 | return ret; |
1037 | 1037 | ||
1038 | return mtd->ecc_stats.failed - stats.failed ? -EBADMSG : 0; | 1038 | if (mtd->ecc_stats.failed - stats.failed) |
1039 | return -EBADMSG; | ||
1040 | |||
1041 | return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; | ||
1039 | } | 1042 | } |
1040 | 1043 | ||
1041 | /** | 1044 | /** |
diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index f6ffe7949b26..dc7573501d8c 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c | |||
@@ -422,7 +422,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p | |||
422 | 422 | ||
423 | ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), | 423 | ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), |
424 | 512, &retlen, movebuf); | 424 | 512, &retlen, movebuf); |
425 | if (ret < 0) { | 425 | if (ret < 0 && ret != -EUCLEAN) { |
426 | ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) | 426 | ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) |
427 | + (block * 512), 512, &retlen, | 427 | + (block * 512), 512, &retlen, |
428 | movebuf); | 428 | movebuf); |
@@ -768,7 +768,9 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, | |||
768 | } else { | 768 | } else { |
769 | loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs; | 769 | loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs; |
770 | size_t retlen; | 770 | size_t retlen; |
771 | if (mtd->read(mtd, ptr, 512, &retlen, buffer)) | 771 | int res = mtd->read(mtd, ptr, 512, &retlen, buffer); |
772 | |||
773 | if (res < 0 && res != -EUCLEAN) | ||
772 | return -EIO; | 774 | return -EIO; |
773 | } | 775 | } |
774 | return 0; | 776 | return 0; |
diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 1195d06d4373..a7f153f79ecb 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c | |||
@@ -296,10 +296,11 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) | |||
296 | /* Do the read... */ | 296 | /* Do the read... */ |
297 | ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf); | 297 | ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf); |
298 | 298 | ||
299 | if (ret == -EBADMSG && retlen == c->wbuf_ofs - start) { | 299 | /* ECC recovered ? */ |
300 | /* ECC recovered */ | 300 | if ((ret == -EUCLEAN || ret == -EBADMSG) && |
301 | (retlen == c->wbuf_ofs - start)) | ||
301 | ret = 0; | 302 | ret = 0; |
302 | } | 303 | |
303 | if (ret || retlen != c->wbuf_ofs - start) { | 304 | if (ret || retlen != c->wbuf_ofs - start) { |
304 | printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n"); | 305 | printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n"); |
305 | 306 | ||
@@ -908,20 +909,21 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re | |||
908 | down_read(&c->wbuf_sem); | 909 | down_read(&c->wbuf_sem); |
909 | ret = c->mtd->read(c->mtd, ofs, len, retlen, buf); | 910 | ret = c->mtd->read(c->mtd, ofs, len, retlen, buf); |
910 | 911 | ||
911 | if ( (ret == -EBADMSG) && (*retlen == len) ) { | 912 | if ( (ret == -EBADMSG || ret == -EUCLEAN) && (*retlen == len) ) { |
912 | printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n", | 913 | if (ret == -EBADMSG) |
913 | len, ofs); | 914 | printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx)" |
915 | " returned ECC error\n", len, ofs); | ||
914 | /* | 916 | /* |
915 | * We have the raw data without ECC correction in the buffer, maybe | 917 | * We have the raw data without ECC correction in the buffer, |
916 | * we are lucky and all data or parts are correct. We check the node. | 918 | * maybe we are lucky and all data or parts are correct. We |
917 | * If data are corrupted node check will sort it out. | 919 | * check the node. If data are corrupted node check will sort |
918 | * We keep this block, it will fail on write or erase and the we | 920 | * it out. We keep this block, it will fail on write or erase |
919 | * mark it bad. Or should we do that now? But we should give him a chance. | 921 | * and the we mark it bad. Or should we do that now? But we |
920 | * Maybe we had a system crash or power loss before the ecc write or | 922 | * should give him a chance. Maybe we had a system crash or |
921 | * a erase was completed. | 923 | * power loss before the ecc write or a erase was completed. |
922 | * So we return success. :) | 924 | * So we return success. :) |
923 | */ | 925 | */ |
924 | ret = 0; | 926 | ret = 0; |
925 | } | 927 | } |
926 | 928 | ||
927 | /* if no writebuffer available or write buffer empty, return */ | 929 | /* if no writebuffer available or write buffer empty, return */ |
@@ -943,7 +945,7 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re | |||
943 | orbf = (c->wbuf_ofs - ofs); /* offset in read buffer */ | 945 | orbf = (c->wbuf_ofs - ofs); /* offset in read buffer */ |
944 | if (orbf > len) /* is write beyond write buffer ? */ | 946 | if (orbf > len) /* is write beyond write buffer ? */ |
945 | goto exit; | 947 | goto exit; |
946 | lwbf = len - orbf; /* number of bytes to copy */ | 948 | lwbf = len - orbf; /* number of bytes to copy */ |
947 | if (lwbf > c->wbuf_len) | 949 | if (lwbf > c->wbuf_len) |
948 | lwbf = c->wbuf_len; | 950 | lwbf = c->wbuf_len; |
949 | } | 951 | } |