From: Boris Brezillon <boris.brezillon@free-electrons.com>
To: Sascha Hauer <s.hauer@pengutronix.de>
Cc: Richard Weinberger <richard@nod.at>,
linux-mtd@lists.infradead.org, kernel@pengutronix.de,
Daniel Walter <dwalter@sigma-star.at>
Subject: Re: Pass -EUCLEN to userspace?
Date: Mon, 25 Apr 2016 16:11:44 +0200 [thread overview]
Message-ID: <20160425161144.0f366c9e@bbrezillon> (raw)
In-Reply-To: <20160425112616.01f9e4e4@bbrezillon>
On Mon, 25 Apr 2016 11:26:16 +0200
Boris Brezillon <boris.brezillon@free-electrons.com> wrote:
> > >
> > > Regarding the maximum number of bitflips per chunk, maybe we can make it
> > > part of the ioctl request instead of saving the statistics at the MTD
> > > level.
> > >
> > > How about creating a new ioctl taking a pointer to this struct as a
> > > parameter:
> > >
> > > struct mtd_extended_read_ops {
> > > /* Existing params */
> > > unsigned int mode;
> > > size_t len;
> > > size_t retlen;
> > > size_t ooblen;
> > > size_t oobretlen;
> > > uint32_t ooboffs;
> > > void *datbuf;
> > > void *oobbuf;
> > >
> > > /*
> > > * Param containing the maximum number of bitflips for this
> > > * read request.
> > > */
> > > unsigned int max_bitflips;
> > > };
> >
> > Not sure how this ioctl exactly should look like, but this would solve
> > the problem.
>
> Let me design a quick prototype, I'll let you follow up with the patch
> submission process...
Below is an untested patch adding a new ioctl returning the ECC/read stats.
Feel free to debug/enhance this implementation and submit it to the MTD
ML.
--->8---
From 2c07a2a015e6c0bdc2f9d91c9a1b971903da0493 Mon Sep 17 00:00:00 2001
From: Boris Brezillon <boris.brezillon@free-electrons.com>
Date: Mon, 25 Apr 2016 16:05:06 +0200
Subject: [PATCH] mtd: add a the MEMREAD ioctl to attach ECC stats to the read
request
TODO: add proper commit message + split changes into several patches.
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
drivers/mtd/devices/docg3.c | 10 +++++
drivers/mtd/mtdchar.c | 76 ++++++++++++++++++++++++++++++++++++++
drivers/mtd/mtdcore.c | 9 +++++
drivers/mtd/nand/nand_base.c | 10 +++++
drivers/mtd/onenand/onenand_base.c | 11 ++++++
include/linux/mtd/mtd.h | 7 ++++
include/uapi/mtd/mtd-abi.h | 50 +++++++++++++++++++++++++
7 files changed, 173 insertions(+)
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index b833e6c..965c97a 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -885,6 +885,8 @@ 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_req_stats *reqstats = ops->stats;
+ struct mtd_ecc_stats stats;
int max_bitflips = 0;
if (buf)
@@ -912,6 +914,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);
+ stats = mtd->ecc_stats;
while (ret >= 0 && (len > 0 || ooblen > 0)) {
calc_block_sector(from - skip, &block0, &block1, &page, &ofs,
docg3->reliable);
@@ -983,6 +986,13 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from,
}
out:
+ if (reqstats) {
+ restats->corrected_bitflips += mtd->ecc_stats.corrected -
+ stats.corrected;
+ restats->uncorrectable_errors += mtd->ecc_stats.failed -
+ stats.failed;
+ }
+
mutex_unlock(&docg3->cascade->lock);
return ret;
err_in_read:
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 2a47a3f..708ecee 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -656,6 +656,75 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd,
return ret;
}
+static int mtdchar_read_ioctl(struct mtd_info *mtd,
+ struct mtd_read_req __user *argp)
+{
+ struct mtd_read_req req;
+ struct mtd_req_stats stats;
+ struct mtd_oob_ops ops = { .stats = &stats };
+ void __user *usr_data, *usr_oob;
+ int ret;
+
+ if (copy_from_user(&req, argp, sizeof(req)))
+ return -EFAULT;
+
+ usr_data = (void __user *)(uintptr_t)req.usr_data;
+ usr_oob = (void __user *)(uintptr_t)req.usr_oob;
+ if ((usr_data && !access_ok(VERIFY_WRITE, usr_data, req.len)) ||
+ (usr_oob && !access_ok(VERIFY_WRITE, usr_oob, req.ooblen)))
+ return -EFAULT;
+
+ if (!mtd->_read_oob)
+ return -EOPNOTSUPP;
+
+ ops.mode = req.mode;
+ ops.len = (size_t)req.len;
+ ops.ooblen = (size_t)req.ooblen;
+ ops.ooboffs = 0;
+
+ if (usr_data) {
+ ops.datbuf = kzalloc(ops.len, GFP_KERNEL);
+ if (!ops.datbuf)
+ return -ENOMEM;
+ }
+
+ if (usr_oob) {
+ ops.oobbuf = kzalloc(ops.ooblen, GFP_KERNEL);
+ if (!ops.oobbuf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+
+ ret = mtd_read_oob(mtd, (loff_t)req.start, &ops);
+ if (ret)
+ goto out;
+
+ if (usr_data && copy_to_user(ops.datbuf, usr_data, ops.retlen)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (usr_oob && copy_to_user(ops.oobbuf, usr_oob, ops.oobretlen)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ req.len = ops.retlen;
+ req.ooblen = ops.oobretlen;
+ req.stats.max_bitflips = stats.max_bitflips;
+ req.stats.corrected_bitflips = stats.corrected_bitflips;
+ req.stats.uncorrectable_errors = stats.uncorrectable_errors;
+ if (copy_to_user(&req, argp, sizeof(req)))
+ ret = -EFAULT;
+
+out:
+ kfree(ops.datbuf);
+ kfree(ops.oobbuf);
+
+ return ret;
+}
+
static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
{
struct mtd_file_info *mfi = file->private_data;
@@ -850,6 +919,13 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
break;
}
+ case MEMREAD:
+ {
+ ret = mtdchar_read_ioctl(mtd,
+ (struct mtd_read_req __user *)arg);
+ break;
+ }
+
case MEMLOCK:
{
struct erase_info_user einfo;
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index e3936b8..ba6488f 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -988,6 +988,11 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
return -EOPNOTSUPP;
ledtrig_mtd_activity();
+
+ /* Make sure all counters are initialized to zero. */
+ if (ops->stats)
+ memset(ops->stats, 0, sizeof(*ops->stats));
+
/*
* In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics
* similar to mtd->_read(), returning a non-negative integer
@@ -999,6 +1004,10 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
return ret_code;
if (mtd->ecc_strength == 0)
return 0; /* device lacks ecc */
+
+ if (ops->stats)
+ ops->stats->max_bitflips = ret_code;
+
return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0;
}
EXPORT_SYMBOL_GPL(mtd_read_oob);
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 7bc37b4..25cab31 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -2162,6 +2162,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
static int nand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
+ struct mtd_req_stats *reqstats = ops->stats;
+ struct mtd_ecc_stats stats;
int ret = -ENOTSUPP;
ops->retlen = 0;
@@ -2185,11 +2187,19 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
goto out;
}
+ stats = mtd->ecc_stats;
if (!ops->datbuf)
ret = nand_do_read_oob(mtd, from, ops);
else
ret = nand_do_read_ops(mtd, from, ops);
+ if (reqstats) {
+ reqstats->uncorrectable_errors += mtd->ecc_stats.failed -
+ stats.failed;
+ reqstats->corrected_bitflips += mtd->ecc_stats.corrected -
+ stats.corrected;
+ }
+
out:
nand_release_device(mtd);
return ret;
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index a4b029a..2f700eb 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -1491,6 +1491,8 @@ 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_req_stats *reqstats = ops->stats;
+ struct mtd_ecc_stats stats;
int ret;
switch (ops->mode) {
@@ -1504,12 +1506,21 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
}
onenand_get_device(mtd, FL_READING);
+ 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 (reqstats) {
+ reqstats->corrected_bitflips += mtd->ecc_stats.corrected -
+ stats.corrected;
+ reqstats->uncorrectable_errors += mtd->ecc_stats.failed -
+ stats.failed;
+ }
onenand_release_device(mtd);
return ret;
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 29a1706..bd5e8a1 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -64,6 +64,12 @@ struct mtd_erase_region_info {
unsigned long *lockmap; /* If keeping bitmap of locks */
};
+struct mtd_req_stats {
+ unsigned int max_bitflips;
+ unsigned int corrected_bitflips;
+ unsigned int uncorrectable_errors;
+};
+
/**
* struct mtd_oob_ops - oob operation operands
* @mode: operation mode
@@ -92,6 +98,7 @@ struct mtd_oob_ops {
uint32_t ooboffs;
uint8_t *datbuf;
uint8_t *oobbuf;
+ struct mtd_req_stats *stats;
};
#define MTD_MAX_OOBFREE_ENTRIES_LARGE 32
diff --git a/include/uapi/mtd/mtd-abi.h b/include/uapi/mtd/mtd-abi.h
index 0ec1da2..a61a042 100644
--- a/include/uapi/mtd/mtd-abi.h
+++ b/include/uapi/mtd/mtd-abi.h
@@ -90,6 +90,52 @@ struct mtd_write_req {
__u8 padding[7];
};
+/**
+ * struct mtd_read_req_stats - statistics attached to a read request
+ *
+ * @max_bitflips: the maximum number of bitflips found in the read portion.
+ * This information can be used to decide when the data stored
+ * on a specific portion should be moved somewhere else to
+ * avoid data loss.
+ * @corrected_bitflips: the number of bitflips corrected during the read
+ * operation
+ * @uncorrectable_errors: the number of uncorrectable errors that happened
+ * during the read operation
+ */
+struct mtd_read_req_stats {
+ __u32 max_bitflips;
+ __u32 corrected_bitflips;
+ __u32 uncorrectable_errors;
+};
+
+/**
+ * struct mtd_read_req - data structure for requesting a write operation
+ *
+ * @start: start address
+ * @len: length of data buffer
+ * @ooblen: length of OOB buffer
+ * @usr_data: user-provided data buffer
+ * @usr_oob: user-provided OOB buffer
+ * @mode: MTD mode (see "MTD operation modes")
+ * @padding: reserved, must be set to 0
+ * @stats: statistics attached to the read request
+ *
+ * This structure supports ioctl(MEMWRITE) operations, allowing data and/or OOB
+ * writes in various modes. To write to OOB-only, set @usr_data == NULL, and to
+ * write data-only, set @usr_oob == NULL. However, setting both @usr_data and
+ * @usr_oob to NULL is not allowed.
+ */
+struct mtd_read_req {
+ __u64 start;
+ __u64 len;
+ __u64 ooblen;
+ __u64 usr_data;
+ __u64 usr_oob;
+ __u8 mode;
+ __u8 padding[7];
+ struct mtd_read_req_stats stats;
+};
+
#define MTD_ABSENT 0
#define MTD_RAM 1
#define MTD_ROM 2
@@ -203,6 +249,10 @@ struct otp_info {
* without OOB, e.g., NOR flash.
*/
#define MEMWRITE _IOWR('M', 24, struct mtd_write_req)
+/*
+ * Same as for MEMWRITE but for read operations.
+ */
+#define MEMREAD _IOWR('M', 25, struct mtd_read_req)
/*
* Obsolete legacy interface. Keep it in order not to break userspace
next prev parent reply other threads:[~2016-04-25 14:12 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-04-20 13:25 Pass -EUCLEN to userspace? Sascha Hauer
2016-04-22 15:24 ` Boris Brezillon
2016-04-22 15:28 ` Boris Brezillon
2016-04-22 15:48 ` Richard Weinberger
2016-04-22 16:11 ` Boris Brezillon
2016-04-22 18:20 ` Richard Weinberger
2016-04-22 18:39 ` Boris Brezillon
2016-04-25 5:28 ` Sascha Hauer
2016-04-25 7:50 ` Boris Brezillon
2016-04-25 8:22 ` Sascha Hauer
2016-04-25 8:40 ` Boris Brezillon
2016-04-25 9:14 ` Sascha Hauer
2016-04-25 9:26 ` Boris Brezillon
2016-04-25 14:11 ` Boris Brezillon [this message]
2016-04-26 7:13 ` Pass -EUCLEAN " Sascha Hauer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20160425161144.0f366c9e@bbrezillon \
--to=boris.brezillon@free-electrons.com \
--cc=dwalter@sigma-star.at \
--cc=kernel@pengutronix.de \
--cc=linux-mtd@lists.infradead.org \
--cc=richard@nod.at \
--cc=s.hauer@pengutronix.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox