diff options
author | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2016-08-22 16:05:19 +0200 |
---|---|---|
committer | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2016-11-17 11:36:56 +0100 |
commit | f1d405ebe533d872c421b523aff371d215bf9118 (patch) | |
tree | 8dfa001f5aa79871d66bb7713e6a6f87fe83b1f7 | |
parent | 5d524111adc549ec80e577dd3d9045a4c589fa7b (diff) |
mtd-utils: Add flash torture test utility
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@sigma-star.at>
Signed-off-by: Richard Weinberger <richard@nod.at>
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | tests/mtd-tests/Makemodule.am | 12 | ||||
-rw-r--r-- | tests/mtd-tests/flash_torture.c | 252 |
4 files changed, 266 insertions, 0 deletions
@@ -78,6 +78,7 @@ stress_2 stress_3 test_1 test_2 +flash_torture ubiattach ubiblock ubicrc32 diff --git a/Makefile.am b/Makefile.am index 686a747..5caa5ca 100644 --- a/Makefile.am +++ b/Makefile.am @@ -46,4 +46,5 @@ include tests/ubi-tests/Makemodule.am include tests/jittertest/Makemodule.am include tests/checkfs/Makemodule.am include tests/fs-tests/Makemodule.am +include tests/mtd-tests/Makemodule.am endif diff --git a/tests/mtd-tests/Makemodule.am b/tests/mtd-tests/Makemodule.am new file mode 100644 index 0000000..d61156f --- /dev/null +++ b/tests/mtd-tests/Makemodule.am @@ -0,0 +1,12 @@ +flash_torture_SOURCES = tests/mtd-tests/flash_torture.c +flash_torture_LDADD = libmtd.a +flash_torture_CPPFLAGS = $(AM_CPPFLAGS) + +MTDTEST_BINS = \ + flash_torture + +if INSTALL_TESTS +pkglibexec_PROGRAMS += $(MTDTEST_BINS) +else +noinst_PROGRAMS += $(MTDTEST_BINS) +endif diff --git a/tests/mtd-tests/flash_torture.c b/tests/mtd-tests/flash_torture.c new file mode 100644 index 0000000..21cedf4 --- /dev/null +++ b/tests/mtd-tests/flash_torture.c @@ -0,0 +1,252 @@ +/* + * 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@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 <getopt.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 const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "peb", required_argument, NULL, 'b' }, + { "count", required_argument, NULL, 'c' }, + { "skip", required_argument, NULL, 's' }, + { "keep", no_argument, NULL, 'k' }, + { "repeate", no_argument, NULL, 'r' }, + { NULL, 0, NULL, 0 }, +}; + +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, --count <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 opt, const char *arg) +{ + char *end; + long num; + + num = strtol(arg, &end, 0); + + if (!end || *end != '\0') { + fprintf(stderr, "-%c: expected integer argument\n", opt); + exit(EXIT_FAILURE); + } + return num; +} + +static void process_options(int argc, char **argv) +{ + int c; + + while (1) { + c = getopt_long(argc, argv, "hb:c:s:kr", options, NULL); + if (c == -1) + break; + + switch (c) { + case 'b': + if (peb >= 0) + goto failmulti; + peb = read_num(c, optarg); + if (peb < 0) + goto failarg; + break; + case 'c': + if (blocks > 0) + goto failmulti; + blocks = read_num(c, optarg); + if (blocks <= 0) + goto failarg; + break; + case 's': + if (skip >= 0) + goto failmulti; + skip = read_num(c, optarg); + if (skip < 0) + goto failarg; + break; + case 'k': + if (flags & KEEP_CONTENTS) + goto failmulti; + flags |= KEEP_CONTENTS; + break; + case 'r': + if (flags & RUN_FOREVER) + goto failmulti; + flags |= RUN_FOREVER; + break; + case 'h': + usage(EXIT_SUCCESS); + default: + exit(EXIT_FAILURE); + } + } + + if (optind < argc) + mtddev = argv[optind++]; + else + errmsg_die("No device specified!\n"); + + if (optind < argc) + usage(EXIT_FAILURE); + if (peb < 0) + peb = 0; + if (skip < 0) + skip = 0; + if (blocks < 0) + blocks = 1; + return; +failmulti: + errmsg_die("'-%c' specified more than once!\n", c); +failarg: + errmsg_die("Invalid argument for '-%c'!\n", c); +} + +int main(int argc, char **argv) +{ + int i, eb, err, count = 0; + 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); + + signal(SIGINT, sighandler); + signal(SIGTERM, sighandler); + signal(SIGHUP, sighandler); + + if (flags & KEEP_CONTENTS) + old = xmalloc(mtd.eb_size); + + 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); + } + + 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; +} + |