Commit 7bea6056 authored by Michał Kępień's avatar Michał Kępień Committed by Miquel Raynal
Browse files

mtd: add ECC error accounting for each read request



Extend struct mtd_req_stats with two new fields holding the number of
corrected bitflips and uncorrectable errors detected during a read
operation.  This is a prerequisite for ultimately passing those counters
to user space, where they can be useful to applications for making
better-informed choices about moving data around.

Unlike 'max_bitflips' (which is set - in a common code path - to the
return value of a function called while the MTD device's mutex is held),
these counters have to be maintained in each MTD driver which defines
the '_read_oob' callback because the statistics need to be calculated
while the MTD device's mutex is held.

Suggested-by: default avatarBoris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: default avatarMichał Kępień <kernel@kempniu.pl>
Signed-off-by: default avatarMiquel Raynal <miquel.raynal@bootlin.com>
Link: https://lore.kernel.org/linux-mtd/20220629125737.14418-4-kernel@kempniu.pl
parent 745df179
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -871,6 +871,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;
	struct mtd_ecc_stats old_stats;
	int max_bitflips = 0;

	if (buf)
@@ -895,6 +896,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);
	old_stats = mtd->ecc_stats;
	while (ret >= 0 && (len > 0 || ooblen > 0)) {
		calc_block_sector(from - skip, &block0, &block1, &page, &ofs,
			docg3->reliable);
@@ -966,6 +968,12 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
	}

out:
	if (ops->stats) {
		ops->stats->uncorrectable_errors +=
			mtd->ecc_stats.failed - old_stats.failed;
		ops->stats->corrected_bitflips +=
			mtd->ecc_stats.corrected - old_stats.corrected;
	}
	mutex_unlock(&docg3->cascade->lock);
	return ret;
err_in_read:
+12 −0
Original line number Diff line number Diff line
@@ -1440,6 +1440,7 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
			    struct mtd_oob_ops *ops)
{
	struct onenand_chip *this = mtd->priv;
	struct mtd_ecc_stats old_stats;
	int ret;

	switch (ops->mode) {
@@ -1453,12 +1454,23 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
	}

	onenand_get_device(mtd, FL_READING);

	old_stats = mtd->ecc_stats;

	if (ops->datbuf)
		ret = ONENAND_IS_4KB_PAGE(this) ?
			onenand_mlc_read_ops_nolock(mtd, from, ops) :
			onenand_read_ops_nolock(mtd, from, ops);
	else
		ret = onenand_read_oob_nolock(mtd, from, ops);

	if (ops->stats) {
		ops->stats->uncorrectable_errors +=
			mtd->ecc_stats.failed - old_stats.failed;
		ops->stats->corrected_bitflips +=
			mtd->ecc_stats.corrected - old_stats.corrected;
	}

	onenand_release_device(mtd);

	return ret;
+10 −0
Original line number Diff line number Diff line
@@ -3818,6 +3818,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
			 struct mtd_oob_ops *ops)
{
	struct nand_chip *chip = mtd_to_nand(mtd);
	struct mtd_ecc_stats old_stats;
	int ret;

	ops->retlen = 0;
@@ -3829,11 +3830,20 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,

	nand_get_device(chip);

	old_stats = mtd->ecc_stats;

	if (!ops->datbuf)
		ret = nand_do_read_oob(chip, from, ops);
	else
		ret = nand_do_read_ops(chip, from, ops);

	if (ops->stats) {
		ops->stats->uncorrectable_errors +=
			mtd->ecc_stats.failed - old_stats.failed;
		ops->stats->corrected_bitflips +=
			mtd->ecc_stats.corrected - old_stats.corrected;
	}

	nand_release_device(chip);
	return ret;
}
+10 −0
Original line number Diff line number Diff line
@@ -635,6 +635,7 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from,
{
	struct spinand_device *spinand = mtd_to_spinand(mtd);
	struct nand_device *nand = mtd_to_nanddev(mtd);
	struct mtd_ecc_stats old_stats;
	unsigned int max_bitflips = 0;
	struct nand_io_iter iter;
	bool disable_ecc = false;
@@ -646,6 +647,8 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from,

	mutex_lock(&spinand->lock);

	old_stats = mtd->ecc_stats;

	nanddev_io_for_each_page(nand, NAND_PAGE_READ, from, ops, &iter) {
		if (disable_ecc)
			iter.req.mode = MTD_OPS_RAW;
@@ -668,6 +671,13 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from,
		ops->oobretlen += iter.req.ooblen;
	}

	if (ops->stats) {
		ops->stats->uncorrectable_errors +=
			mtd->ecc_stats.failed - old_stats.failed;
		ops->stats->corrected_bitflips +=
			mtd->ecc_stats.corrected - old_stats.corrected;
	}

	mutex_unlock(&spinand->lock);

	if (ecc_failed && !ret)
+2 −0
Original line number Diff line number Diff line
@@ -41,6 +41,8 @@ struct mtd_erase_region_info {
};

struct mtd_req_stats {
	unsigned int uncorrectable_errors;
	unsigned int corrected_bitflips;
	unsigned int max_bitflips;
};