From: Thomas Gleixner Date: Mon, 29 May 2006 12:56:39 +0000 (+0200) Subject: [MTD] NAND Signal that a bitflip was corrected by ECC X-Git-Tag: v2.6.18-rc1~1105^2~24 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9a1fcdfd4bee27c418424cac47abf7c049541297;p=linux-2.6 [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 --- diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index efb1a95aa0..1e21a2c3dd 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 ret = mtd->read(mtd, (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, &retlen, movebuf); - if (ret < 0) { + if (ret < 0 && ret != -EUCLEAN) { ret = mtd->read(mtd, (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, @@ -922,7 +922,10 @@ foundit: } else { size_t retlen; loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; - if (mtd->read(mtd, ptr, SECTORSIZE, &retlen, buffer)) + int ret = mtd->read(mtd, ptr, SECTORSIZE, &retlen, buffer); + + /* Handle corrected bit flips gracefully */ + if (ret < 0 && ret != -EUCLEAN) return -EIO; } return 0; diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 7522fc3a28..a48210d58b 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 /* Nand returns -EBADMSG on ecc errors, but it returns * the data. For our userspace tools it is important * to dump areas with ecc errors ! + * For kernel internal usage it also might return -EUCLEAN + * to signal the caller that a bitflip has occured and has + * been corrected by the ECC algorithm. * Userspace software which accesses NAND this way * must be aware of the fact that it deals with NAND */ - if (!ret || (ret == -EBADMSG)) { + if (!ret || (ret == -EUCLEAN) || (ret == -EBADMSG)) { *ppos += retlen; if (copy_to_user(buf, kbuf, retlen)) { kfree(kbuf); diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 38151b8e66..3c8d5e6fa0 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, size_t * retlen, u_char * buf) { struct mtd_concat *concat = CONCAT(mtd); - int err = -EINVAL; + int ret = 0, err = -EINVAL; int i; *retlen = 0; @@ -80,9 +80,18 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len, err = subdev->read(subdev, from, size, &retsize, buf); - if (err) + if (err && (err != -EBADMSG) && (err != -EUCLEAN)) break; + /* Save information about bitflips! */ + if (err) { + if (err == -EBADMSG) + ret = err; + else if (!ret) + ret = err; + err = 0; + } + *retlen += retsize; len -= size; if (len == 0) @@ -92,7 +101,7 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len, buf += size; from = 0; } - return err; + return err ? err : ret; } static int diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index b8e6e1579c..7a3a449077 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, if (ret) return ret; - return mtd->ecc_stats.failed - stats.failed ? -EBADMSG : 0; + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; + + return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; } /** diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index f6ffe7949b..dc7573501d 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 ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), 512, &retlen, movebuf); - if (ret < 0) { + if (ret < 0 && ret != -EUCLEAN) { ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), 512, &retlen, movebuf); @@ -768,7 +768,9 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, } else { loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs; size_t retlen; - if (mtd->read(mtd, ptr, 512, &retlen, buffer)) + int res = mtd->read(mtd, ptr, 512, &retlen, buffer); + + if (res < 0 && res != -EUCLEAN) return -EIO; } return 0; diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 1195d06d43..a7f153f79e 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) /* Do the read... */ ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf); - if (ret == -EBADMSG && retlen == c->wbuf_ofs - start) { - /* ECC recovered */ + /* ECC recovered ? */ + if ((ret == -EUCLEAN || ret == -EBADMSG) && + (retlen == c->wbuf_ofs - start)) ret = 0; - } + if (ret || retlen != c->wbuf_ofs - start) { printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n"); @@ -908,20 +909,21 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re down_read(&c->wbuf_sem); ret = c->mtd->read(c->mtd, ofs, len, retlen, buf); - if ( (ret == -EBADMSG) && (*retlen == len) ) { - printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n", - len, ofs); + if ( (ret == -EBADMSG || ret == -EUCLEAN) && (*retlen == len) ) { + if (ret == -EBADMSG) + printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx)" + " returned ECC error\n", len, ofs); /* - * We have the raw data without ECC correction in the buffer, maybe - * we are lucky and all data or parts are correct. We check the node. - * If data are corrupted node check will sort it out. - * We keep this block, it will fail on write or erase and the we - * mark it bad. Or should we do that now? But we should give him a chance. - * Maybe we had a system crash or power loss before the ecc write or - * a erase was completed. + * We have the raw data without ECC correction in the buffer, + * maybe we are lucky and all data or parts are correct. We + * check the node. If data are corrupted node check will sort + * it out. We keep this block, it will fail on write or erase + * and the we mark it bad. Or should we do that now? But we + * should give him a chance. Maybe we had a system crash or + * power loss before the ecc write or a erase was completed. * So we return success. :) */ - ret = 0; + ret = 0; } /* 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 orbf = (c->wbuf_ofs - ofs); /* offset in read buffer */ if (orbf > len) /* is write beyond write buffer ? */ goto exit; - lwbf = len - orbf; /* number of bytes to copy */ + lwbf = len - orbf; /* number of bytes to copy */ if (lwbf > c->wbuf_len) lwbf = c->wbuf_len; }