[PATCH 3/8] mtd-utils: Add flash torture test utility

Boris Brezillon boris.brezillon at free-electrons.com
Tue Apr 26 01:13:49 PDT 2016


On Tue, 26 Apr 2016 00:13:24 +0200
Richard Weinberger <richard at nod.at> wrote:

> From: David Oberhollenzer <david.oberhollenzer at sigma-star.at>
> 
> Basically a user space port of the mtd torture test kernel module. In
> addition to the block offset and count module parameters, the utility
> supports a block stride and can restore the block contents after test.
> 
> In contrast to the kernel module, the torture test is implemented by
> the libmtd mtd_toruture function and thus doesn't allow for similarly
> fine grained options on diagnostics.
> 
> Signed-off-by: David Oberhollenzer <david.oberhollenzer at sigma-star.at>
> Signed-off-by: Richard Weinberger <richard at nod.at>
> ---
>  .gitignore                 |   1 +
>  Makefile                   |   2 +-
>  misc-utils/flash_torture.c | 240 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 242 insertions(+), 1 deletion(-)
>  create mode 100644 misc-utils/flash_torture.c
> 
> diff --git a/.gitignore b/.gitignore
> index 2aac52c..5b529d1 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -35,6 +35,7 @@
>  /jffsX-utils/mkfs.jffs2
>  /misc-utils/mtd_debug
>  /misc-utils/mtdpart
> +/misc-utils/flash_torture
>  /nand-utils/nanddump
>  /nand-utils/nandtest
>  /nand-utils/nandwrite
> diff --git a/Makefile b/Makefile
> index 977c9c5..af3d1fd 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -20,7 +20,7 @@ MISC_BINS = \
>  	ftl_format doc_loadbios ftl_check mtd_debug docfdisk \
>  	serve_image recv_image mtdpart flash_erase flash_lock \
>  	flash_unlock flash_otp_info flash_otp_dump flash_otp_lock \
> -	flash_otp_write flashcp
> +	flash_otp_write flashcp flash_torture
>  UBI_BINS = \
>  	ubiupdatevol ubimkvol ubirmvol ubicrc32 ubinfo ubiattach \
>  	ubidetach ubinize ubiformat ubirename mtdinfo ubirsvol ubiblock
> diff --git a/misc-utils/flash_torture.c b/misc-utils/flash_torture.c
> new file mode 100644
> index 0000000..b5625c8
> --- /dev/null
> +++ b/misc-utils/flash_torture.c
> @@ -0,0 +1,240 @@
> +/*
> + * Copyright (C) 2006-2008 Artem Bityutskiy
> + * Copyright (C) 2006-2008 Jarkko Lavinen
> + * Copyright (C) 2006-2008 Adrian Hunter
> + * Copyright (C) 2015 sigma star gmbh
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program; see the file COPYING. If not, write to the Free Software
> + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> + *
> + *
> + * WARNING: this test program may kill your flash and your device. Do not
> + * use it unless you know what you do. Authors are not responsible for any
> + * damage caused by this program.
> + *
> + * Author: David Oberhollenzer <david.oberhollenzer at sigma-star.at>
> + *
> + * Based on linux torturetest.c
> + * Authors: Artem Bityutskiy, Jarkko Lavinen, Adria Hunter
> + */
> +
> +#define PROGRAM_NAME "flash_torture"
> +
> +#define KEEP_CONTENTS 0x01
> +#define RUN_FOREVER 0x02
> +
> +#include <mtd/mtd-user.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <libmtd.h>
> +#include <signal.h>
> +#include <stdio.h>
> +#include <fcntl.h>
> +
> +#include "common.h"
> +
> +static int peb=-1, blocks=-1, skip=-1;
> +static struct mtd_dev_info mtd;
> +static sig_atomic_t flags=0;
> +static const char *mtddev;
> +static libmtd_t mtd_desc;
> +static int mtdfd;
> +
> +static void sighandler(int sig)
> +{
> +	if (sig == SIGINT || sig == SIGTERM || sig == SIGHUP)
> +		flags &= ~RUN_FOREVER;
> +}
> +
> +static void usage(int status)
> +{
> +	fputs(
> +	"Usage: "PROGRAM_NAME" [OPTIONS] <device>\n\n"
> +	"Options:\n"
> +	"  -h, --help         Display this help output\n"
> +	"  -b, --peb <num>    Start from this physical erase block\n"
> +	"  -c, --blocks <num> Number of erase blocks to torture\n"
> +	"  -s, --skip <num>   Number of erase blocks to skip\n"
> +	"  -k, --keep         Try to restore existing contents after test\n"
> +	"  -r, --repeate      Repeate the torture test indefinitely\n",
> +	status==EXIT_SUCCESS ? stdout : stderr);
> +	exit(status);
> +}
> +
> +static long read_num(int idx, int argidx, int argc, char **argv)
> +{
> +	char *end;
> +	long num;
> +
> +	if (argidx >= argc) {
> +		fprintf(stderr, "%s: missing argument\n", argv[idx]);
> +		exit(EXIT_FAILURE);
> +	}
> +
> +	num = strtol(argv[argidx], &end, 0);
> +
> +	if (!end || *end!='\0') {
> +		fprintf(stderr, "%s: expected integer argument\n", argv[idx]);
> +		exit(EXIT_FAILURE);
> +	}
> +	return num;
> +}
> +
> +static void process_options(int argc, char **argv)
> +{
> +	int i;
> +
> +	for (i=1; i<argc; ++i) {
> +		if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) {
> +			usage(EXIT_SUCCESS);
> +		} else if (!strcmp(argv[i], "--peb") || !strcmp(argv[i], "-b")) {
> +			if (peb >= 0)
> +				goto failmulti;
> +			peb = read_num(i, i+1, argc, argv);
> +			if (peb < 0)
> +				goto failarg;
> +			++i;
> +		} else if (!strcmp(argv[i], "--blocks") || !strcmp(argv[i], "-c")) {
> +			if (blocks > 0)
> +				goto failmulti;
> +			blocks = read_num(i, i+1, argc, argv);
> +			if (blocks <= 0)
> +				goto failarg;
> +			++i;
> +		} else if (!strcmp(argv[i], "--skip") || !strcmp(argv[i], "-s")) {
> +			if (skip >= 0)
> +				goto failmulti;
> +			skip = read_num(i, i+1, argc, argv);
> +			if (skip < 0)
> +				goto failarg;
> +			++i;
> +		} else if (!strcmp(argv[i], "--keep") || !strcmp(argv[i], "-k")) {
> +			if (flags & KEEP_CONTENTS)
> +				goto failmulti;
> +			flags |= KEEP_CONTENTS;
> +		} else if (!strcmp(argv[i], "--repeate") || !strcmp(argv[i], "-r")) {
> +			if (flags & RUN_FOREVER)
> +				goto failmulti;
> +			flags |= RUN_FOREVER;
> +		} else {
> +			if (mtddev)
> +				usage(EXIT_FAILURE);
> +			mtddev = argv[i];
> +		}
> +	}

Hm, why not using getopt_long() to parse the command line?

> +
> +	if (!mtddev)
> +		errmsg_die("No device specified!\n");
> +	if (peb < 0)
> +		peb = 0;
> +	if (skip < 0)
> +		skip = 0;
> +	if (blocks < 0)
> +		blocks = 1;
> +	return;
> +failmulti:
> +	fprintf(stderr, "'%s' specified more than once!\n", argv[i]);
> +	exit(EXIT_FAILURE);
> +failarg:
> +	fprintf(stderr, "Invalid argument for '%s'!\n", argv[i]);
> +	exit(EXIT_FAILURE);
> +}
> +
> +int main(int argc, char **argv)
> +{
> +	int i, eb, err, count = 0;
> +	char* is_bad = NULL;

	char *is_bad = NULL;

> +	void *old=NULL;
> +
> +	process_options(argc, argv);
> +
> +	mtd_desc = libmtd_open();
> +	if (!mtd_desc)
> +		return errmsg("can't initialize libmtd");
> +
> +	if (mtd_get_dev_info(mtd_desc, mtddev, &mtd) < 0)
> +		return errmsg("mtd_get_dev_info failed");
> +
> +	if (peb >= mtd.eb_cnt)
> +		return errmsg("Physical erase block %d is out of range!\n", peb);
> +
> +	if ((peb + (blocks - 1)*(skip + 1)) >= mtd.eb_cnt) {
> +		return errmsg("Given block range exceeds block count of %d!\n",
> +				mtd.eb_cnt);
> +	}

You can drop the curly braces here.

> +
> +	signal(SIGINT, sighandler);
> +	signal(SIGTERM, sighandler);
> +	signal(SIGHUP, sighandler);
> +
> +	if (flags & KEEP_CONTENTS) {
> +		old = xmalloc(mtd.eb_size);
> +	}

Ditto.

> +
> +	is_bad = xmalloc(blocks);
> +
> +	if ((mtdfd = open(mtddev, O_RDWR)) == -1) {
> +		perror(mtddev);
> +		free(is_bad);
> +		free(old);
> +		return EXIT_FAILURE;
> +	}
> +
> +	for (i = 0; i < blocks; ++i) {
> +		eb = peb + i * (skip + 1);
> +		is_bad[i] = mtd_is_bad(&mtd, mtdfd, eb);
> +		if (is_bad[i]) {
> +			fprintf(stderr, "PEB %d marked bad, will be skipped\n", eb);
> +		}

Ditto.

> +	}
> +
> +	do {
> +		for (i = 0; i < blocks; ++i) {
> +			if (is_bad[i])
> +				continue;
> +
> +			eb = peb + i * (skip + 1);
> +
> +			if (flags & KEEP_CONTENTS) {
> +				err = mtd_read(&mtd, mtdfd, eb, 0, old, mtd.eb_size);
> +				if (err) {
> +					fprintf(stderr, "Failed to create backup copy "
> +							"of PEB %d, skipping!\n", eb);
> +					continue;
> +				}
> +			}
> +
> +			if (mtd_torture(mtd_desc, &mtd, mtdfd, eb))
> +				fprintf(stderr, "Block %d failed torture test!\n", eb);
> +
> +			if (flags & KEEP_CONTENTS) {
> +				err = mtd_erase(mtd_desc, &mtd, mtdfd, eb);
> +				if (err) {
> +					fprintf(stderr, "mtd_erase failed for block %d!\n", eb);
> +					continue;
> +				}
> +				err = mtd_write(mtd_desc, &mtd, mtdfd, eb, 0,
> +						old, mtd.eb_size, NULL, 0, 0);
> +				if (err)
> +					fprintf(stderr, "Failed to restore block %d!\n", eb);
> +			}
> +		}
> +
> +		printf("Torture test iterations done: %d\n", ++count);
> +	} while (flags & RUN_FOREVER);
> +
> +	free(old);
> +	free(is_bad);
> +	close(mtdfd);
> +	return EXIT_SUCCESS;
> +}



-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com



More information about the linux-mtd mailing list