Skip to content

Commit

Permalink
mtd: driver _read() returns max_bitflips; mtd_read() returns -EUCLEAN
Browse files Browse the repository at this point in the history
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.html

Signed-off-by: Mike Dunn <mikedunn@newsguy.com>
Acked-by: Robert Jarzmik <robert.jarzmik@free.fr>
Acked-by: Brian Norris <computersforpeace@gmail.com>
Ivan Djelic <ivan.djelic@parrot.com>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>

Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
  • Loading branch information
Mike Dunn authored and David Woodhouse committed May 14, 2012
1 parent e2788c9 commit edbc454
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 19 deletions.
7 changes: 5 additions & 2 deletions Documentation/ABI/testing/sysfs-class-mtd
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,10 @@ Description:
block degradation, but high enough to avoid the consequences of
a persistent return value of -EUCLEAN on devices where sticky
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
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.
6 changes: 4 additions & 2 deletions drivers/mtd/devices/docg3.c
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
u8 *buf = ops->datbuf;
size_t len, ooblen, nbdata, nboob;
u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1;
int max_bitflips = 0;

if (buf)
len = ops->len;
Expand All @@ -876,7 +877,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
ret = 0;
skip = from % DOC_LAYOUT_PAGE_SIZE;
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,
docg3->reliable);
nbdata = min_t(size_t, len, DOC_LAYOUT_PAGE_SIZE - skip);
Expand Down Expand Up @@ -936,7 +937,8 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
}
if (ret > 0) {
mtd->ecc_stats.corrected += ret;
ret = -EUCLEAN;
max_bitflips = max(max_bitflips, ret);
ret = max_bitflips;
}
}

Expand Down
14 changes: 13 additions & 1 deletion drivers/mtd/mtdcore.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
u_char *buf)
{
int ret_code;
*retlen = 0;
if (from < 0 || from > mtd->size || len > mtd->size - from)
return -EINVAL;
if (!len)
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);

Expand Down
12 changes: 6 additions & 6 deletions drivers/mtd/mtdpart.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
stats = part->master->ecc_stats;
res = part->master->_read(part->master, from + part->offset, len,
retlen, buf);
if (unlikely(res)) {
if (mtd_is_bitflip(res))
mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected;
if (mtd_is_eccerr(res))
mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed;
}
if (unlikely(mtd_is_eccerr(res)))
mtd->ecc_stats.failed +=
part->master->ecc_stats.failed - stats.failed;
else
mtd->ecc_stats.corrected +=
part->master->ecc_stats.corrected - stats.corrected;
return res;
}

Expand Down
4 changes: 2 additions & 2 deletions drivers/mtd/nand/alauda.c
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len,
}
err = 0;
if (corrected)
err = -EUCLEAN;
err = 1; /* return max_bitflips per ecc step */
if (uncorrected)
err = -EBADMSG;
out:
Expand Down Expand Up @@ -446,7 +446,7 @@ static int alauda_read(struct mtd_info *mtd, loff_t from, size_t len,
}
err = 0;
if (corrected)
err = -EUCLEAN;
err = 1; /* return max_bitflips per ecc step */
if (uncorrected)
err = -EBADMSG;
return err;
Expand Down
18 changes: 14 additions & 4 deletions drivers/mtd/nand/nand_base.c
Original file line number Diff line number Diff line change
Expand Up @@ -1486,6 +1486,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
mtd->oobavail : mtd->oobsize;

uint8_t *bufpoi, *oob, *buf;
unsigned int max_bitflips = 0;

stats = mtd->ecc_stats;

Expand Down Expand Up @@ -1513,7 +1514,10 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
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))
ret = chip->ecc.read_page_raw(mtd, chip,
bufpoi, page);
Expand All @@ -1530,15 +1534,19 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
break;
}

max_bitflips = max_t(unsigned int, max_bitflips, ret);

/* Transfer not aligned data */
if (!aligned) {
if (!NAND_SUBPAGE_READ(chip) && !oob &&
!(mtd->ecc_stats.failed - stats.failed) &&
(ops->mode != MTD_OPS_RAW))
(ops->mode != MTD_OPS_RAW)) {
chip->pagebuf = realpage;
else
chip->pagebuf_bitflips = ret;
} else {
/* Invalidate page cache */
chip->pagebuf = -1;
}
memcpy(buf, chip->buffers->databuf + col, bytes);
}

Expand Down Expand Up @@ -1571,6 +1579,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
} else {
memcpy(buf, chip->buffers->databuf + col, bytes);
buf += bytes;
max_bitflips = max_t(unsigned int, max_bitflips,
chip->pagebuf_bitflips);
}

readlen -= bytes;
Expand Down Expand Up @@ -1609,7 +1619,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
if (mtd->ecc_stats.failed - stats.failed)
return -EBADMSG;

return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
return max_bitflips;
}

/**
Expand Down
6 changes: 4 additions & 2 deletions drivers/mtd/onenand/onenand_base.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
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;
}

/**
Expand Down Expand Up @@ -1333,7 +1334,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
if (mtd->ecc_stats.failed - stats.failed)
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;
}

/**
Expand Down
3 changes: 3 additions & 0 deletions include/linux/mtd/nand.h
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,8 @@ struct nand_buffers {
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
* @pagebuf: [INTERN] holds the pagenumber which is currently in
* data_buf.
* @pagebuf_bitflips: [INTERN] holds the bitflip count for the page which is
* currently in data_buf.
* @subpagesize: [INTERN] holds the subpagesize
* @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded),
* non 0 if ONFI supported.
Expand Down Expand Up @@ -519,6 +521,7 @@ struct nand_chip {
uint64_t chipsize;
int pagemask;
int pagebuf;
unsigned int pagebuf_bitflips;
int subpagesize;
uint8_t cellinfo;
int badblockpos;
Expand Down

0 comments on commit edbc454

Please sign in to comment.