Commit edbc4540 authored by Mike Dunn's avatar Mike Dunn Committed by David Woodhouse

mtd: driver _read() returns max_bitflips; mtd_read() returns -EUCLEAN

The drivers' _read() method, absent an error, returns a non-negative integer
indicating the maximum number of bit errors that were corrected in any one
region comprising an ecc step.  MTD returns -EUCLEAN if this is >=
bitflip_threshold, 0 otherwise.  If bitflip_threshold is zero, the comparison is
not made since these devices lack ECC and always return zero in the non-error
case (thanks Brian)¹.  Note that this is a subtle change to the driver
interface.

This and the preceding patches in this set were tested with ubi on top of the
nandsim and docg4 devices, running the ubi test io_basic from mtd-utils.

¹ http://lists.infradead.org/pipermail/linux-mtd/2012-March/040468.htmlSigned-off-by: default avatarMike Dunn <mikedunn@newsguy.com>
Acked-by: default avatarRobert Jarzmik <robert.jarzmik@free.fr>
Acked-by: default avatarBrian Norris <computersforpeace@gmail.com>
Ivan Djelic <ivan.djelic@parrot.com>
Signed-off-by: default avatarArtem Bityutskiy <artem.bityutskiy@linux.intel.com>
Signed-off-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent e2788c98
...@@ -167,7 +167,10 @@ Description: ...@@ -167,7 +167,10 @@ Description:
block degradation, but high enough to avoid the consequences of block degradation, but high enough to avoid the consequences of
a persistent return value of -EUCLEAN on devices where sticky a persistent return value of -EUCLEAN on devices where sticky
bitflips occur. Note that if bitflip_threshold exceeds bitflips occur. Note that if bitflip_threshold exceeds
ecc_strength, -EUCLEAN is never returned by the read functions. ecc_strength, -EUCLEAN is never returned by mtd_read().
Conversely, if bitflip_threshold is zero, -EUCLEAN is always
returned, absent a hard error.
This is generally applicable only to NAND flash devices with ECC This is generally applicable only to NAND flash devices with ECC
capability. It is ignored on devices lacking ECC capability. capability. It is ignored on devices lacking ECC capability;
i.e., devices for which ecc_strength is zero.
...@@ -850,6 +850,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, ...@@ -850,6 +850,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
u8 *buf = ops->datbuf; u8 *buf = ops->datbuf;
size_t len, ooblen, nbdata, nboob; size_t len, ooblen, nbdata, nboob;
u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1; u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1;
int max_bitflips = 0;
if (buf) if (buf)
len = ops->len; len = ops->len;
...@@ -876,7 +877,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, ...@@ -876,7 +877,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
ret = 0; ret = 0;
skip = from % DOC_LAYOUT_PAGE_SIZE; skip = from % DOC_LAYOUT_PAGE_SIZE;
mutex_lock(&docg3->cascade->lock); mutex_lock(&docg3->cascade->lock);
while (!ret && (len > 0 || ooblen > 0)) { while (ret >= 0 && (len > 0 || ooblen > 0)) {
calc_block_sector(from - skip, &block0, &block1, &page, &ofs, calc_block_sector(from - skip, &block0, &block1, &page, &ofs,
docg3->reliable); docg3->reliable);
nbdata = min_t(size_t, len, DOC_LAYOUT_PAGE_SIZE - skip); nbdata = min_t(size_t, len, DOC_LAYOUT_PAGE_SIZE - skip);
...@@ -936,7 +937,8 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, ...@@ -936,7 +937,8 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
} }
if (ret > 0) { if (ret > 0) {
mtd->ecc_stats.corrected += ret; mtd->ecc_stats.corrected += ret;
ret = -EUCLEAN; max_bitflips = max(max_bitflips, ret);
ret = max_bitflips;
} }
} }
......
...@@ -800,12 +800,24 @@ EXPORT_SYMBOL_GPL(mtd_get_unmapped_area); ...@@ -800,12 +800,24 @@ EXPORT_SYMBOL_GPL(mtd_get_unmapped_area);
int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
u_char *buf) u_char *buf)
{ {
int ret_code;
*retlen = 0; *retlen = 0;
if (from < 0 || from > mtd->size || len > mtd->size - from) if (from < 0 || from > mtd->size || len > mtd->size - from)
return -EINVAL; return -EINVAL;
if (!len) if (!len)
return 0; return 0;
return mtd->_read(mtd, from, len, retlen, buf);
/*
* In the absence of an error, drivers return a non-negative integer
* representing the maximum number of bitflips that were corrected on
* any one ecc region (if applicable; zero otherwise).
*/
ret_code = mtd->_read(mtd, from, len, retlen, buf);
if (unlikely(ret_code < 0))
return ret_code;
if (mtd->ecc_strength == 0)
return 0; /* device lacks ecc */
return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0;
} }
EXPORT_SYMBOL_GPL(mtd_read); EXPORT_SYMBOL_GPL(mtd_read);
......
...@@ -67,12 +67,12 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -67,12 +67,12 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
stats = part->master->ecc_stats; stats = part->master->ecc_stats;
res = part->master->_read(part->master, from + part->offset, len, res = part->master->_read(part->master, from + part->offset, len,
retlen, buf); retlen, buf);
if (unlikely(res)) { if (unlikely(mtd_is_eccerr(res)))
if (mtd_is_bitflip(res)) mtd->ecc_stats.failed +=
mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected; part->master->ecc_stats.failed - stats.failed;
if (mtd_is_eccerr(res)) else
mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed; mtd->ecc_stats.corrected +=
} part->master->ecc_stats.corrected - stats.corrected;
return res; return res;
} }
......
...@@ -414,7 +414,7 @@ static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -414,7 +414,7 @@ static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len,
} }
err = 0; err = 0;
if (corrected) if (corrected)
err = -EUCLEAN; err = 1; /* return max_bitflips per ecc step */
if (uncorrected) if (uncorrected)
err = -EBADMSG; err = -EBADMSG;
out: out:
...@@ -446,7 +446,7 @@ static int alauda_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -446,7 +446,7 @@ static int alauda_read(struct mtd_info *mtd, loff_t from, size_t len,
} }
err = 0; err = 0;
if (corrected) if (corrected)
err = -EUCLEAN; err = 1; /* return max_bitflips per ecc step */
if (uncorrected) if (uncorrected)
err = -EBADMSG; err = -EBADMSG;
return err; return err;
......
...@@ -1486,6 +1486,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, ...@@ -1486,6 +1486,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
mtd->oobavail : mtd->oobsize; mtd->oobavail : mtd->oobsize;
uint8_t *bufpoi, *oob, *buf; uint8_t *bufpoi, *oob, *buf;
unsigned int max_bitflips = 0;
stats = mtd->ecc_stats; stats = mtd->ecc_stats;
...@@ -1513,7 +1514,10 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, ...@@ -1513,7 +1514,10 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
sndcmd = 0; sndcmd = 0;
} }
/* Now read the page into the buffer */ /*
* Now read the page into the buffer. Absent an error,
* the read methods return max bitflips per ecc step.
*/
if (unlikely(ops->mode == MTD_OPS_RAW)) if (unlikely(ops->mode == MTD_OPS_RAW))
ret = chip->ecc.read_page_raw(mtd, chip, ret = chip->ecc.read_page_raw(mtd, chip,
bufpoi, page); bufpoi, page);
...@@ -1530,15 +1534,19 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, ...@@ -1530,15 +1534,19 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
break; break;
} }
max_bitflips = max_t(unsigned int, max_bitflips, ret);
/* Transfer not aligned data */ /* Transfer not aligned data */
if (!aligned) { if (!aligned) {
if (!NAND_SUBPAGE_READ(chip) && !oob && if (!NAND_SUBPAGE_READ(chip) && !oob &&
!(mtd->ecc_stats.failed - stats.failed) && !(mtd->ecc_stats.failed - stats.failed) &&
(ops->mode != MTD_OPS_RAW)) (ops->mode != MTD_OPS_RAW)) {
chip->pagebuf = realpage; chip->pagebuf = realpage;
else chip->pagebuf_bitflips = ret;
} else {
/* Invalidate page cache */ /* Invalidate page cache */
chip->pagebuf = -1; chip->pagebuf = -1;
}
memcpy(buf, chip->buffers->databuf + col, bytes); memcpy(buf, chip->buffers->databuf + col, bytes);
} }
...@@ -1571,6 +1579,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, ...@@ -1571,6 +1579,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
} else { } else {
memcpy(buf, chip->buffers->databuf + col, bytes); memcpy(buf, chip->buffers->databuf + col, bytes);
buf += bytes; buf += bytes;
max_bitflips = max_t(unsigned int, max_bitflips,
chip->pagebuf_bitflips);
} }
readlen -= bytes; readlen -= bytes;
...@@ -1609,7 +1619,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, ...@@ -1609,7 +1619,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
if (mtd->ecc_stats.failed - stats.failed) if (mtd->ecc_stats.failed - stats.failed)
return -EBADMSG; return -EBADMSG;
return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; return max_bitflips;
} }
/** /**
......
...@@ -1201,7 +1201,8 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from, ...@@ -1201,7 +1201,8 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from,
if (mtd->ecc_stats.failed - stats.failed) if (mtd->ecc_stats.failed - stats.failed)
return -EBADMSG; return -EBADMSG;
return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; /* return max bitflips per ecc step; ONENANDs correct 1 bit only */
return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0;
} }
/** /**
...@@ -1333,7 +1334,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, ...@@ -1333,7 +1334,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
if (mtd->ecc_stats.failed - stats.failed) if (mtd->ecc_stats.failed - stats.failed)
return -EBADMSG; return -EBADMSG;
return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; /* return max bitflips per ecc step; ONENANDs correct 1 bit only */
return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0;
} }
/** /**
......
...@@ -459,6 +459,8 @@ struct nand_buffers { ...@@ -459,6 +459,8 @@ struct nand_buffers {
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
* @pagebuf: [INTERN] holds the pagenumber which is currently in * @pagebuf: [INTERN] holds the pagenumber which is currently in
* data_buf. * data_buf.
* @pagebuf_bitflips: [INTERN] holds the bitflip count for the page which is
* currently in data_buf.
* @subpagesize: [INTERN] holds the subpagesize * @subpagesize: [INTERN] holds the subpagesize
* @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded), * @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded),
* non 0 if ONFI supported. * non 0 if ONFI supported.
...@@ -519,6 +521,7 @@ struct nand_chip { ...@@ -519,6 +521,7 @@ struct nand_chip {
uint64_t chipsize; uint64_t chipsize;
int pagemask; int pagemask;
int pagebuf; int pagebuf;
unsigned int pagebuf_bitflips;
int subpagesize; int subpagesize;
uint8_t cellinfo; uint8_t cellinfo;
int badblockpos; int badblockpos;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment