[U-Boot] [PATCH v2 1/4] nand: Extend nand_(read|write)_skip_bad with *actual and limit parameters
Scott Wood
scottwood at freescale.com
Wed Feb 27 03:08:46 CET 2013
On 02/26/2013 09:56:08 AM, Tom Rini wrote:
> We make these two functions take a size_t pointer to how much space
> was used on NAND to read or write the buffer (when reads/writes
> happen)
> so that bad blocks can be accounted for. We also make them take an
> loff_t limit on how much data can be read or written. This means that
> we can now catch the case of when writing to a partition would exceed
> the partition size due to bad blocks. To do this we also need to make
> check_skip_len care about total actual size used rather than
> block_size
> chunks used. All callers of nand_(read|write)_skip_bad are adjusted
> to
> call these with the most sensible limits available.
>
> The changes were started by Pantelis and finished by Tom.
>
> Cc: Scott Wood <scottwood at freescale.com>
> Signed-off-by: Pantelis Antoniou <panto at antoniou-consulting.com>
> Signed-off-by: Tom Rini <trini at ti.com>
> ---
> common/cmd_nand.c | 61
> +++++++++++++++++++++--------------------
> common/env_nand.c | 5 ++--
> drivers/mtd/nand/nand_util.c | 62
> +++++++++++++++++++++++++++++++-----------
> include/nand.h | 4 +--
> 4 files changed, 82 insertions(+), 50 deletions(-)
>
> diff --git a/common/cmd_nand.c b/common/cmd_nand.c
> index 1568594..e091e02 100644
> --- a/common/cmd_nand.c
> +++ b/common/cmd_nand.c
> @@ -137,7 +137,8 @@ static inline int str2long(const char *p, ulong
> *num)
> return *p != '\0' && *endptr == '\0';
> }
>
> -static int get_part(const char *partname, int *idx, loff_t *off,
> loff_t *size)
> +static int get_part(const char *partname, int *idx, loff_t *off,
> loff_t *size,
> + loff_t *maxsize)
> {
> #ifdef CONFIG_CMD_MTDPARTS
> struct mtd_device *dev;
> @@ -160,6 +161,7 @@ static int get_part(const char *partname, int
> *idx, loff_t *off, loff_t *size)
>
> *off = part->offset;
> *size = part->size;
> + *maxsize = part->offset + part->size;
> *idx = dev->id->num;
The name "maxsize" suggests that it's a size, not a position.
>
> ret = set_dev(*idx);
> @@ -173,10 +175,11 @@ static int get_part(const char *partname, int
> *idx, loff_t *off, loff_t *size)
> #endif
> }
>
> -static int arg_off(const char *arg, int *idx, loff_t *off, loff_t
> *maxsize)
> +static int arg_off(const char *arg, int *idx, loff_t *off, loff_t
> *size,
> + loff_t *maxsize)
> {
> if (!str2off(arg, off))
> - return get_part(arg, idx, off, maxsize);
> + return get_part(arg, idx, off, size, maxsize);
>
> if (*off >= nand_info[*idx].size) {
> puts("Offset exceeds device limit\n");
...and in the get_part case arg-off is still treating maxsize as a size.
> @@ -605,7 +601,7 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag,
> int argc, char * const argv[])
> }
>
> if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) ==
> 0) {
> - size_t rwsize;
> + size_t rwsize, actual;
> ulong pagecount = 1;
> int read;
> int raw;
> @@ -625,7 +621,7 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag,
> int argc, char * const argv[])
> if (s && !strcmp(s, ".raw")) {
> raw = 1;
>
> - if (arg_off(argv[3], &dev, &off, &size))
> + if (arg_off(argv[3], &dev, &off, &size,
> &maxsize))
> return 1;
>
> if (argc > 4 && !str2long(argv[4], &pagecount))
> {
> @@ -641,7 +637,7 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag,
> int argc, char * const argv[])
> rwsize = pagecount * (nand->writesize +
> nand->oobsize);
> } else {
> if (arg_off_size(argc - 3, argv + 3, &dev,
> - &off, &size) != 0)
> + &off, &size, &maxsize)
> != 0)
> return 1;
>
> rwsize = size;
> @@ -651,9 +647,11 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag,
> int argc, char * const argv[])
> !strcmp(s, ".e") || !strcmp(s, ".i")) {
> if (read)
> ret = nand_read_skip_bad(nand, off,
> &rwsize,
> + &actual,
> maxsize,
> (u_char
> *)addr);
> else
> ret = nand_write_skip_bad(nand, off,
> &rwsize,
> + &actual,
> maxsize,
> (u_char
> *)addr, 0);
> #ifdef CONFIG_CMD_NAND_TRIMFFS
> } else if (!strcmp(s, ".trimffs")) {
> @@ -661,8 +659,8 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag,
> int argc, char * const argv[])
> printf("Unknown nand command suffix
> '%s'\n", s);
> return 1;
> }
> - ret = nand_write_skip_bad(nand, off, &rwsize,
> - (u_char *)addr,
> + ret = nand_write_skip_bad(nand, off, &rwsize,
> &actual,
> + maxsize, (u_char *)addr,
> WITH_DROP_FFS);
Do we care about actual here? Let the skip_bad functions accept NULL
if the caller doesn't care.
> diff --git a/drivers/mtd/nand/nand_util.c
> b/drivers/mtd/nand/nand_util.c
> index 2ba0c5e..5ed5b1d 100644
> --- a/drivers/mtd/nand/nand_util.c
> +++ b/drivers/mtd/nand/nand_util.c
> @@ -397,34 +397,38 @@ int nand_unlock(struct mtd_info *mtd, loff_t
> start, size_t length,
> * blocks fits into device
> *
> * @param nand NAND device
> - * @param offset offset in flash
> + * @param offsetp offset in flash (on exit offset where it's ending)
> * @param length image length
> * @return 0 if the image fits and there are no bad blocks
> * 1 if the image fits, but there are bad blocks
> * -1 if the image does not fit
> */
> -static int check_skip_len(nand_info_t *nand, loff_t offset, size_t
> length)
> +static int check_skip_len(nand_info_t *nand, loff_t *offset, size_t
> length)
Comment changed "offset" to "offsetp" but code did not.
Can we use a different parameter to return the end offset (or actual
size)? That way we don't need the tmp_offset stuff,
and there should be fewer changes to this function.
> {
> - size_t len_excl_bad = 0;
> int ret = 0;
>
> - while (len_excl_bad < length) {
> + while (length > 0) {
> size_t block_len, block_off;
> loff_t block_start;
>
> - if (offset >= nand->size)
> + if (*offset >= nand->size)
> return -1;
>
> - block_start = offset & ~(loff_t)(nand->erasesize - 1);
> - block_off = offset & (nand->erasesize - 1);
> + block_start = *offset & ~(loff_t)(nand->erasesize - 1);
> + block_off = *offset & (nand->erasesize - 1);
> block_len = nand->erasesize - block_off;
>
> - if (!nand_block_isbad(nand, block_start))
> - len_excl_bad += block_len;
> - else
> + if (!nand_block_isbad(nand, block_start)) {
> + if (block_len > length) {
> + /* Final chunk is smaller than block. */
> + *offset += length;
> + return ret;
> + } else
> + length -= block_len;
> + } else
> ret = 1;
Traditionally U-Boot style has been to use braces on both sides of an
if/else if one side needs them.
> - offset += block_len;
> + *offset += block_len;
> }
>
> return ret;
> @@ -459,22 +463,26 @@ static size_t drop_ffs(const nand_info_t *nand,
> const u_char *buf,
> * Write image to NAND flash.
> * Blocks that are marked bad are skipped and the is written to the
> next
> * block instead as long as the image is short enough to fit even
> after
> - * skipping the bad blocks.
> + * skipping the bad blocks. Note that the actual size needed may
> exceed
> + * both the length and available NAND due to bad blocks.
If that happens, then the function returns failure. Are the contents
of "actual" well-defined when the function returns failure?
> *
> * @param nand NAND device
> * @param offset offset in flash
> * @param length buffer length
> + * @param actual size required to write length worth of buffer
> + * @param lim end location of where data in the
> buffer may be written.
> * @param buffer buffer to read from
> * @param flags flags modifying the behaviour of the
> write to NAND
> * @return 0 in case of success
> */
Please note which pointer parameters are in/out versus out-only.
> int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t
> *length,
> - u_char *buffer, int flags)
> + size_t *actual, loff_t lim, u_char *buffer, int
> flags)
> {
> int rval = 0, blocksize;
> size_t left_to_write = *length;
> u_char *p_buffer = buffer;
> int need_skip;
> + loff_t tmp_offset;
>
> #ifdef CONFIG_CMD_NAND_YAFFS
> if (flags & WITH_YAFFS_OOB) {
> @@ -509,16 +517,25 @@ int nand_write_skip_bad(nand_info_t *nand,
> loff_t offset, size_t *length,
> if ((offset & (nand->writesize - 1)) != 0) {
> printf("Attempt to write non page-aligned data\n");
> *length = 0;
> + *actual = 0;
> return -EINVAL;
> }
>
> - need_skip = check_skip_len(nand, offset, *length);
> + tmp_offset = offset;
> + need_skip = check_skip_len(nand, &tmp_offset, *length);
> + *actual = tmp_offset;
More size/offset mismatch with actual. Docs above say it's a size.
-Scott
More information about the U-Boot
mailing list