All of lore.kernel.org
 help / color / mirror / Atom feed
From: Boris Brezillon <boris.brezillon@free-electrons.com>
To: Masahiro Yamada <yamada.masahiro@socionext.com>
Cc: Richard Weinberger <richard@nod.at>,
	Marek Vasut <marek.vasut@gmail.com>,
	Artem Bityutskiy <artem.bityutskiy@linux.intel.com>,
	Cyrille Pitchen <cyrille.pitchen@wedev4u.fr>,
	Linux Kernel Mailing List <linux-kernel@vger.kernel.org>,
	Dinh Nguyen <dinguyen@kernel.org>,
	linux-mtd@lists.infradead.org,
	Masami Hiramatsu <mhiramat@kernel.org>,
	Chuanxiao Dong <chuanxiao.dong@intel.com>,
	Jassi Brar <jaswinder.singh@linaro.org>,
	Brian Norris <computersforpeace@gmail.com>,
	Enrico Jorns <ejo@pengutronix.de>,
	David Woodhouse <dwmw2@infradead.org>,
	Graham Moore <grmoore@opensource.altera.com>
Subject: Re: [PATCH v4 03/23] mtd: nand: add generic helpers to check, match, maximize ECC settings
Date: Wed, 7 Jun 2017 08:16:13 +0200	[thread overview]
Message-ID: <20170607081613.719094ca@bbrezillon> (raw)
In-Reply-To: <CAK7LNASq-tPL+bHHp9OC3GKi5M07F917kXkUT9BcdcA7-Ao2yw@mail.gmail.com>

On Wed, 7 Jun 2017 10:48:33 +0900
Masahiro Yamada <yamada.masahiro@socionext.com> wrote:

> 2017-06-07 6:47 GMT+09:00 Boris Brezillon <boris.brezillon@free-electrons.com>:
> > On Tue,  6 Jun 2017 08:21:42 +0900
> > Masahiro Yamada <yamada.masahiro@socionext.com> wrote:
> >  
> >> Driver are responsible for setting up ECC parameters correctly.
> >> Those include:
> >>   - Check if ECC parameters specified (usually by DT) are valid
> >>   - Meet the chip's ECC requirement
> >>   - Maximize ECC strength if NAND_ECC_MAXIMIZE flag is set
> >>
> >> The logic can be generalized by factoring out common code.
> >>
> >> This commit adds 3 helpers to the NAND framework:
> >> nand_check_ecc_caps - Check if preset step_size and strength are valid
> >> nand_match_ecc_req - Match the chip's requirement
> >> nand_maximize_ecc - Maximize the ECC strength
> >>
> >> To use the helpers above, a driver needs to provide:
> >>   - Data array of supported ECC step size and strength
> >>   - A hook that calculates ECC bytes from the combination of
> >>     step_size and strength.
> >>
> >> By using those helpers, code duplication among drivers will be
> >> reduced.
> >>
> >> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> >> ---
> >>
> >> Changes since the previous version:
> >>
> >>  - Step size info holds an array of associated strengths
> >>  - nand_match_ecc_req() does not take care of the case
> >>    where ecc_size/strength is already set
> >>  - Reflect more comments from Boris
> >>
> >> Previous version:
> >> http://patchwork.ozlabs.org/patch/752107/
> >>
> >>
> >> Changes in v4: None
> >> Changes in v3: None
> >> Changes in v2: None
> >>
> >>  drivers/mtd/nand/nand_base.c | 219 +++++++++++++++++++++++++++++++++++++++++++
> >>  include/linux/mtd/nand.h     |  35 +++++++
> >>  2 files changed, 254 insertions(+)
> >>
> >> diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> >> index bdfa903..f2da4f2 100644
> >> --- a/drivers/mtd/nand/nand_base.c
> >> +++ b/drivers/mtd/nand/nand_base.c
> >> @@ -4509,6 +4509,225 @@ static int nand_set_ecc_soft_ops(struct mtd_info *mtd)
> >>       }
> >>  }
> >>
> >> +/**
> >> + * nand_check_ecc_caps - check the sanity of preset ECC settings
> >> + * @mtd: mtd info structure
> >> + * @chip: nand chip info structure
> >> + * @caps: ECC caps info structure
> >> + *
> >> + * When ECC step size and strength are already set, check if they are supported
> >> + * by the controller and the calculated ECC bytes fit within the chip's OOB.
> >> + * On success, the calculated ECC bytes is set.
> >> + */
> >> +int nand_check_ecc_caps(struct mtd_info *mtd, struct nand_chip *chip,

One more thing I didn't spot in my previous review: please only pass
chip here. mtd can be extracted using nand_to_mtd(chip). This is
applicable to all your helpers.

> >> +                     const struct nand_ecc_caps *caps)
> >> +{
> >> +     const struct nand_ecc_step_info *stepinfo;
> >> +     int avail_oobsize = mtd->oobsize - caps->oob_reserve_bytes;
> >> +     int preset_step = chip->ecc.size;
> >> +     int preset_strength = chip->ecc.strength;
> >> +     int ecc_bytes;
> >> +     int i, j;
> >> +
> >> +     if (WARN_ON(avail_oobsize < 0))
> >> +             return -EINVAL;
> >> +
> >> +     if (!preset_step || !preset_strength)
> >> +             return -ENODATA;
> >> +
> >> +     for (i = 0; i < caps->nstepinfos; i++) {
> >> +             stepinfo = &caps->stepinfos[i];
> >> +
> >> +             if (stepinfo->stepsize != preset_step)
> >> +                     continue;
> >> +
> >> +             for (j = 0; j < stepinfo->nstrengths; j++) {
> >> +                     if (stepinfo->strengths[j] == preset_strength)
> >> +                             goto found;
> >> +             }
> >> +     }
> >> +
> >> +     pr_err("ECC (step, strength) = (%d, %d) not supported on this controller",
> >> +            preset_step, preset_strength);
> >> +
> >> +     return -ENOTSUPP;
> >> +
> >> +found:  
> >
> > I prefer something like:
> >
> >         if (i == caps->nstepinfos) {
> >                 pr_err(...);
> >                 return -ENOTSUPP;
> >         }
> >
> >         ...
> >
> > instead of this 'found' label.  
> 
> 
> I want to bail-out if (step, strength) matches.
> In this version, the for-loop is double-nested by "step" and "strength".
> In C language, it is not possible to bail-out from multi-nested loop
> with a single "break;" statement.  That is why I used "found:" label to do it.

You're right. I didn't pay attention to the nested for loop.

> 
> In my first version where there was a single for-loop,
> I did not use the goto label.
> http://patchwork.ozlabs.org/patch/752107/
> 
> Do you have any suggestion for cleaner implementation?
> 
> 

You can do:

	nsteps = mtd->writesize / preset_step;

	for (i = 0; i < caps->nstepinfos; i++) {
		stepinfo = &caps->stepinfos[i];

		if (stepinfo->stepsize != preset_step)
			continue;

		for (j = 0; j < stepinfo->nstrengths; j++) {
			if (stepinfo->strengths[j] != preset_strength)
				continue;

			ecc_bytes = caps->calc_ecc_bytes(preset_step,
							 preset_strength);
			if (WARN_ON_ONCE(ecc_bytes < 0))
				return ecc_bytes;

			if (ecc_bytes * nsteps > avail_oobsize) {
				pr_err("ECC (step, strength) = (%d, %d) does not fit in OOB",
				       preset_step, preset_strength);
				return -ENOSPC;
			}

			chip->ecc.bytes = ecc_bytes;

			return 0;
		}
	}

	pr_err("ECC (step, strength) = (%d, %d) not supported on this controller",
	       preset_step, preset_strength);

	return -ENOTSUPP;

 
> 
> >> +     ecc_bytes = caps->calc_ecc_bytes(preset_step, preset_strength);
> >> +     if (WARN_ON_ONCE(ecc_bytes < 0))
> >> +             return ecc_bytes;
> >> +
> >> +     if (ecc_bytes * mtd->writesize / preset_step > avail_oobsize) {
> >> +             pr_err("ECC (step, strength) = (%d, %d) does not fit in OOB",
> >> +                    preset_step, preset_strength);
> >> +             return -ENOSPC;
> >> +     }
> >> +
> >> +     chip->ecc.bytes = ecc_bytes;
> >> +
> >> +     return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(nand_check_ecc_caps);
> >> +
> >> +/**
> >> + * nand_match_ecc_req - meet the chip's requirement with least ECC bytes
> >> + * @mtd: mtd info structure
> >> + * @chip: nand chip info structure
> >> + * @caps: ECC engine caps info structure
> >> + *
> >> + * If a chip's ECC requirement is provided, try to meet it with the least
> >> + * number of ECC bytes (i.e. with the largest number of OOB-free bytes).
> >> + * On success, the chosen ECC settings are set.
> >> + */
> >> +int nand_match_ecc_req(struct mtd_info *mtd, struct nand_chip *chip,
> >> +                    const struct nand_ecc_caps *caps)
> >> +{
> >> +     const struct nand_ecc_step_info *stepinfo;
> >> +     int avail_oobsize = mtd->oobsize - caps->oob_reserve_bytes;
> >> +     int req_step = chip->ecc_step_ds;
> >> +     int req_strength = chip->ecc_strength_ds;
> >> +     int req_corr, step_size, strength, steps, ecc_bytes, ecc_bytes_total;
> >> +     int best_step, best_strength, best_ecc_bytes;
> >> +     int best_ecc_bytes_total = INT_MAX;  
> >
> > Just nitpicking, but why not -1 instead of INT_MAX?  
> 
> Because nand_match_ecc_req() prefers a smaller ecc_bytes_total.
> So I chose the largest int number as an init value.
> If we started from -1, the following if-conditional would have no effect.

Okay, that's a good reason :-).

> 
>      /*
>       * We assume the best is to meet the chip's requrement
>       * with the least number of ECC bytes.
>       */
>      if (ecc_bytes_total < best_ecc_bytes_total) {
>                 best_ecc_bytes_total = ecc_bytes_total;
>                 best_step = step_size;
>                 best_strength = strength;
>                 best_ecc_bytes = ecc_bytes;
>      }
> 
> 
> 
> 
> 
> 
> >> +     int i, j;
> >> +
> >> +     if (WARN_ON(avail_oobsize < 0))
> >> +             return -EINVAL;
> >> +
> >> +     /* No information provided by the NAND chip */
> >> +     if (!req_step || !req_strength)
> >> +             return -ENOTSUPP;
> >> +
> >> +     /* number of correctable bits the chip requires in a page */
> >> +     req_corr = mtd->writesize / req_step * req_strength;
> >> +
> >> +     for (i = 0; i < caps->nstepinfos; i++) {
> >> +             stepinfo = &caps->stepinfos[i];
> >> +             step_size = stepinfo->stepsize;
> >> +
> >> +             for (j = 0; j < stepinfo->nstrengths; j++) {
> >> +                     strength = stepinfo->strengths[j];
> >> +
> >> +                     /*
> >> +                      * If both step size and strength are smaller than the
> >> +                      * chip's requirement, it is not easy to compare the
> >> +                      * resulted reliability.
> >> +                      */
> >> +                     if (step_size < req_step && strength < req_strength)
> >> +                             continue;
> >> +
> >> +                     if (mtd->writesize % step_size)
> >> +                             continue;
> >> +
> >> +                     steps = mtd->writesize / step_size;
> >> +
> >> +                     ecc_bytes = caps->calc_ecc_bytes(step_size, strength);
> >> +                     if (WARN_ON_ONCE(ecc_bytes < 0))
> >> +                             continue;
> >> +                     ecc_bytes_total = ecc_bytes * steps;
> >> +
> >> +                     if (ecc_bytes_total > avail_oobsize ||
> >> +                         strength * steps < req_corr)
> >> +                             continue;
> >> +
> >> +                     /*
> >> +                      * We assume the best is to meet the chip's requrement
> >> +                      * with the least number of ECC bytes.
> >> +                      */
> >> +                     if (ecc_bytes_total < best_ecc_bytes_total) {
> >> +                             best_ecc_bytes_total = ecc_bytes_total;
> >> +                             best_step = step_size;
> >> +                             best_strength = strength;
> >> +                             best_ecc_bytes = ecc_bytes;
> >> +                     }
> >> +             }
> >> +     }
> >> +
> >> +     if (best_ecc_bytes_total == INT_MAX)
> >> +             return -ENOTSUPP;
> >> +
> >> +     chip->ecc.size = best_step;
> >> +     chip->ecc.strength = best_strength;
> >> +     chip->ecc.bytes = best_ecc_bytes;
> >> +
> >> +     return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(nand_match_ecc_req);
> >> +

[...]

> >> +
> >> +/**
> >> + * struct nand_ecc_caps - capability of ECC engine
> >> + * @stepinfos: array of ECC step information
> >> + * @nstepinfos: number of ECC step information
> >> + * @calc_ecc_bytes: driver's hook to calculate ECC bytes per step
> >> + * @oob_reserve_bytes: number of bytes in OOB that must be reserved
> >> + */
> >> +struct nand_ecc_caps {
> >> +     const struct nand_ecc_step_info *stepinfos;
> >> +     int nstepinfos;
> >> +     int (*calc_ecc_bytes)(int step_size, int strength);
> >> +     int oob_reserve_bytes;  
> >
> > Why is this needed? I thought we agreed on passing oobavail as an
> > argument to these helper funcs. If a driver needs to reserve a few OOB
> > bytes, then doing mtd->oobsize - rsvd_bytes is not such a big deal.  
> 
> 
> oobavail is really chip-dependent, so I agreed
> that it can not be included in the caps struct.
> 
> Then, I flipped the logic.
> The number of reserved bytes will be more chip-independent.
> But, oob_reserve_bytes may not necessarily a fixed value.
> 
> I can pass oobavail as a function argument.

Yes please.

Thanks,

Boris

  reply	other threads:[~2017-06-07  6:16 UTC|newest]

Thread overview: 44+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-06-05 23:21 [PATCH v4 00/23] mtd: nand: denali: Denali NAND IP patch bomb Masahiro Yamada
2017-06-05 23:21 ` Masahiro Yamada
2017-06-05 23:21 ` [PATCH v4 01/23] mtd: nand: denali_dt: clean up resource ioremap Masahiro Yamada
2017-06-05 23:21 ` [PATCH v4 02/23] mtd: nand: denali: use BIT() and GENMASK() for register macros Masahiro Yamada
2017-06-05 23:21 ` [PATCH v4 03/23] mtd: nand: add generic helpers to check, match, maximize ECC settings Masahiro Yamada
2017-06-06 21:47   ` Boris Brezillon
2017-06-07  1:48     ` Masahiro Yamada
2017-06-07  6:16       ` Boris Brezillon [this message]
2017-06-05 23:21 ` [PATCH v4 04/23] mtd: nand: denali: avoid hard-coding ECC step, strength, bytes Masahiro Yamada
2017-06-05 23:21   ` Masahiro Yamada
2017-06-06 22:01   ` Boris Brezillon
2017-06-06 22:01     ` Boris Brezillon
2017-06-07  3:09     ` Masahiro Yamada
2017-06-07  3:09       ` Masahiro Yamada
2017-06-07  7:02       ` Boris Brezillon
2017-06-07  7:02         ` Boris Brezillon
2017-06-07  7:21         ` Masahiro Yamada
2017-06-07  7:21           ` Masahiro Yamada
2017-06-07  7:45           ` Boris Brezillon
2017-06-07  7:45             ` Boris Brezillon
2017-06-05 23:21 ` [PATCH v4 05/23] mtd: nand: denali: remove Toshiba and Hynix specific fixup code Masahiro Yamada
2017-06-05 23:21 ` [PATCH v4 06/23] mtd: nand: denali_dt: add compatible strings for UniPhier SoC variants Masahiro Yamada
2017-06-05 23:21 ` [PATCH v4 07/23] mtd: nand: denali: set NAND_ECC_CUSTOM_PAGE_ACCESS Masahiro Yamada
2017-06-05 23:21 ` [PATCH v4 08/23] mtd: nand: denali: do not propagate NAND_STATUS_FAIL to waitfunc() Masahiro Yamada
2017-06-05 23:21 ` [PATCH v4 09/23] mtd: nand: denali: remove unneeded find_valid_banks() Masahiro Yamada
2017-06-05 23:21 ` [PATCH v4 10/23] mtd: nand: denali: handle timing parameters by setup_data_interface() Masahiro Yamada
2017-06-05 23:21 ` [PATCH v4 11/23] mtd: nand: denali: rework interrupt handling Masahiro Yamada
2017-06-05 23:21 ` [PATCH v4 12/23] mtd: nand: denali: fix NAND_CMD_STATUS handling Masahiro Yamada
2017-06-05 23:21 ` [PATCH v4 13/23] mtd: nand: denali: fix NAND_CMD_PARAM handling Masahiro Yamada
2017-06-05 23:21 ` [PATCH v4 14/23] mtd: nand: denali: switch over to cmd_ctrl instead of cmdfunc Masahiro Yamada
2017-06-05 23:21 ` [PATCH v4 15/23] mtd: nand: denali: fix bank reset function to detect the number of chips Masahiro Yamada
2017-06-05 23:21 ` [PATCH v4 16/23] mtd: nand: denali: use interrupt instead of polling for bank reset Masahiro Yamada
2017-06-05 23:21 ` [PATCH v4 17/23] mtd: nand: denali: propagate page to helpers via function argument Masahiro Yamada
2017-06-05 23:21 ` [PATCH v4 18/23] mtd: nand: denali: merge struct nand_buf into struct denali_nand_info Masahiro Yamada
2017-06-05 23:21 ` [PATCH v4 19/23] mtd: nand: denali: use flag instead of register macro for direction Masahiro Yamada
2017-06-05 23:21 ` [PATCH v4 20/23] mtd: nand: denali: fix raw and oob accessors for syndrome page layout Masahiro Yamada
2017-06-05 23:22 ` [PATCH v4 21/23] mtd: nand: denali: skip driver internal bounce buffer when possible Masahiro Yamada
2017-06-05 23:22 ` [PATCH v4 22/23] mtd: nand: denali: use non-managed kmalloc() for DMA buffer Masahiro Yamada
2017-06-05 23:22 ` [PATCH v4 23/23] mtd: nand: denali: enable bad block table scan Masahiro Yamada
2017-06-06 22:09 ` [PATCH v4 00/23] mtd: nand: denali: Denali NAND IP patch bomb Boris Brezillon
2017-06-06 22:09   ` Boris Brezillon
2017-06-07  1:21   ` Masahiro Yamada
2017-06-07  7:24     ` Boris Brezillon
2017-06-07  7:24       ` Boris Brezillon

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=20170607081613.719094ca@bbrezillon \
    --to=boris.brezillon@free-electrons.com \
    --cc=artem.bityutskiy@linux.intel.com \
    --cc=chuanxiao.dong@intel.com \
    --cc=computersforpeace@gmail.com \
    --cc=cyrille.pitchen@wedev4u.fr \
    --cc=dinguyen@kernel.org \
    --cc=dwmw2@infradead.org \
    --cc=ejo@pengutronix.de \
    --cc=grmoore@opensource.altera.com \
    --cc=jaswinder.singh@linaro.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mtd@lists.infradead.org \
    --cc=marek.vasut@gmail.com \
    --cc=mhiramat@kernel.org \
    --cc=richard@nod.at \
    --cc=yamada.masahiro@socionext.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.