diff mbox series

[v4,03/23] mtd: nand: add generic helpers to check, match, maximize ECC settings

Message ID 1496704922-12261-4-git-send-email-yamada.masahiro@socionext.com
State New
Headers show
Series mtd: nand: denali: Denali NAND IP patch bomb | expand

Commit Message

Masahiro Yamada June 5, 2017, 11:21 p.m. UTC
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(+)

-- 
2.7.4

Comments

Boris Brezillon June 6, 2017, 9:47 p.m. UTC | #1
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,

> +			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.

> +	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?

> +	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);

> +

> +/**

> + * nand_maximize_ecc - choose the max ECC strength available

> + * @mtd: mtd info structure

> + * @chip: nand chip info structure

> + * @caps: ECC engine caps info structure

> + *

> + * Choose the max ECC strength that is supported on the controller, and can fit

> + * within the chip's OOB.  On success, the chosen ECC settings are set.

> + */

> +int nand_maximize_ecc(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 step_size, strength, steps, ecc_bytes, corr;

> +	int best_corr = 0;

> +	int best_step = 0;

> +	int best_strength, best_ecc_bytes;

> +	int i, j;

> +

> +	if (WARN_ON(avail_oobsize < 0))

> +		return -EINVAL;

> +

> +	for (i = 0; i < caps->nstepinfos; i++) {

> +		stepinfo = &caps->stepinfos[i];

> +		step_size = stepinfo->stepsize;

> +

> +


Extra blank line here.

> +		/* If chip->ecc.size is already set, respect it */

> +		if (chip->ecc.size && step_size != chip->ecc.size)

> +			continue;

> +

> +		for (j = 0; j < stepinfo->nstrengths; j++) {

> +			strength = stepinfo->strengths[j];

> +

> +			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;

> +

> +			if (ecc_bytes * steps > avail_oobsize)

> +				continue;

> +

> +			corr = strength * steps;

> +

> +			/*

> +			 * If the number of correctable bits is the same,

> +			 * bigger step_size has more reliability.

> +			 */

> +			if (corr > best_corr ||

> +			    (corr == best_corr && step_size > best_step)) {

> +				best_corr = corr;

> +				best_step = step_size;

> +				best_strength = strength;

> +				best_ecc_bytes = ecc_bytes;

> +			}

> +		}

> +	}

> +

> +	if (!best_corr)

> +		return -ENOTSUPP;

> +

> +	chip->ecc.size = best_step;

> +	chip->ecc.strength = best_strength;

> +	chip->ecc.bytes = best_ecc_bytes;

> +

> +	return 0;

> +}

> +EXPORT_SYMBOL_GPL(nand_maximize_ecc);

> +

>  /*

>   * Check if the chip configuration meet the datasheet requirements.

>  

> diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h

> index 8f67b15..97ccb76 100644

> --- a/include/linux/mtd/nand.h

> +++ b/include/linux/mtd/nand.h

> @@ -477,6 +477,32 @@ static inline void nand_hw_control_init(struct nand_hw_control *nfc)

>  }

>  

>  /**

> + * struct nand_ecc_step_info - ECC step information of ECC engine

> + * @stepsize: data bytes per ECC step

> + * @strengths: array of supported strengths

> + * @nstrengths: number of supported strengths

> + */

> +struct nand_ecc_step_info {

> +	int stepsize;

> +	const int *strengths;

> +	int nstrengths;

> +};

> +

> +/**

> + * 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.

> +};

> +

> +/**

>   * struct nand_ecc_ctrl - Control structure for ECC

>   * @mode:	ECC mode

>   * @algo:	ECC algorithm

> @@ -1244,6 +1270,15 @@ int nand_check_erased_ecc_chunk(void *data, int datalen,

>  				void *extraoob, int extraooblen,

>  				int threshold);

>  

> +int nand_check_ecc_caps(struct mtd_info *mtd, struct nand_chip *chip,

> +			const struct nand_ecc_caps *caps);

> +

> +int nand_match_ecc_req(struct mtd_info *mtd, struct nand_chip *chip,

> +		       const struct nand_ecc_caps *caps);

> +

> +int nand_maximize_ecc(struct mtd_info *mtd, struct nand_chip *chip,

> +		      const struct nand_ecc_caps *caps);

> +

>  /* Default write_oob implementation */

>  int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page);

>
Masahiro Yamada June 7, 2017, 1:48 a.m. UTC | #2
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,

>> +                     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.

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?






