]> www.infradead.org Git - mtd-utils.git/commitdiff
mtd-utils: Add flash torture test utility
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>
Mon, 22 Aug 2016 14:05:19 +0000 (16:05 +0200)
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>
Thu, 17 Nov 2016 10:36:56 +0000 (11:36 +0100)
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>
.gitignore
Makefile.am
tests/mtd-tests/Makemodule.am [new file with mode: 0644]
tests/mtd-tests/flash_torture.c [new file with mode: 0644]

index db12271f0f1a277cca27f435f2559e74c81cd2dc..755dd5dbb9a6aa36d125e347dc7106548586191d 100644 (file)
@@ -78,6 +78,7 @@ stress_2
 stress_3
 test_1
 test_2
+flash_torture
 ubiattach
 ubiblock
 ubicrc32
index 686a7477b54d45a1b0e1e4c9c79d644c688876f9..5caa5cad4f30414c936d3b643fec2a1c0f464a66 100644 (file)
@@ -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 (file)
index 0000000..d61156f
--- /dev/null
@@ -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 (file)
index 0000000..21cedf4
--- /dev/null
@@ -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;
+}
+