[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>
This commit is contained in:
parent
8593fbc68b
commit
9a1fcdfd4b
6 changed files with 46 additions and 24 deletions
|
@ -355,7 +355,7 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
|
||||||
ret = mtd->read(mtd, (inftl->EraseSize * BlockMap[block]) +
|
ret = mtd->read(mtd, (inftl->EraseSize * BlockMap[block]) +
|
||||||
(block * SECTORSIZE), SECTORSIZE, &retlen,
|
(block * SECTORSIZE), SECTORSIZE, &retlen,
|
||||||
movebuf);
|
movebuf);
|
||||||
if (ret < 0) {
|
if (ret < 0 && ret != -EUCLEAN) {
|
||||||
ret = mtd->read(mtd,
|
ret = mtd->read(mtd,
|
||||||
(inftl->EraseSize * BlockMap[block]) +
|
(inftl->EraseSize * BlockMap[block]) +
|
||||||
(block * SECTORSIZE), SECTORSIZE,
|
(block * SECTORSIZE), SECTORSIZE,
|
||||||
|
@ -922,7 +922,10 @@ static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
|
||||||
} else {
|
} else {
|
||||||
size_t retlen;
|
size_t retlen;
|
||||||
loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
|
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 -EIO;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -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
|
/* Nand returns -EBADMSG on ecc errors, but it returns
|
||||||
* the data. For our userspace tools it is important
|
* the data. For our userspace tools it is important
|
||||||
* to dump areas with ecc errors !
|
* 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
|
* Userspace software which accesses NAND this way
|
||||||
* must be aware of the fact that it deals with NAND
|
* must be aware of the fact that it deals with NAND
|
||||||
*/
|
*/
|
||||||
if (!ret || (ret == -EBADMSG)) {
|
if (!ret || (ret == -EUCLEAN) || (ret == -EBADMSG)) {
|
||||||
*ppos += retlen;
|
*ppos += retlen;
|
||||||
if (copy_to_user(buf, kbuf, retlen)) {
|
if (copy_to_user(buf, kbuf, retlen)) {
|
||||||
kfree(kbuf);
|
kfree(kbuf);
|
||||||
|
|
|
@ -56,7 +56,7 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||||
size_t * retlen, u_char * buf)
|
size_t * retlen, u_char * buf)
|
||||||
{
|
{
|
||||||
struct mtd_concat *concat = CONCAT(mtd);
|
struct mtd_concat *concat = CONCAT(mtd);
|
||||||
int err = -EINVAL;
|
int ret = 0, err = -EINVAL;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
*retlen = 0;
|
*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);
|
err = subdev->read(subdev, from, size, &retsize, buf);
|
||||||
|
|
||||||
if (err)
|
if (err && (err != -EBADMSG) && (err != -EUCLEAN))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/* Save information about bitflips! */
|
||||||
|
if (err) {
|
||||||
|
if (err == -EBADMSG)
|
||||||
|
ret = err;
|
||||||
|
else if (!ret)
|
||||||
|
ret = err;
|
||||||
|
err = 0;
|
||||||
|
}
|
||||||
|
|
||||||
*retlen += retsize;
|
*retlen += retsize;
|
||||||
len -= size;
|
len -= size;
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
|
@ -92,7 +101,7 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||||
buf += size;
|
buf += size;
|
||||||
from = 0;
|
from = 0;
|
||||||
}
|
}
|
||||||
return err;
|
return err ? err : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
|
@ -1035,7 +1035,10 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
||||||
if (ret)
|
if (ret)
|
||||||
return 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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),
|
ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
|
||||||
512, &retlen, movebuf);
|
512, &retlen, movebuf);
|
||||||
if (ret < 0) {
|
if (ret < 0 && ret != -EUCLEAN) {
|
||||||
ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block])
|
ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block])
|
||||||
+ (block * 512), 512, &retlen,
|
+ (block * 512), 512, &retlen,
|
||||||
movebuf);
|
movebuf);
|
||||||
|
@ -768,7 +768,9 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
|
||||||
} else {
|
} else {
|
||||||
loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
|
loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
|
||||||
size_t retlen;
|
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 -EIO;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -296,10 +296,11 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
|
||||||
/* Do the read... */
|
/* Do the read... */
|
||||||
ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf);
|
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;
|
ret = 0;
|
||||||
}
|
|
||||||
if (ret || retlen != c->wbuf_ofs - start) {
|
if (ret || retlen != c->wbuf_ofs - start) {
|
||||||
printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n");
|
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);
|
down_read(&c->wbuf_sem);
|
||||||
ret = c->mtd->read(c->mtd, ofs, len, retlen, buf);
|
ret = c->mtd->read(c->mtd, ofs, len, retlen, buf);
|
||||||
|
|
||||||
if ( (ret == -EBADMSG) && (*retlen == len) ) {
|
if ( (ret == -EBADMSG || ret == -EUCLEAN) && (*retlen == len) ) {
|
||||||
printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n",
|
if (ret == -EBADMSG)
|
||||||
len, ofs);
|
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 have the raw data without ECC correction in the buffer,
|
||||||
* we are lucky and all data or parts are correct. We check the node.
|
* maybe we are lucky and all data or parts are correct. We
|
||||||
* If data are corrupted node check will sort it out.
|
* check the node. If data are corrupted node check will sort
|
||||||
* We keep this block, it will fail on write or erase and the we
|
* it out. We keep this block, it will fail on write or erase
|
||||||
* mark it bad. Or should we do that now? But we should give him a chance.
|
* and the we mark it bad. Or should we do that now? But we
|
||||||
* Maybe we had a system crash or power loss before the ecc write or
|
* should give him a chance. Maybe we had a system crash or
|
||||||
* a erase was completed.
|
* power loss before the ecc write or a erase was completed.
|
||||||
* So we return success. :)
|
* So we return success. :)
|
||||||
*/
|
*/
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if no writebuffer available or write buffer empty, return */
|
/* 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 */
|
orbf = (c->wbuf_ofs - ofs); /* offset in read buffer */
|
||||||
if (orbf > len) /* is write beyond write buffer ? */
|
if (orbf > len) /* is write beyond write buffer ? */
|
||||||
goto exit;
|
goto exit;
|
||||||
lwbf = len - orbf; /* number of bytes to copy */
|
lwbf = len - orbf; /* number of bytes to copy */
|
||||||
if (lwbf > c->wbuf_len)
|
if (lwbf > c->wbuf_len)
|
||||||
lwbf = c->wbuf_len;
|
lwbf = c->wbuf_len;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue