mtd: spinand: Fix double counting of ECC stats

In the raw NAND world, ECC engines increment ecc_stats and the final
caller is responsible for returning -EBADMSG if the verification
failed.

In the SPI-NAND world it was a bit different until now because there was
only one possible ECC engine: the on-die one. Indeed, the
spinand_mtd_read() call was incrementing the ecc_stats counters
depending on the outcome of spinand_check_ecc_status() directly.

So now let's split the logic like this:
- spinand_check_ecc_status() is specific to the SPI-NAND on-die engine
  and is kept very simple: it just returns the ECC status (bonus point:
  the content of this helper can be overloaded).
- spinand_ondie_ecc_finish_io_req() is the caller of
  spinand_check_ecc_status() and will increment the counters and
  eventually return -EBADMSG.
- spinand_mtd_read() is not tied to the on-die ECC implementation and
  should be able to handle results coming from other ECC engines: it has
  the responsibility of returning the maximum number of bitflips which
  happened during the entire operation as this is the only helper that
  is aware that several pages may be read in a row.

Fixes: 945845b54c ("mtd: spinand: Instantiate a SPI-NAND on-die ECC engine")
Reported-by: YouChing Lin <ycllin@mxic.com.tw>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Tested-by: YouChing Lin <ycllin@mxic.com.tw>
Link: https://lore.kernel.org/linux-mtd/20210527084345.208215-1-miquel.raynal@bootlin.com
This commit is contained in:
Miquel Raynal 2021-05-27 10:43:45 +02:00
parent 0b34e320a4
commit c93081b265

View File

@ -290,6 +290,8 @@ static int spinand_ondie_ecc_finish_io_req(struct nand_device *nand,
{
struct spinand_ondie_ecc_conf *engine_conf = nand->ecc.ctx.priv;
struct spinand_device *spinand = nand_to_spinand(nand);
struct mtd_info *mtd = spinand_to_mtd(spinand);
int ret;
if (req->mode == MTD_OPS_RAW)
return 0;
@ -299,7 +301,13 @@ static int spinand_ondie_ecc_finish_io_req(struct nand_device *nand,
return 0;
/* Finish a page write: check the status, report errors/bitflips */
return spinand_check_ecc_status(spinand, engine_conf->status);
ret = spinand_check_ecc_status(spinand, engine_conf->status);
if (ret == -EBADMSG)
mtd->ecc_stats.failed++;
else if (ret > 0)
mtd->ecc_stats.corrected += ret;
return ret;
}
static struct nand_ecc_engine_ops spinand_ondie_ecc_engine_ops = {
@ -620,13 +628,10 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from,
if (ret < 0 && ret != -EBADMSG)
break;
if (ret == -EBADMSG) {
if (ret == -EBADMSG)
ecc_failed = true;
mtd->ecc_stats.failed++;
} else {
mtd->ecc_stats.corrected += ret;
else
max_bitflips = max_t(unsigned int, max_bitflips, ret);
}
ret = 0;
ops->retlen += iter.req.datalen;