* [PATCH 1/5] mtd: nand: add basic stuff to support power-cut emulation
2015-09-25 15:09 [PATCH 0/5] mtd: nand/UBI: add power-cut emulation Boris Brezillon
@ 2015-09-25 15:09 ` Boris Brezillon
2015-09-28 5:41 ` kbuild test robot
2015-09-25 15:09 ` [PATCH 2/5] mtd: nand: return -EROFS in case of " Boris Brezillon
` (3 subsequent siblings)
4 siblings, 1 reply; 9+ messages in thread
From: Boris Brezillon @ 2015-09-25 15:09 UTC (permalink / raw)
To: David Woodhouse, Brian Norris, linux-mtd, Artem Bityutskiy,
Richard Weinberger
Cc: Andrea Scian, Bean Huo 霍斌斌 (beanhuo),
Karl Zhang 张双锣 (karlzhang), Iwo Mergler,
Jeff Lauruhn (jlauruhn), Stefan Roese, linux-kernel,
Boris Brezillon
Add a new status flag to support power-cut emulation. Since real NAND
status is limited to 8bits, we can safely use higher bits for something
else. The NAND_STATUS_POWER_CUT is assigned bit 30 to avoid integer
overflow (not sure this is necessary).
The NAND status is currently retrieved using ->read_byte() after sending
a NAND_CMD_STATUS command, which prevents the nandsim (or any other
implementation that wants to support power-cut emulation) to return a
value with the NAND_STATUS_POWER_CUT flag set.
Add a ->get_status() method returning an int, and provide a default
implementation.
Also note that ->get_status() could be used for other cases than power-cut
emulation. For example, some NAND controllers are able to retrieve the
NAND status by their own after launching a specific command, and this
status is then stored in a specific register, thus preventing the extra
->cmdfunc() + ->read_byte() calls.
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
drivers/mtd/nand/nand_base.c | 17 ++++++++++++++---
include/linux/mtd/nand.h | 4 ++++
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 37c0d9d..a621814 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -478,8 +478,7 @@ static int nand_check_wp(struct mtd_info *mtd)
return 0;
/* Check the WP bit */
- chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
- return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;
+ return (chip->get_status(mtd) & NAND_STATUS_WP) ? 0 : 1;
}
/**
@@ -880,6 +879,15 @@ static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip,
}
}
+static int nand_get_status(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+
+ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+
+ return (int)chip->read_byte(mtd);
+}
+
/**
* nand_wait - [DEFAULT] wait until the command is done
* @mtd: MTD device structure
@@ -922,7 +930,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
}
led_trigger_event(nand_led_trigger, LED_OFF);
- status = (int)chip->read_byte(mtd);
+ status = chip->get_status(mtd);
/* This can happen if in case of timeout or buggy dev_ready */
WARN_ON(!(status & NAND_STATUS_READY));
return status;
@@ -3106,6 +3114,9 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
if (chip->cmdfunc == NULL)
chip->cmdfunc = nand_command;
+ if (!chip->get_status)
+ chip->get_status = nand_get_status;
+
/* check, if a user supplied wait function given */
if (chip->waitfunc == NULL)
chip->waitfunc = nand_wait;
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index c4d8e30..075d7b8 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -107,6 +107,9 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
#define NAND_STATUS_READY 0x40
#define NAND_STATUS_WP 0x80
+/* Status bits reserved for NAND emulation */
+#define NAND_STATUS_POWER_CUT 0x40000000
+
/*
* Constants for ECC_MODES
*/
@@ -661,6 +664,7 @@ struct nand_chip {
int (*init_size)(struct mtd_info *mtd, struct nand_chip *this,
u8 *id_data);
int (*dev_ready)(struct mtd_info *mtd);
+ int (*get_status)(struct mtd_info *mtd);
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,
int page_addr);
int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
--
1.9.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH 1/5] mtd: nand: add basic stuff to support power-cut emulation
2015-09-25 15:09 ` [PATCH 1/5] mtd: nand: add basic stuff to support " Boris Brezillon
@ 2015-09-28 5:41 ` kbuild test robot
0 siblings, 0 replies; 9+ messages in thread
From: kbuild test robot @ 2015-09-28 5:41 UTC (permalink / raw)
To: Boris Brezillon
Cc: kbuild-all, David Woodhouse, Brian Norris, linux-mtd,
Artem Bityutskiy, Richard Weinberger, Andrea Scian,
Bean Huo 霍斌斌 (beanhuo),
Karl Zhang 张双锣 (karlzhang), Iwo Mergler,
Jeff Lauruhn (jlauruhn), Stefan Roese, linux-kernel,
Boris Brezillon
[-- Attachment #1: Type: text/plain, Size: 2425 bytes --]
Hi Boris,
[auto build test results on v4.3-rc2 -- if it's inappropriate base, please ignore]
config: i386-allnoconfig (attached as .config)
reproduce:
git checkout f3924c26b426d59134751b046f403f3f66e16ed7
# save the attached .config to linux build tree
make ARCH=i386
All warnings (new ones prefixed by >>):
>> include/linux/mtd/nand.h:730: warning: No description found for parameter 'get_status'
vim +/get_status +730 include/linux/mtd/nand.h
30631cb8 Alessandro Rubini 2009-09-20 714 flstate_t state;
f75e5097 Thomas Gleixner 2006-05-26 715
f75e5097 Thomas Gleixner 2006-05-26 716 uint8_t *oob_poi;
f75e5097 Thomas Gleixner 2006-05-26 717 struct nand_hw_control *controller;
f75e5097 Thomas Gleixner 2006-05-26 718
f75e5097 Thomas Gleixner 2006-05-26 719 struct nand_ecc_ctrl ecc;
4bf63fcb David Woodhouse 2006-09-25 720 struct nand_buffers *buffers;
f75e5097 Thomas Gleixner 2006-05-26 721 struct nand_hw_control hwcontrol;
f75e5097 Thomas Gleixner 2006-05-26 722
^1da177e Linus Torvalds 2005-04-16 723 uint8_t *bbt;
^1da177e Linus Torvalds 2005-04-16 724 struct nand_bbt_descr *bbt_td;
^1da177e Linus Torvalds 2005-04-16 725 struct nand_bbt_descr *bbt_md;
f75e5097 Thomas Gleixner 2006-05-26 726
^1da177e Linus Torvalds 2005-04-16 727 struct nand_bbt_descr *badblock_pattern;
f75e5097 Thomas Gleixner 2006-05-26 728
^1da177e Linus Torvalds 2005-04-16 729 void *priv;
^1da177e Linus Torvalds 2005-04-16 @730 };
^1da177e Linus Torvalds 2005-04-16 731
^1da177e Linus Torvalds 2005-04-16 732 /*
^1da177e Linus Torvalds 2005-04-16 733 * NAND Flash Manufacturer ID Codes
^1da177e Linus Torvalds 2005-04-16 734 */
^1da177e Linus Torvalds 2005-04-16 735 #define NAND_MFR_TOSHIBA 0x98
^1da177e Linus Torvalds 2005-04-16 736 #define NAND_MFR_SAMSUNG 0xec
^1da177e Linus Torvalds 2005-04-16 737 #define NAND_MFR_FUJITSU 0x04
^1da177e Linus Torvalds 2005-04-16 738 #define NAND_MFR_NATIONAL 0x8f
:::::: The code at line 730 was first introduced by commit
:::::: 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 Linux-2.6.12-rc2
:::::: TO: Linus Torvalds <torvalds@ppc970.osdl.org>
:::::: CC: Linus Torvalds <torvalds@ppc970.osdl.org>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 6062 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 2/5] mtd: nand: return -EROFS in case of power-cut emulation
2015-09-25 15:09 [PATCH 0/5] mtd: nand/UBI: add power-cut emulation Boris Brezillon
2015-09-25 15:09 ` [PATCH 1/5] mtd: nand: add basic stuff to support " Boris Brezillon
@ 2015-09-25 15:09 ` Boris Brezillon
2015-09-25 15:09 ` [PATCH 3/5] UBI: switch the UBI device in read-only mode when mtd returns -EROFS Boris Brezillon
` (2 subsequent siblings)
4 siblings, 0 replies; 9+ messages in thread
From: Boris Brezillon @ 2015-09-25 15:09 UTC (permalink / raw)
To: David Woodhouse, Brian Norris, linux-mtd, Artem Bityutskiy,
Richard Weinberger
Cc: Andrea Scian, Bean Huo 霍斌斌 (beanhuo),
Karl Zhang 张双锣 (karlzhang), Iwo Mergler,
Jeff Lauruhn (jlauruhn), Stefan Roese, linux-kernel,
Boris Brezillon
Now that we are able to emulate power-cuts, we should differentiate the
power-cut emulation and input/output error cases to let the MTD user
decide what it should do.
EROFS was chosen to mimic what's done in UBI when a power-cut emulation
occurs.
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
drivers/mtd/nand/nand_base.c | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index a621814..223aabdb 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1951,6 +1951,9 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
status = chip->waitfunc(mtd, chip);
+ if (unlikely(status & NAND_STATUS_POWER_CUT))
+ return -EROFS;
+
return status & NAND_STATUS_FAIL ? -EIO : 0;
}
@@ -2009,6 +2012,8 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd,
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
status = chip->waitfunc(mtd, chip);
+ if (unlikely(status & NAND_STATUS_POWER_CUT))
+ return -EROFS;
return status & NAND_STATUS_FAIL ? -EIO : 0;
}
@@ -2442,6 +2447,9 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
status = chip->errstat(mtd, chip, FL_WRITING, status,
page);
+ if (unlikely(status & NAND_STATUS_POWER_CUT))
+ return -EROFS;
+
if (status & NAND_STATUS_FAIL)
return -EIO;
} else {
@@ -2868,7 +2876,7 @@ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
int allowbbt)
{
- int page, status, pages_per_block, ret, chipnr;
+ int page, status = 0, pages_per_block, ret, chipnr;
struct nand_chip *chip = mtd->priv;
loff_t len;
@@ -2958,7 +2966,12 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
erase_exit:
- ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
+ if (instr->state == MTD_ERASE_DONE)
+ ret = 0;
+ else if (unlikely(status & NAND_STATUS_POWER_CUT))
+ ret = -EROFS;
+ else
+ ret = -EIO;
/* Deselect and wake up anyone waiting on the device */
chip->select_chip(mtd, -1);
--
1.9.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 3/5] UBI: switch the UBI device in read-only mode when mtd returns -EROFS
2015-09-25 15:09 [PATCH 0/5] mtd: nand/UBI: add power-cut emulation Boris Brezillon
2015-09-25 15:09 ` [PATCH 1/5] mtd: nand: add basic stuff to support " Boris Brezillon
2015-09-25 15:09 ` [PATCH 2/5] mtd: nand: return -EROFS in case of " Boris Brezillon
@ 2015-09-25 15:09 ` Boris Brezillon
2015-09-25 17:34 ` Richard Weinberger
2015-09-25 15:09 ` [PATCH 4/5] mtd: nand: nandsim: implement ->get_status() Boris Brezillon
2015-09-25 15:09 ` [PATCH 5/5] mtd: nand: nandsim: add support for power-cut emulation Boris Brezillon
4 siblings, 1 reply; 9+ messages in thread
From: Boris Brezillon @ 2015-09-25 15:09 UTC (permalink / raw)
To: David Woodhouse, Brian Norris, linux-mtd, Artem Bityutskiy,
Richard Weinberger
Cc: Andrea Scian, Bean Huo 霍斌斌 (beanhuo),
Karl Zhang 张双锣 (karlzhang), Iwo Mergler,
Jeff Lauruhn (jlauruhn), Stefan Roese, linux-kernel,
Boris Brezillon
The NAND layer is now able to forward power-cut emulation errors from
a NAND driver to the MTD user.
Check for this kind of errors in UBI and switch the UBI device in read-only
mode if such an error occurs.
The error will also be forwarded to the UBI user requesting the operation
if any, thus allowing UBIFS to act accordingly.
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
drivers/mtd/ubi/io.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c
index 5bbd1f0..c5ebef7 100644
--- a/drivers/mtd/ubi/io.c
+++ b/drivers/mtd/ubi/io.c
@@ -303,6 +303,8 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
len = ubi->peb_size - offset;
if (len)
err = ubi_self_check_all_ff(ubi, pnum, offset, len);
+ } else if (err == -EROFS) {
+ ubi_ro_mode(ubi);
}
return err;
@@ -355,7 +357,7 @@ retry:
err = mtd_erase(ubi->mtd, &ei);
if (err) {
- if (retries++ < UBI_IO_RETRIES) {
+ if (err != -EROFS && retries++ < UBI_IO_RETRIES) {
ubi_warn(ubi, "error %d while erasing PEB %d, retry",
err, pnum);
yield();
--
1.9.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH 3/5] UBI: switch the UBI device in read-only mode when mtd returns -EROFS
2015-09-25 15:09 ` [PATCH 3/5] UBI: switch the UBI device in read-only mode when mtd returns -EROFS Boris Brezillon
@ 2015-09-25 17:34 ` Richard Weinberger
2015-09-26 8:40 ` Boris Brezillon
0 siblings, 1 reply; 9+ messages in thread
From: Richard Weinberger @ 2015-09-25 17:34 UTC (permalink / raw)
To: Boris Brezillon, David Woodhouse, Brian Norris, linux-mtd,
Artem Bityutskiy
Cc: Andrea Scian, Bean Huo 霍斌斌 (beanhuo),
Karl Zhang 张双锣 (karlzhang), Iwo Mergler,
Jeff Lauruhn (jlauruhn), Stefan Roese, linux-kernel
Am 25.09.2015 um 17:09 schrieb Boris Brezillon:
> The NAND layer is now able to forward power-cut emulation errors from
> a NAND driver to the MTD user.
> Check for this kind of errors in UBI and switch the UBI device in read-only
> mode if such an error occurs.
> The error will also be forwarded to the UBI user requesting the operation
> if any, thus allowing UBIFS to act accordingly.
>
> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> ---
> drivers/mtd/ubi/io.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c
> index 5bbd1f0..c5ebef7 100644
> --- a/drivers/mtd/ubi/io.c
> +++ b/drivers/mtd/ubi/io.c
> @@ -303,6 +303,8 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
> len = ubi->peb_size - offset;
> if (len)
> err = ubi_self_check_all_ff(ubi, pnum, offset, len);
> + } else if (err == -EROFS) {
> + ubi_ro_mode(ubi);
> }
>
> return err;
> @@ -355,7 +357,7 @@ retry:
>
> err = mtd_erase(ubi->mtd, &ei);
> if (err) {
> - if (retries++ < UBI_IO_RETRIES) {
> + if (err != -EROFS && retries++ < UBI_IO_RETRIES) {
Looks good! :)
One small nit, can you please add a comment above both lines that the EROFS checks are
here because of emulators.
Thanks,
//richard
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH 3/5] UBI: switch the UBI device in read-only mode when mtd returns -EROFS
2015-09-25 17:34 ` Richard Weinberger
@ 2015-09-26 8:40 ` Boris Brezillon
0 siblings, 0 replies; 9+ messages in thread
From: Boris Brezillon @ 2015-09-26 8:40 UTC (permalink / raw)
To: Richard Weinberger
Cc: David Woodhouse, Brian Norris, linux-mtd, Artem Bityutskiy,
Andrea Scian, Bean Huo 霍斌斌 (beanhuo),
Karl Zhang 张双锣 (karlzhang), Iwo Mergler,
Jeff Lauruhn (jlauruhn), Stefan Roese, linux-kernel
Hi Richard,
On Fri, 25 Sep 2015 19:34:42 +0200
Richard Weinberger <richard@nod.at> wrote:
> Am 25.09.2015 um 17:09 schrieb Boris Brezillon:
> > The NAND layer is now able to forward power-cut emulation errors from
> > a NAND driver to the MTD user.
> > Check for this kind of errors in UBI and switch the UBI device in read-only
> > mode if such an error occurs.
> > The error will also be forwarded to the UBI user requesting the operation
> > if any, thus allowing UBIFS to act accordingly.
> >
> > Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> > ---
> > drivers/mtd/ubi/io.c | 4 +++-
> > 1 file changed, 3 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c
> > index 5bbd1f0..c5ebef7 100644
> > --- a/drivers/mtd/ubi/io.c
> > +++ b/drivers/mtd/ubi/io.c
> > @@ -303,6 +303,8 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset,
> > len = ubi->peb_size - offset;
> > if (len)
> > err = ubi_self_check_all_ff(ubi, pnum, offset, len);
> > + } else if (err == -EROFS) {
> > + ubi_ro_mode(ubi);
> > }
> >
> > return err;
> > @@ -355,7 +357,7 @@ retry:
> >
> > err = mtd_erase(ubi->mtd, &ei);
> > if (err) {
> > - if (retries++ < UBI_IO_RETRIES) {
> > + if (err != -EROFS && retries++ < UBI_IO_RETRIES) {
>
> Looks good! :)
> One small nit, can you please add a comment above both lines that the EROFS checks are
> here because of emulators.
Sure, I'll add a comment.
Thanks,
Boris
--
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 4/5] mtd: nand: nandsim: implement ->get_status()
2015-09-25 15:09 [PATCH 0/5] mtd: nand/UBI: add power-cut emulation Boris Brezillon
` (2 preceding siblings ...)
2015-09-25 15:09 ` [PATCH 3/5] UBI: switch the UBI device in read-only mode when mtd returns -EROFS Boris Brezillon
@ 2015-09-25 15:09 ` Boris Brezillon
2015-09-25 15:09 ` [PATCH 5/5] mtd: nand: nandsim: add support for power-cut emulation Boris Brezillon
4 siblings, 0 replies; 9+ messages in thread
From: Boris Brezillon @ 2015-09-25 15:09 UTC (permalink / raw)
To: David Woodhouse, Brian Norris, linux-mtd, Artem Bityutskiy,
Richard Weinberger
Cc: Andrea Scian, Bean Huo 霍斌斌 (beanhuo),
Karl Zhang 张双锣 (karlzhang), Iwo Mergler,
Jeff Lauruhn (jlauruhn), Stefan Roese, linux-kernel,
Boris Brezillon
We need to implement a specific ->get_status() function to support
power-cut emulation.
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
drivers/mtd/nand/nandsim.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
index b16d70a..efd8763 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/nandsim.c
@@ -347,7 +347,7 @@ struct nandsim {
/* NAND flash internal registers */
struct {
unsigned command; /* the command register */
- u_char status; /* the status register */
+ int status; /* the status register */
uint row; /* the page number */
uint column; /* the offset within page */
uint count; /* internal counter */
@@ -2221,6 +2221,13 @@ static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
return;
}
+static int ns_nand_get_status(struct mtd_info *mtd)
+{
+ struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
+
+ return ns->regs.status;
+}
+
/*
* Module initialization function
*/
@@ -2256,6 +2263,7 @@ static int __init ns_init_module(void)
chip->write_buf = ns_nand_write_buf;
chip->read_buf = ns_nand_read_buf;
chip->read_word = ns_nand_read_word;
+ chip->get_status = ns_nand_get_status;
chip->ecc.mode = NAND_ECC_SOFT;
/* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */
/* and 'badblocks' parameters to work */
--
1.9.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 5/5] mtd: nand: nandsim: add support for power-cut emulation
2015-09-25 15:09 [PATCH 0/5] mtd: nand/UBI: add power-cut emulation Boris Brezillon
` (3 preceding siblings ...)
2015-09-25 15:09 ` [PATCH 4/5] mtd: nand: nandsim: implement ->get_status() Boris Brezillon
@ 2015-09-25 15:09 ` Boris Brezillon
4 siblings, 0 replies; 9+ messages in thread
From: Boris Brezillon @ 2015-09-25 15:09 UTC (permalink / raw)
To: David Woodhouse, Brian Norris, linux-mtd, Artem Bityutskiy,
Richard Weinberger
Cc: Andrea Scian, Bean Huo 霍斌斌 (beanhuo),
Karl Zhang 张双锣 (karlzhang), Iwo Mergler,
Jeff Lauruhn (jlauruhn), Stefan Roese, linux-kernel,
Boris Brezillon
Add support for power-cut emulation on program and erase operations.
This power-cut emulation is configurable through the nandsim/powercut file
exposed in debugfs and can be triggered on erase or program operations.
The user can also specify a specific block and/or page (relative to a
block) at which he wants the power-cut to occur.
Here are some configuration examples:
disabled => the power-cut emulation is disabled
on-erase => power-cut emulation is active and triggered on all erase
operations
on-erase:12 => power-cut emulation is active and triggered each time block
12 is accessed
on-program => power-cut emulation is active and triggered each time a page
is programmed
on-program::5 => power-cut emulation is active and triggered each time page
5 of any block is programmed
on-program:12:5 => power-cut emulation is active and triggered each time
page 5 of block 12 is programmed
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
drivers/mtd/nand/nandsim.c | 172 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 162 insertions(+), 10 deletions(-)
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
index efd8763..2f3f674 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/nandsim.c
@@ -289,6 +289,7 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should "
struct nandsim_debug_info {
struct dentry *dfs_root;
struct dentry *dfs_wear_report;
+ struct dentry *dfs_powercut;
};
/*
@@ -299,6 +300,24 @@ union ns_mem {
uint16_t *word; /* for 16-bit word access */
};
+enum ns_powercut_status {
+ NS_POWER_CUT_DISABLED,
+ NS_POWER_CUT_ON_ERASE,
+ NS_POWER_CUT_ON_PROGRAM,
+};
+
+struct ns_powercut {
+ enum ns_powercut_status status;
+ int block;
+ int page;
+};
+
+static struct ns_powercut powercut_info = {
+ .status = NS_POWER_CUT_DISABLED,
+ .block = -1,
+ .page = -1,
+};
+
/*
* The structure which describes all the internal simulator data.
*/
@@ -369,6 +388,7 @@ struct nandsim {
void *file_buf;
struct page *held_pages[NS_MAX_HELD_PAGES];
int held_cnt;
+ bool powercut;
struct nandsim_debug_info dbg;
};
@@ -514,6 +534,78 @@ static const struct file_operations dfs_fops = {
.release = single_release,
};
+static ssize_t read_file_powercut(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[32];
+ int size = 0;
+
+ switch (powercut_info.status) {
+ case NS_POWER_CUT_DISABLED:
+ size = snprintf(buf, sizeof(buf), "disabled\n");
+ break;
+ case NS_POWER_CUT_ON_ERASE:
+ size = snprintf(buf, sizeof(buf), "on-erase:%d\n",
+ powercut_info.block);
+ break;
+ case NS_POWER_CUT_ON_PROGRAM:
+ size = snprintf(buf, sizeof(buf), "on-program:%d:%d\n",
+ powercut_info.block, powercut_info.page);
+ break;
+ default:
+ break;
+ }
+
+ return simple_read_from_buffer(user_buf, size, ppos, buf, size);
+}
+
+static ssize_t write_file_powercut(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[32];
+ size_t buf_size;
+ char *ptr = buf;
+ long block = -1, page = -1;
+ int ret;
+
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+
+ if (!strncmp(buf, "disabled", sizeof("disabled") - 1)) {
+ powercut_info.status = NS_POWER_CUT_DISABLED;
+ } else if (!strncmp(buf, "on-erase", sizeof("on-erase") - 1)) {
+ strsep(&ptr, ":");
+ if (ptr)
+ ret = kstrtol(ptr, 0, &block);
+ powercut_info.status = NS_POWER_CUT_ON_ERASE;
+ } else if (!strncmp(buf, "on-program", sizeof("on-program") - 1)) {
+ strsep(&ptr, ":");
+ if (ptr) {
+ ret = kstrtol(ptr, 0, &block);
+ strsep(&ptr, ":");
+ if (ptr)
+ ret = kstrtol(ptr, 0, &page);
+ }
+ powercut_info.status = NS_POWER_CUT_ON_PROGRAM;
+ }
+
+ powercut_info.block = block;
+ powercut_info.page = page;
+
+ return count;
+}
+
+static const struct file_operations powercut_fops = {
+ .read = read_file_powercut,
+ .write = write_file_powercut,
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+
/**
* nandsim_debugfs_create - initialize debugfs
* @dev: nandsim device description object
@@ -546,6 +638,12 @@ static int nandsim_debugfs_create(struct nandsim *dev)
goto out_remove;
dbg->dfs_wear_report = dent;
+ dent = debugfs_create_file("powercut", S_IWUSR | S_IRUSR,
+ dbg->dfs_root, dev, &powercut_fops);
+ if (IS_ERR_OR_NULL(dent))
+ goto out_remove;
+ dbg->dfs_powercut = dent;
+
return 0;
out_remove:
@@ -1206,6 +1304,10 @@ static inline void switch_to_ready_state(struct nandsim *ns, u_char status)
ns->regs.row = 0;
ns->regs.column = 0;
ns->regs.status = status;
+ if (ns->powercut) {
+ ns->powercut = false;
+ ns->regs.status |= NAND_STATUS_POWER_CUT;
+ }
}
/*
@@ -1509,29 +1611,60 @@ static void read_page(struct nandsim *ns, int num)
/*
* Erase all pages in the specified sector.
*/
-static void erase_sector(struct nandsim *ns)
+static int erase_sector(struct nandsim *ns)
{
union ns_mem *mypage;
int i;
+ if (powercut_info.status == NS_POWER_CUT_ON_ERASE) {
+ long block = ns->regs.row / ns->geom.pgsec;
+
+ if (powercut_info.block < 0 ||
+ powercut_info.block == block)
+ ns->powercut = true;
+ }
+
if (ns->cfile) {
- for (i = 0; i < ns->geom.pgsec; i++)
- if (__test_and_clear_bit(ns->regs.row + i,
- ns->pages_written)) {
- NS_DBG("erase_sector: freeing page %d\n", ns->regs.row + i);
+ for (i = 0; i < ns->geom.pgsec; i++) {
+ if (ns->powercut) {
+ loff_t pos = (loff_t)(ns->regs.row + i) *
+ ns->geom.pgszoob;
+
+ /*
+ * Corrupt all pages of the block if a
+ * power-cut occurs in the middle of a
+ * program operation.
+ */
+ prandom_bytes(ns->file_buf, ns->geom.pgszoob);
+ write_file(ns, ns->cfile, ns->file_buf,
+ ns->geom.pgszoob, pos);
+ set_bit(ns->regs.row + i, ns->pages_written);
+ NS_DBG("erase_sector (power-cut emulation): corrupting page %d\n",
+ ns->regs.row + i);
+ } else if (__test_and_clear_bit(ns->regs.row + i,
+ ns->pages_written)) {
+ NS_DBG("erase_sector: freeing page %d\n",
+ ns->regs.row + i);
}
- return;
+ }
+
+ return ns->powercut ? -1 : 0;
}
mypage = NS_GET_PAGE(ns);
for (i = 0; i < ns->geom.pgsec; i++) {
- if (mypage->byte != NULL) {
+ if (ns->powercut) {
+ if (mypage->byte)
+ prandom_bytes(mypage->byte, ns->geom.pgszoob);
+ } else if (mypage->byte != NULL) {
NS_DBG("erase_sector: freeing page %d\n", ns->regs.row+i);
kmem_cache_free(ns->nand_pages_slab, mypage->byte);
mypage->byte = NULL;
}
mypage++;
}
+
+ return ns->powercut ? -1 : 0;
}
/*
@@ -1543,6 +1676,24 @@ static int prog_page(struct nandsim *ns, int num)
union ns_mem *mypage;
u_char *pg_off;
+ if (powercut_info.status == NS_POWER_CUT_ON_PROGRAM) {
+ long block = ns->regs.row / ns->geom.pgsec;
+ long page = ns->regs.row % ns->geom.pgsec;
+
+ if (powercut_info.block < 0 ||
+ powercut_info.block == block) {
+ if (powercut_info.page < 0 ||
+ powercut_info.page == page) {
+ /*
+ * Corrupt the page if a power-cut occurs in
+ * the middle of a program operation.
+ */
+ prandom_bytes(ns->buf.byte, ns->geom.pgszoob);
+ ns->powercut = true;
+ }
+ }
+ }
+
if (ns->cfile) {
loff_t off;
ssize_t tx;
@@ -1579,7 +1730,7 @@ static int prog_page(struct nandsim *ns, int num)
return -1;
}
}
- return 0;
+ return ns->powercut ? -1 : 0;
}
mypage = NS_GET_PAGE(ns);
@@ -1603,7 +1754,7 @@ static int prog_page(struct nandsim *ns, int num)
for (i = 0; i < num; i++)
pg_off[i] &= ns->buf.byte[i];
- return 0;
+ return ns->powercut ? -1 : 0;
}
/*
@@ -1681,7 +1832,8 @@ static int do_state_action(struct nandsim *ns, uint32_t action)
ns->regs.row, NS_RAW_OFFSET(ns));
NS_LOG("erase sector %u\n", erase_block_no);
- erase_sector(ns);
+ if (erase_sector(ns) == -1)
+ return -1;
NS_MDELAY(erase_delay);
--
1.9.1
^ permalink raw reply related [flat|nested] 9+ messages in thread