>> +     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.

     /*
      * 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);

>> +

>> +/**

>> + * nand_maximize_ecc - choose the max ECC strength available

>> + * @mtd: mtd info structure

>> + * @chip: nand chip info structure

>> + * @caps: ECC engine caps info structure

>> + *

>> + * Choose the max ECC strength that is supported on the controller, and can fit

>> + * within the chip's OOB.  On success, the chosen ECC settings are set.

>> + */

>> +int nand_maximize_ecc(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 step_size, strength, steps, ecc_bytes, corr;

>> +     int best_corr = 0;

>> +     int best_step = 0;

>> +     int best_strength, best_ecc_bytes;

>> +     int i, j;

>> +

>> +     if (WARN_ON(avail_oobsize < 0))

>> +             return -EINVAL;

>> +

>> +     for (i = 0; i < caps->nstepinfos; i++) {

>> +             stepinfo = &caps->stepinfos[i];

>> +             step_size = stepinfo->stepsize;

>> +

>> +

>

> Extra blank line here.


OK. I will remove it.



>> +

>> +/**

>> + * 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.


-- 
Best Regards
Masahiro Yamada
Boris Brezillon June 7, 2017, 6:16 a.m. UTC | #3
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
diff mbox series

Patch

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,
+			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:
+	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;
+	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);
+
+/**
+ * nand_maximize_ecc - choose the max ECC strength available
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @caps: ECC engine caps info structure
+ *
+ * Choose the max ECC strength that is supported on the controller, and can fit
+ * within the chip's OOB.  On success, the chosen ECC settings are set.
+ */
+int nand_maximize_ecc(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 step_size, strength, steps, ecc_bytes, corr;
+	int best_corr = 0;
+	int best_step = 0;
+	int best_strength, best_ecc_bytes;
+	int i, j;
+
+	if (WARN_ON(avail_oobsize < 0))
+		return -EINVAL;
+
+	for (i = 0; i < caps->nstepinfos; i++) {
+		stepinfo = &caps->stepinfos[i];
+		step_size = stepinfo->stepsize;
+
+
+		/* If chip->ecc.size is already set, respect it */
+		if (chip->ecc.size && step_size != chip->ecc.size)
+			continue;
+
+		for (j = 0; j < stepinfo->nstrengths; j++) {
+			strength = stepinfo->strengths[j];
+
+			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;
+
+			if (ecc_bytes * steps > avail_oobsize)
+				continue;
+
+			corr = strength * steps;
+
+			/*
+			 * If the number of correctable bits is the same,
+			 * bigger step_size has more reliability.
+			 */
+			if (corr > best_corr ||
+			    (corr == best_corr && step_size > best_step)) {
+				best_corr = corr;
+				best_step = step_size;
+				best_strength = strength;
+				best_ecc_bytes = ecc_bytes;
+			}
+		}
+	}
+
+	if (!best_corr)
+		return -ENOTSUPP;
+
+	chip->ecc.size = best_step;
+	chip->ecc.strength = best_strength;
+	chip->ecc.bytes = best_ecc_bytes;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(nand_maximize_ecc);
+
 /*
  * Check if the chip configuration meet the datasheet requirements.
 
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 8f67b15..97ccb76 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -477,6 +477,32 @@  static inline void nand_hw_control_init(struct nand_hw_control *nfc)
 }
 
 /**
+ * struct nand_ecc_step_info - ECC step information of ECC engine
+ * @stepsize: data bytes per ECC step
+ * @strengths: array of supported strengths
+ * @nstrengths: number of supported strengths
+ */
+struct nand_ecc_step_info {
+	int stepsize;
+	const int *strengths;
+	int nstrengths;
+};
+
+/**
+ * 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;
+};
+
+/**
  * struct nand_ecc_ctrl - Control structure for ECC
  * @mode:	ECC mode
  * @algo:	ECC algorithm
@@ -1244,6 +1270,15 @@  int nand_check_erased_ecc_chunk(void *data, int datalen,
 				void *extraoob, int extraooblen,
 				int threshold);
 
+int nand_check_ecc_caps(struct mtd_info *mtd, struct nand_chip *chip,
+			const struct nand_ecc_caps *caps);
+
+int nand_match_ecc_req(struct mtd_info *mtd, struct nand_chip *chip,
+		       const struct nand_ecc_caps *caps);
+
+int nand_maximize_ecc(struct mtd_info *mtd, struct nand_chip *chip,
+		      const struct nand_ecc_caps *caps);
+
 /* Default write_oob implementation */
 int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page);