diff mbox

mtd: nandsim: Allow nandsim to re-use persisted data

Message ID alpine.DEB.2.10.1309251518370.22371@cheetah.fastcat.org
State New, archived
Headers show

Commit Message

Matthew Gabeler-Lee Sept. 25, 2013, 7:25 p.m. UTC
mtd: nandsim: Allow nandsim to re-use persisted data

Add optional (default disabled) module parameter to nandsim to instruct it
to assume that the cache file already has valid data in it.  This allows
data written to the cache file (or device) to be re-used across reloads of
the nandsim module, and thus reboots of the host, in much the same way that
block2mtd allows.

Signed-off-by: Matthew Gabeler-Lee <matthew@beechwoods.com>
---
  drivers/mtd/nand/nandsim.c | 42 ++++++++++++++++++++++++++++++++++++++----
  1 file changed, 38 insertions(+), 4 deletions(-)

Comments

Richard Weinberger Nov. 7, 2013, 3:12 p.m. UTC | #1
Hi!

On Wed, Sep 25, 2013 at 9:25 PM, Matthew Gabeler-Lee <fastcat@gmail.com> wrote:
> mtd: nandsim: Allow nandsim to re-use persisted data
>
> Add optional (default disabled) module parameter to nandsim to instruct it
> to assume that the cache file already has valid data in it.  This allows
> data written to the cache file (or device) to be re-used across reloads of
> the nandsim module, and thus reboots of the host, in much the same way that
> block2mtd allows.
>
> Signed-off-by: Matthew Gabeler-Lee <matthew@beechwoods.com>
> ---
>  drivers/mtd/nand/nandsim.c | 42 ++++++++++++++++++++++++++++++++++++++----
>  1 file changed, 38 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
> index bdc1d15..65ac60f 100644
> --- a/drivers/mtd/nand/nandsim.c
> +++ b/drivers/mtd/nand/nandsim.c
> @@ -109,6 +109,7 @@ static unsigned int bitflips = 0;
>  static char *gravepages = NULL;
>  static unsigned int overridesize = 0;
>  static char *cache_file = NULL;
> +static uint cache_file_written = 0;
>  static unsigned int bbt;
>  static unsigned int bch;
>
> @@ -133,6 +134,7 @@ module_param(bitflips,       uint, 0400);
>  module_param(gravepages,     charp, 0400);
>  module_param(overridesize,   uint, 0400);
>  module_param(cache_file,     charp, 0400);
> +module_param(cache_file_written, uint, 0400);
>  module_param(bbt,           uint, 0400);
>  module_param(bch,           uint, 0400);
>
> @@ -166,6 +168,7 @@ MODULE_PARM_DESC(overridesize,   "Specifies the NAND
> Flash size overriding the I
>                                  "The size is specified in erase blocks and
> as the exponent of a power of two"
>                                  " e.g. 5 means a size of 32 erase blocks");
>  MODULE_PARM_DESC(cache_file,     "File to use to cache nand pages instead
> of memory");
> +MODULE_PARM_DESC(cache_file_written, "Assume any pages in cache file have
> already been written, use their existing contents");
>  MODULE_PARM_DESC(bbt,           "0 OOB, 1 BBT with marker in OOB, 2 BBT
> with marker in data area");
>  MODULE_PARM_DESC(bch,           "Enable BCH ecc and set how many bits
> should "
>                                  "be correctable in 512-byte blocks");
> @@ -592,6 +595,9 @@ static int alloc_device(struct nandsim *ns)
>                         err = -ENOMEM;
>                         goto err_close;
>                 }
> +               if (cache_file_written) {
> +                       memset(ns->pages_written, 0xFF, ns->geom.pgnum);
> +               }

Hmm, that's a very ugly hack.
It will slow down nandsim because now it will ask the file cache every time.

If you want persistent storage we'd need a sane file format with header
which contains the page_written bitmask, the nand geometry, etc....

>                 ns->file_buf = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
>                 if (!ns->file_buf) {
>                         NS_ERR("alloc_device: unable to allocate file
> buf\n");
> @@ -1484,7 +1490,17 @@ static void read_page(struct nandsim *ns, int num)
>                         pos = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off;
>                         tx = read_file(ns, ns->cfile, ns->buf.byte, num,
> pos);
>                         if (tx != num) {
> -                               NS_ERR("read_page: read error for page %d
> ret %ld\n", ns->regs.row, (long)tx);
> +                               if (ns->pages_written[ns->regs.row] == 0xFF)
> {
> +                                       NS_WARN("read_page: read error
> assuming unwritten for page %d ret %ld\n", ns->regs.row, (long)tx);
> +                                       ns->pages_written[ns->regs.row] = 0;
> +                                       memset(ns->buf.byte, 0xFF, num);
> +                                       tx = write_file(ns, ns->cfile,
> ns->buf.byte, num, pos);
> +                                       if (tx != num) {
> +                                               NS_ERR("read_page: write
> error for page %d ret %ld\n", ns->regs.row, (long)tx);
> +                                       }
> +                               } else {
> +                                       NS_ERR("read_page: read error for
> page %d ret %ld\n", ns->regs.row, (long)tx);
> +                               }
>                                 return;
>                         }
>                         do_bit_flips(ns, num);
> @@ -1515,11 +1531,22 @@ static void erase_sector(struct nandsim *ns)
>         int i;
>
>         if (ns->cfile) {
> -               for (i = 0; i < ns->geom.pgsec; i++)
> +               loff_t pos;
> +               ssize_t tx;
> + +             memset(ns->file_buf, 0xff, ns->geom.pgszoob);
> +               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);
> +                               pos = (loff_t)(ns->regs.row + i) *
> ns->geom.pgszoob;
> +                               tx = write_file(ns, ns->cfile, ns->file_buf,
> ns->geom.pgszoob, pos);
> +                               if (tx != ns->geom.pgszoob) {
> +                                       NS_ERR("erase_sector: write error
> for page %d ret %ld\n", ns->regs.row, (long)tx);
> +                               }
>                         }
> +                       ns->pages_written[ns->regs.row + i] = 0;
> +               }
>                 return;
>         }
>
> @@ -1558,8 +1585,15 @@ static int prog_page(struct nandsim *ns, int num)
>                         all = 0;
>                         tx = read_file(ns, ns->cfile, pg_off, num, off);
>                         if (tx != num) {
> -                               NS_ERR("prog_page: read error for page %d
> ret %ld\n", ns->regs.row, (long)tx);
> -                               return -1;
> +                               if (ns->pages_written[ns->regs.row] == 0xFF)
> {
> +                                       NS_WARN("prog_page: read error
> assuming unwritten for page %d ret %ld\n", ns->regs.row, (long)tx);
> +                                       ns->pages_written[ns->regs.row] = 0;
> +                                       all = 1;
> +                                       memset(ns->file_buf, 0xff,
> ns->geom.pgszoob);
> +                               } else {
> +                                       NS_ERR("prog_page: read error for
> page %d ret %ld\n", ns->regs.row, (long)tx);
> +                                       return -1;
> +                               }
>                         }
>                 }
>                 for (i = 0; i < num; i++)
> --
> 1.8.4.rc3
>
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
Matthew Gabeler-Lee Nov. 7, 2013, 5:36 p.m. UTC | #2
On 11/07/2013 10:12 AM, Richard Weinberger wrote:
> Hi!

Hi! Thanks for taking a look at this.  PS: First kernel patch I've ever
done...

> On Wed, Sep 25, 2013 at 9:25 PM, Matthew Gabeler-Lee <fastcat@gmail.com> wrote:
>> @@ -592,6 +595,9 @@ static int alloc_device(struct nandsim *ns)
>>                         err = -ENOMEM;
>>                         goto err_close;
>>                 }
>> +               if (cache_file_written) {
>> +                       memset(ns->pages_written, 0xFF, ns->geom.pgnum);
>> +               }
> 
> Hmm, that's a very ugly hack.
> It will slow down nandsim because now it will ask the file cache every time.
> 
> If you want persistent storage we'd need a sane file format with header
> which contains the page_written bitmask, the nand geometry, etc....

Agreed that it's a hack.  My thought process was to a) minimize the
changes and b) have the resulting persisted image be usable to write to
a real flash device, if that was desired.  I'm not sure if the OOB data
would make part b not actually work, though.  I agree that a real file
format (or split files) that tracks metadata would be better overall.
It would beg for some userspace tools to work with it.

As far as performance is concerned, if considering a real file format,
in my testing it appeared that the non-aligned writes resulting from the
OOB bytes may also have been a problem.  AFAICT my application (UBI/FS)
never actually accesses those OOB bytes, so getting creative with where
they are stored in a file format could have some benefits.
diff mbox

Patch

diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
index bdc1d15..65ac60f 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/nandsim.c
@@ -109,6 +109,7 @@  static unsigned int bitflips = 0;
  static char *gravepages = NULL;
  static unsigned int overridesize = 0;
  static char *cache_file = NULL;
+static uint cache_file_written = 0;
  static unsigned int bbt;
  static unsigned int bch;

@@ -133,6 +134,7 @@  module_param(bitflips,       uint, 0400);
  module_param(gravepages,     charp, 0400);
  module_param(overridesize,   uint, 0400);
  module_param(cache_file,     charp, 0400);
+module_param(cache_file_written, uint, 0400);
  module_param(bbt,	     uint, 0400);
  module_param(bch,	     uint, 0400);

@@ -166,6 +168,7 @@  MODULE_PARM_DESC(overridesize,   "Specifies the NAND Flash size overriding the I
  				 "The size is specified in erase blocks and as the exponent of a power of two"
  				 " e.g. 5 means a size of 32 erase blocks");
  MODULE_PARM_DESC(cache_file,     "File to use to cache nand pages instead of memory");
+MODULE_PARM_DESC(cache_file_written, "Assume any pages in cache file have already been written, use their existing contents");
  MODULE_PARM_DESC(bbt,		 "0 OOB, 1 BBT with marker in OOB, 2 BBT with marker in data area");
  MODULE_PARM_DESC(bch,		 "Enable BCH ecc and set how many bits should "
  				 "be correctable in 512-byte blocks");
@@ -592,6 +595,9 @@  static int alloc_device(struct nandsim *ns)
  			err = -ENOMEM;
  			goto err_close;
  		}
+		if (cache_file_written) {
+			memset(ns->pages_written, 0xFF, ns->geom.pgnum);
+		}
  		ns->file_buf = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
  		if (!ns->file_buf) {
  			NS_ERR("alloc_device: unable to allocate file buf\n");
@@ -1484,7 +1490,17 @@  static void read_page(struct nandsim *ns, int num)
  			pos = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off;
  			tx = read_file(ns, ns->cfile, ns->buf.byte, num, pos);
  			if (tx != num) {
-				NS_ERR("read_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx);
+				if (ns->pages_written[ns->regs.row] == 0xFF) {
+					NS_WARN("read_page: read error assuming unwritten for page %d ret %ld\n", ns->regs.row, (long)tx);
+					ns->pages_written[ns->regs.row] = 0;
+					memset(ns->buf.byte, 0xFF, num);
+					tx = write_file(ns, ns->cfile, ns->buf.byte, num, pos);
+					if (tx != num) {
+						NS_ERR("read_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx);
+					}
+				} else {
+					NS_ERR("read_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx);
+				}
  				return;
  			}
  			do_bit_flips(ns, num);
@@ -1515,11 +1531,22 @@  static void erase_sector(struct nandsim *ns)
  	int i;

  	if (ns->cfile) {
-		for (i = 0; i < ns->geom.pgsec; i++)
+		loff_t pos;
+		ssize_t tx;
+ 
+		memset(ns->file_buf, 0xff, ns->geom.pgszoob);
+		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);
+				pos = (loff_t)(ns->regs.row + i) * ns->geom.pgszoob;
+				tx = write_file(ns, ns->cfile, ns->file_buf, ns->geom.pgszoob, pos);
+				if (tx != ns->geom.pgszoob) {
+					NS_ERR("erase_sector: write error for page %d ret %ld\n", ns->regs.row, (long)tx);
+				}
  			}
+			ns->pages_written[ns->regs.row + i] = 0;
+		}
  		return;
  	}

@@ -1558,8 +1585,15 @@  static int prog_page(struct nandsim *ns, int num)
  			all = 0;
  			tx = read_file(ns, ns->cfile, pg_off, num, off);
  			if (tx != num) {
-				NS_ERR("prog_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx);
-				return -1;
+				if (ns->pages_written[ns->regs.row] == 0xFF) {
+					NS_WARN("prog_page: read error assuming unwritten for page %d ret %ld\n", ns->regs.row, (long)tx);
+					ns->pages_written[ns->regs.row] = 0;
+					all = 1;
+					memset(ns->file_buf, 0xff, ns->geom.pgszoob);
+				} else {
+					NS_ERR("prog_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx);
+					return -1;
+				}
  			}
  		}
  		for (i = 0; i < num; i++)