From 92a5d591906d88302bcecfce1d6b61c6a1675374 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Mon, 22 Aug 2016 16:08:34 +0200 Subject: mtd-utils: Add flash stress test utility Basically a user space port of the mtd stress 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. Signed-off-by: David Oberhollenzer Signed-off-by: Richard Weinberger --- .gitignore | 1 + tests/mtd-tests/Makemodule.am | 6 +- tests/mtd-tests/flash_stress.c | 287 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 tests/mtd-tests/flash_stress.c diff --git a/.gitignore b/.gitignore index 755dd5d..a37cbdc 100644 --- a/.gitignore +++ b/.gitignore @@ -79,6 +79,7 @@ stress_3 test_1 test_2 flash_torture +flash_stress ubiattach ubiblock ubicrc32 diff --git a/tests/mtd-tests/Makemodule.am b/tests/mtd-tests/Makemodule.am index d61156f..541bc4b 100644 --- a/tests/mtd-tests/Makemodule.am +++ b/tests/mtd-tests/Makemodule.am @@ -2,8 +2,12 @@ flash_torture_SOURCES = tests/mtd-tests/flash_torture.c flash_torture_LDADD = libmtd.a flash_torture_CPPFLAGS = $(AM_CPPFLAGS) +flash_stress_SOURCES = tests/mtd-tests/flash_stress.c +flash_stress_LDADD = libmtd.a +flash_stress_CPPFLAGS = $(AM_CPPFLAGS) + MTDTEST_BINS = \ - flash_torture + flash_torture flash_stress if INSTALL_TESTS pkglibexec_PROGRAMS += $(MTDTEST_BINS) diff --git a/tests/mtd-tests/flash_stress.c b/tests/mtd-tests/flash_stress.c new file mode 100644 index 0000000..f50c664 --- /dev/null +++ b/tests/mtd-tests/flash_stress.c @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2006-2008 Nokia Corporation + * 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. + * + * Test random reads, writes and erases on MTD device. + * + * Author: David Oberhollenzer + * + * Based on linux stresstest.c + * Author: Adrian Hunter + */ +#define PROGRAM_NAME "flash_stress" + +#define KEEP_CONTENTS 0x01 +#define COUNT_CHANGED 0x02 +#define SEED_SET 0x04 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +static struct mtd_dev_info mtd; +static const char *mtddev; +static libmtd_t mtd_desc; +static int fd; + +static unsigned char *writebuf; +static unsigned char *readbuf; +static unsigned char *old; +static unsigned char *bbt; + +static int pgsize; +static int pgcnt; + +static int count = 10000; +static int flags = 0; + +static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "keep", no_argument, NULL, 'k' }, + { "seed", required_argument, NULL, 's' }, + { "count", required_argument, NULL, 'c' }, + { NULL, 0, NULL, 0 }, +}; + +static void usage(int status) +{ + fputs( + "Usage: "PROGRAM_NAME" [OPTIONS] \n\n" + "Options:\n" + " -h, --help Display this help output\n" + " -c, --count Number of operations to do (default is 10000)\n" + " -s, --seed Seed for pseudor random number generator\n" + " -k, --keep Restore existing contents after test\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, "hc:s:k", options, NULL); + if (c == -1) + break; + + switch (c) { + case 'k': + if (flags & KEEP_CONTENTS) + goto failmulti; + flags |= KEEP_CONTENTS; + break; + case 's': + if (flags & SEED_SET) + goto failmulti; + srand(read_num(c, optarg)); + flags |= SEED_SET; + break; + case 'c': + if (flags & COUNT_CHANGED) + goto failmulti; + count = read_num(c, optarg); + if (count <= 0) + goto failarg; + flags |= COUNT_CHANGED; + 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 (!(flags & SEED_SET)) + srand(time(NULL)); + return; +failmulti: + errmsg_die("'-%c' specified more than once!\n", c); +failarg: + errmsg_die("Invalid argument for '-%c'!\n", c); +} + +static int rand_eb(void) +{ + unsigned int eb; + + /* Read or write up 2 eraseblocks at a time - hence 'mtd.eb_cnt - 1' */ + do { + eb = rand() % (mtd.eb_cnt - 1); + } while (bbt[eb]); + + return eb; +} + +static int do_read(void) +{ + int eb = rand_eb(); + int offs = rand() % pgcnt; + int len = rand() % (pgcnt - offs); + + offs *= pgsize; + len *= pgsize; + return mtd_read(&mtd, fd, eb, offs, readbuf, len); +} + +static int do_write(void) +{ + int eb = rand_eb(), err, err1; + int offs = rand() % pgcnt; + int len = rand() % (pgcnt - offs); + + offs *= pgsize; + len *= pgsize; + + if (flags & KEEP_CONTENTS) { + err = mtd_read(&mtd, fd, eb, 0, old, mtd.eb_size); + if (err) { + fputs("Error backing up old erase block contents\n", stderr); + return -1; + } + } + + err = mtd_erase(mtd_desc, &mtd, fd, eb); + if (err) + goto out; + + err = mtd_write(mtd_desc, &mtd, fd, eb, offs, + writebuf, len, NULL, 0, 0); + if (err) + goto out; + + err = 0; +out: + if (flags & KEEP_CONTENTS) { + if (mtd_erase(mtd_desc, &mtd, fd, eb)) { + fprintf(stderr, "mtd_erase: PEB %d", eb); + return -1; + } + + err1 = mtd_write(mtd_desc, &mtd, fd, eb, 0, + old, mtd.eb_size, NULL, 0, 0); + + if (err1) { + fprintf(stderr, "Failed to restore old contents\n"); + return -1; + } + } + return err; +} + +static void scan_for_bad_eraseblocks(unsigned int eb, int ebcnt) +{ + int i, bad = 0; + + puts("scanning for bad eraseblocks"); + + for (i = 0; i < ebcnt; ++i) { + bbt[i] = mtd_is_bad(&mtd, fd, eb + i) ? 1 : 0; + if (bbt[i]) + bad += 1; + } + + printf("scanned %d eraseblocks, %d are bad\n", ebcnt, bad); +} + +int main(int argc, char **argv) +{ + int status = EXIT_FAILURE, i, op, err; + + 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 (mtd.subpage_size == 1) { + puts("not NAND flash, assume page size is 512 bytes."); + pgsize = 512; + } else { + pgsize = mtd.subpage_size; + } + + pgcnt = mtd.eb_size / pgsize; + + readbuf = xmalloc(mtd.eb_size); + writebuf = xmalloc(mtd.eb_size); + bbt = xzalloc(mtd.eb_cnt); + + if (flags & KEEP_CONTENTS) + old = xmalloc(mtd.eb_size); + + for (i = 0; i < mtd.eb_size; ++i) + writebuf[i] = rand(); + + /* Open device file */ + if ((fd = open(mtddev, O_RDWR)) == -1) { + perror(mtddev); + goto out; + } + + /* Do operations */ + scan_for_bad_eraseblocks(0, mtd.eb_cnt); + + puts("doing operations"); + for (op = 0; op < count; op++) { + if ((op & 1023) == 0) + printf("%d operations done\n", op); + err = (rand() & 1) ? do_read() : do_write(); + if (err) + goto out; + } + printf("finished, %d operations done\n", op); + + status = EXIT_SUCCESS; +out: + close(fd); + free(bbt); + free(writebuf); + free(readbuf); + free(old); + return status; +} -- cgit v1.2.3