mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-27 17:18:59 +00:00
fa4dc86e98
MEMREAD is a new ioctl for MTD character devices that was first included in Linux 6.1. It allows userspace applications to use the Linux kernel's OOB autoplacement mechanism while reading data from NAND devices. The Yafut tool needs this ioctl to do its job. Signed-off-by: Michał Kępień <openwrt@kempniu.pl>
173 lines
5.3 KiB
Diff
173 lines
5.3 KiB
Diff
From 2ed18d818d1f7492172f8dd5904344c7d367e8ed Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= <kernel@kempniu.pl>
|
|
Date: Wed, 29 Jun 2022 14:57:36 +0200
|
|
Subject: [PATCH 3/4] mtd: add ECC error accounting for each read request
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
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: Boris Brezillon <boris.brezillon@collabora.com>
|
|
Signed-off-by: Michał Kępień <kernel@kempniu.pl>
|
|
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
|
|
Link: https://lore.kernel.org/linux-mtd/20220629125737.14418-4-kernel@kempniu.pl
|
|
---
|
|
drivers/mtd/devices/docg3.c | 8 ++++++++
|
|
drivers/mtd/nand/onenand/onenand_base.c | 12 ++++++++++++
|
|
drivers/mtd/nand/raw/nand_base.c | 10 ++++++++++
|
|
drivers/mtd/nand/spi/core.c | 10 ++++++++++
|
|
include/linux/mtd/mtd.h | 2 ++
|
|
5 files changed, 42 insertions(+)
|
|
|
|
--- a/drivers/mtd/devices/docg3.c
|
|
+++ b/drivers/mtd/devices/docg3.c
|
|
@@ -871,6 +871,7 @@ static int doc_read_oob(struct mtd_info
|
|
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
|
|
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
|
|
}
|
|
|
|
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:
|
|
--- a/drivers/mtd/nand/onenand/onenand_base.c
|
|
+++ b/drivers/mtd/nand/onenand/onenand_base.c
|
|
@@ -1440,6 +1440,7 @@ static int onenand_read_oob(struct mtd_i
|
|
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_i
|
|
}
|
|
|
|
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;
|
|
--- a/drivers/mtd/nand/raw/nand_base.c
|
|
+++ b/drivers/mtd/nand/raw/nand_base.c
|
|
@@ -3815,6 +3815,7 @@ static int nand_read_oob(struct mtd_info
|
|
struct mtd_oob_ops *ops)
|
|
{
|
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
|
+ struct mtd_ecc_stats old_stats;
|
|
int ret;
|
|
|
|
ops->retlen = 0;
|
|
@@ -3826,11 +3827,20 @@ static int nand_read_oob(struct mtd_info
|
|
|
|
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;
|
|
}
|
|
--- a/drivers/mtd/nand/spi/core.c
|
|
+++ b/drivers/mtd/nand/spi/core.c
|
|
@@ -629,6 +629,7 @@ static int spinand_mtd_read(struct mtd_i
|
|
{
|
|
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;
|
|
@@ -640,6 +641,8 @@ static int spinand_mtd_read(struct mtd_i
|
|
|
|
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;
|
|
@@ -662,6 +665,13 @@ static int spinand_mtd_read(struct mtd_i
|
|
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)
|
|
--- a/include/linux/mtd/mtd.h
|
|
+++ b/include/linux/mtd/mtd.h
|
|
@@ -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;
|
|
};
|
|
|