[RESEND PATCH v2 2/3] cmd: mtd: add torture command support

Heinrich Schuchardt xypron.glpk at gmx.de
Tue Sep 10 04:49:52 CEST 2024


On 9/10/24 00:21, Mikhail Kshevetskiy wrote:
> Some nand flashes (like spi-nand one) are registered with mtd
> subsystem only, thus nand command can't be used to work with
> such flashes. As result some functionality is missing.
>
> This patch implements 'nand torture' functionality for mtd command.

Can we call this 'nand writetest' and the test in patch 3/3 'nand readtest'?

When I read 'torture' I thought of a test running until the NAND flash
is worn out.

Please, provide a man-page for the command in doc/usage/cmd/nand.rst

Best regards

Heinrich

>
> Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
> ---
>   cmd/Kconfig |   6 ++
>   cmd/mtd.c   | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>   2 files changed, 202 insertions(+)
>
> diff --git a/cmd/Kconfig b/cmd/Kconfig
> index 53ebafcb610..698e0e697f2 100644
> --- a/cmd/Kconfig
> +++ b/cmd/Kconfig
> @@ -1439,6 +1439,12 @@ config CMD_MTD_MARKBAD
>   	help
>   	  MTD markbad command support.
>
> +config CMD_MTD_TORTURE
> +	bool "mtd torture"
> +	depends on CMD_MTD
> +	help
> +	  MTD torture command support.
> +
>   config CMD_MUX
>   	bool "mux"
>   	depends on MULTIPLEXER
> diff --git a/cmd/mtd.c b/cmd/mtd.c
> index 45084d39d3a..69ddfe1d9c6 100644
> --- a/cmd/mtd.c
> +++ b/cmd/mtd.c
> @@ -18,6 +18,7 @@
>   #include <mtd.h>
>   #include <dm/devres.h>
>   #include <linux/err.h>
> +#include <memalign.h>
>
>   #include <linux/ctype.h>
>
> @@ -742,6 +743,194 @@ out_put_mtd:
>   }
>   #endif
>
> +#ifdef CONFIG_CMD_MTD_TORTURE
> +/**
> + * nand_check_pattern:
> + *
> + * Check if buffer contains only a certain byte pattern.
> + *
> + * @param buf buffer to check
> + * @param patt the pattern to check
> + * @param size buffer size in bytes
> + * Return: 1 if there are only patt bytes in buf
> + *         0 if something else was found
> + */
> +static int nand_check_pattern(const u_char *buf, u_char patt, int size)
> +{
> +	int i;
> +
> +	for (i = 0; i < size; i++)
> +		if (buf[i] != patt)
> +			return 0;
> +	return 1;
> +}
> +
> +/**
> + * nand_torture:
> + *
> + * Torture a block of NAND flash.
> + * This is useful to determine if a block that caused a write error is still
> + * good or should be marked as bad.
> + *
> + * @param mtd nand mtd instance
> + * @param offset offset in flash
> + * Return: 0 if the block is still good
> + */
> +static int nand_torture(struct mtd_info *mtd, loff_t offset)
> +{
> +	u_char patterns[] = {0xa5, 0x5a, 0x00};
> +	struct erase_info instr = {
> +		.mtd = mtd,
> +		.addr = offset,
> +		.len = mtd->erasesize,
> +	};
> +	size_t retlen;
> +	int err, ret = -1, i, patt_count;
> +	u_char *buf;
> +
> +	if ((offset & (mtd->erasesize - 1)) != 0) {
> +		puts("Attempt to torture a block at a non block-aligned offset\n");
> +		return -EINVAL;
> +	}
> +
> +	if (offset + mtd->erasesize > mtd->size) {
> +		puts("Attempt to torture a block outside the flash area\n");
> +		return -EINVAL;
> +	}
> +
> +	patt_count = ARRAY_SIZE(patterns);
> +
> +	buf = malloc_cache_aligned(mtd->erasesize);
> +	if (buf == NULL) {
> +		puts("Out of memory for erase block buffer\n");
> +		return -ENOMEM;
> +	}
> +
> +	for (i = 0; i < patt_count; i++) {
> +		err = mtd_erase(mtd, &instr);
> +		if (err) {
> +			printf("%s: erase() failed for block at 0x%llx: %d\n",
> +			       mtd->name, instr.addr, err);
> +			goto out;
> +		}
> +
> +		/* Make sure the block contains only 0xff bytes */
> +		err = mtd_read(mtd, offset, mtd->erasesize, &retlen, buf);
> +		if ((err && err != -EUCLEAN) || retlen != mtd->erasesize) {
> +			printf("%s: read() failed for block at 0x%llx: %d\n",
> +			       mtd->name, instr.addr, err);
> +			goto out;
> +		}
> +
> +		err = nand_check_pattern(buf, 0xff, mtd->erasesize);
> +		if (!err) {
> +			printf("Erased block at 0x%llx, but a non-0xff byte was found\n",
> +			       offset);
> +			ret = -EIO;
> +			goto out;
> +		}
> +
> +		/* Write a pattern and check it */
> +		memset(buf, patterns[i], mtd->erasesize);
> +		err = mtd_write(mtd, offset, mtd->erasesize, &retlen, buf);
> +		if (err || retlen != mtd->erasesize) {
> +			printf("%s: write() failed for block at 0x%llx: %d\n",
> +			       mtd->name, instr.addr, err);
> +			goto out;
> +		}
> +
> +		err = mtd_read(mtd, offset, mtd->erasesize, &retlen, buf);
> +		if ((err && err != -EUCLEAN) || retlen != mtd->erasesize) {
> +			printf("%s: read() failed for block at 0x%llx: %d\n",
> +			       mtd->name, instr.addr, err);
> +			goto out;
> +		}
> +
> +		err = nand_check_pattern(buf, patterns[i], mtd->erasesize);
> +		if (!err) {
> +			printf("Pattern 0x%.2x checking failed for block at "
> +			       "0x%llx\n", patterns[i], offset);
> +			ret = -EIO;
> +			goto out;
> +		}
> +	}
> +
> +	ret = 0;
> +
> +out:
> +	free(buf);
> +	return ret;
> +}
> +
> +static int do_mtd_torture(struct cmd_tbl *cmdtp, int flag, int argc,
> +			  char *const argv[])
> +{
> +	struct mtd_info *mtd;
> +	loff_t off, len;
> +	int ret = 0;
> +	unsigned int failed = 0, passed = 0;
> +
> +	if (argc < 2)
> +		return CMD_RET_USAGE;
> +
> +	mtd = get_mtd_by_name(argv[1]);
> +	if (IS_ERR_OR_NULL(mtd))
> +		return CMD_RET_FAILURE;
> +
> +	if (!mtd_can_have_bb(mtd)) {
> +		printf("Only NAND-based devices can be tortured\n");
> +		goto out_put_mtd;
> +	}
> +
> +	argc -= 2;
> +	argv += 2;
> +
> +	off = argc > 0 ? hextoul(argv[0], NULL) : 0;
> +	len = argc > 1 ? hextoul(argv[1], NULL) : mtd->size;
> +
> +	if (!mtd_is_aligned_with_block_size(mtd, off)) {
> +		printf("Offset not aligned with a block (0x%x)\n",
> +		       mtd->erasesize);
> +		ret = CMD_RET_FAILURE;
> +		goto out_put_mtd;
> +	}
> +
> +	if (!mtd_is_aligned_with_block_size(mtd, len)) {
> +		printf("Size not a multiple of a block (0x%x)\n",
> +		       mtd->erasesize);
> +		ret = CMD_RET_FAILURE;
> +		goto out_put_mtd;
> +	}
> +
> +	printf("\nNAND torture: device '%s' offset 0x%llx size 0x%llx (block size 0x%x)\n",
> +	       mtd->name, off, len, mtd->erasesize);
> +	while (len > 0) {
> +		printf("\r  block at %llx ", off);
> +		if (mtd_block_isbad(mtd, off)) {
> +			printf("marked bad, skipping\n");
> +		} else {
> +			ret = nand_torture(mtd, off);
> +			if (ret) {
> +				failed++;
> +				printf("failed\n");
> +			} else {
> +				passed++;
> +			}
> +		}
> +		off += mtd->erasesize;
> +		len -= mtd->erasesize;
> +	}
> +	printf("\n Passed: %u, failed: %u\n", passed, failed);
> +	if (failed != 0)
> +		ret = CMD_RET_FAILURE;
> +
> +out_put_mtd:
> +	put_mtd_device(mtd);
> +
> +	return ret;
> +}
> +#endif
> +
>   static int do_mtd_bad(struct cmd_tbl *cmdtp, int flag, int argc,
>   		      char *const argv[])
>   {
> @@ -827,6 +1016,9 @@ U_BOOT_LONGHELP(mtd,
>   #endif
>   #ifdef CONFIG_CMD_MTD_MARKBAD
>   	"mtd markbad                           <name>         <off> [<off> ...]\n"
> +#endif
> +#ifdef CONFIG_CMD_MTD_TORTURE
> +	"mtd torture                           <name>        [<off> [<size>]]\n"
>   #endif
>   	"\n"
>   	"With:\n"
> @@ -864,6 +1056,10 @@ U_BOOT_CMD_WITH_SUBCMDS(mtd, "MTD utils", mtd_help_text,
>   #ifdef CONFIG_CMD_MTD_MARKBAD
>   		U_BOOT_SUBCMD_MKENT_COMPLETE(markbad, 20, 0, do_mtd_markbad,
>   					     mtd_name_complete),
> +#endif
> +#ifdef CONFIG_CMD_MTD_TORTURE
> +		U_BOOT_SUBCMD_MKENT_COMPLETE(torture, 4, 0, do_mtd_torture,
> +					     mtd_name_complete),
>   #endif
>   		U_BOOT_SUBCMD_MKENT_COMPLETE(bad, 2, 1, do_mtd_bad,
>   					     mtd_name_complete));



More information about the U-Boot mailing list