diff options
Diffstat (limited to 'tests/mtd-tests')
-rw-r--r-- | tests/mtd-tests/Makemodule.am | 13 | ||||
-rw-r--r-- | tests/mtd-tests/flash_readtest.c | 12 | ||||
-rw-r--r-- | tests/mtd-tests/flash_speed.c | 276 | ||||
-rw-r--r-- | tests/mtd-tests/flash_stress.c | 12 | ||||
-rw-r--r-- | tests/mtd-tests/flash_torture.c | 10 | ||||
-rw-r--r-- | tests/mtd-tests/nandbiterrs.c | 35 |
6 files changed, 256 insertions, 102 deletions
diff --git a/tests/mtd-tests/Makemodule.am b/tests/mtd-tests/Makemodule.am index 5925fd9..d02e9e4 100644 --- a/tests/mtd-tests/Makemodule.am +++ b/tests/mtd-tests/Makemodule.am @@ -7,9 +7,12 @@ flash_stress_LDADD = libmtd.a flash_stress_CPPFLAGS = $(AM_CPPFLAGS) flash_speed_SOURCES = tests/mtd-tests/flash_speed.c -flash_speed_LDADD = libmtd.a +flash_speed_LDADD = libmtd.a $(PTHREAD_LIBS) flash_speed_CPPFLAGS = $(AM_CPPFLAGS) +flash_speed_LDADD += $(PTHREAD_CFLAGS) +flash_speed_CPPFLAGS += $(PTHREAD_CFLAGS) + nandbiterrs_SOURCES = tests/mtd-tests/nandbiterrs.c nandbiterrs_LDADD = libmtd.a nandbiterrs_CPPFLAGS = $(AM_CPPFLAGS) @@ -26,12 +29,6 @@ nandsubpagetest_SOURCES = tests/mtd-tests/nandsubpagetest.c nandsubpagetest_LDADD = libmtd.a nandsubpagetest_CPPFLAGS = $(AM_CPPFLAGS) -MTDTEST_BINS = \ +test_PROGRAMS += \ flash_torture flash_stress flash_speed nandbiterrs flash_readtest \ nandpagetest nandsubpagetest - -if INSTALL_TESTS -pkglibexec_PROGRAMS += $(MTDTEST_BINS) -else -noinst_PROGRAMS += $(MTDTEST_BINS) -endif diff --git a/tests/mtd-tests/flash_readtest.c b/tests/mtd-tests/flash_readtest.c index c5fabc9..519ff89 100644 --- a/tests/mtd-tests/flash_readtest.c +++ b/tests/mtd-tests/flash_readtest.c @@ -125,10 +125,14 @@ static void process_options(int argc, char **argv) } } - if (optind < argc) - mtddev = argv[optind++]; - else + if (optind < argc) { + mtddev = mtd_find_dev_node(argv[optind]); + if (!mtddev) + errmsg_die("Can't find MTD device %s", argv[optind]); + optind++; + } else { errmsg_die("No device specified!\n"); + } if (optind < argc) usage(EXIT_FAILURE); @@ -227,7 +231,7 @@ int main(int argc, char **argv) puts("not NAND flash, assume page size is 512 bytes."); pgsize = 512; } else { - pgsize = mtd.subpage_size; + pgsize = mtd.min_io_size; } pgcnt = mtd.eb_size / pgsize; diff --git a/tests/mtd-tests/flash_speed.c b/tests/mtd-tests/flash_speed.c index 0058269..11f396c 100644 --- a/tests/mtd-tests/flash_speed.c +++ b/tests/mtd-tests/flash_speed.c @@ -23,6 +23,7 @@ * Author: Adrian Hunter <adrian.hunter@nokia.com> */ #define DESTRUCTIVE 0x01 +#define CONTINOUS 0x02 #define PROGRAM_NAME "flash_speed" @@ -33,6 +34,7 @@ #include <stdlib.h> #include <libmtd.h> #include <getopt.h> +#include <pthread.h> #include <stdio.h> #include <fcntl.h> #include <time.h> @@ -46,7 +48,9 @@ static const char *mtddev; static libmtd_t mtd_desc; static int fd; -static int peb=-1, count=-1, skip=-1, flags=0; +static int npages = 1; +static int peb=-1, count=-1, skip=-1, flags=0, speb=-1; +static bool continuous = false; static struct timespec start, finish; static int pgsize, pgcnt; static int goodebcnt; @@ -57,6 +61,8 @@ static const struct option options[] = { { "peb", required_argument, NULL, 'b' }, { "count", required_argument, NULL, 'c' }, { "skip", required_argument, NULL, 's' }, + { "sec-peb", required_argument, NULL, 'k' }, + { "continuous", no_argument, NULL, 'C' }, { NULL, 0, NULL, 0 }, }; @@ -69,7 +75,9 @@ static NORETURN void usage(int status) " -b, --peb <num> Start from this physical erase block\n" " -c, --count <num> Number of erase blocks to use (default: all)\n" " -s, --skip <num> Number of blocks to skip\n" - " -d, --destructive Run destructive (erase and write speed) tests\n", + " -d, --destructive Run destructive (erase and write speed) tests\n" + " -k, --sec-peb <num> Start of secondary block to measure RWW latency (requires -d)\n" + " -C, --continuous Increase the number of consecutive pages gradually\n", status==EXIT_SUCCESS ? stdout : stderr); exit(status); } @@ -93,7 +101,7 @@ static void process_options(int argc, char **argv) int c; while (1) { - c = getopt_long(argc, argv, "hb:c:s:d", options, NULL); + c = getopt_long(argc, argv, "hb:c:s:dk:C", options, NULL); if (c == -1) break; @@ -126,15 +134,29 @@ static void process_options(int argc, char **argv) goto failmulti; flags |= DESTRUCTIVE; break; + case 'k': + if (speb >= 0) + goto failmulti; + speb = read_num(c, optarg); + if (speb < 0) + goto failarg; + break; + case 'C': + continuous = true; + break; default: exit(EXIT_FAILURE); } } - if (optind < argc) - mtddev = argv[optind++]; - else + if (optind < argc) { + mtddev = mtd_find_dev_node(argv[optind]); + if (!mtddev) + errmsg_die("Can't find MTD device %s", argv[optind]); + optind++; + } else { errmsg_die("No device specified!\n"); + } if (optind < argc) usage(EXIT_FAILURE); @@ -144,11 +166,15 @@ static void process_options(int argc, char **argv) skip = 0; if (count < 0) count = 1; + if (speb >= 0 && !(flags & DESTRUCTIVE)) + goto faildestr; return; failmulti: errmsg_die("'-%c' specified more than once!\n", c); failarg: errmsg_die("Invalid argument for '-%c'!\n", c); +faildestr: + errmsg_die("'-k' specified, -d is missing!\n"); } static int write_eraseblock(int ebnum) @@ -214,71 +240,56 @@ static int write_eraseblock_by_2pages(int ebnum) return err; } -static int read_eraseblock_by_page(int ebnum) -{ - void *buf = iobuf; - int i, err = 0; - - for (i = 0; i < pgcnt; ++i) { - err = mtd_read(&mtd, fd, ebnum, i * pgsize, iobuf, pgsize); - if (err) { - fprintf(stderr, "Error reading block %d, page %d!\n", - ebnum, i); - break; - } - buf += pgsize; - } - - return err; -} - -static int read_eraseblock_by_2pages(int ebnum) +static int read_eraseblock_by_npages(int ebnum) { - int i, n = pgcnt / 2, err = 0; - size_t sz = pgsize * 2; + int i, n = pgcnt / npages, err = 0; + size_t sz = pgsize * npages; void *buf = iobuf; for (i = 0; i < n; ++i) { err = mtd_read(&mtd, fd, ebnum, i * sz, iobuf, sz); if (err) { - fprintf(stderr, "Error reading block %d, page %d + %d!\n", - ebnum, i*2, i*2+1); + fprintf(stderr, "Error reading block %d, page [%d-%d]!\n", + ebnum, i*npages, (i*npages) + npages- 1); return err; } buf += sz; } - if (pgcnt % 2) { - err = mtd_read(&mtd, fd, ebnum, i * sz, iobuf, pgsize); - if (err) { - fprintf(stderr, "Error reading block %d, page %d!\n", - ebnum, i*2); - } - } return err; } -static void start_timing(void) +static void start_timing(struct timespec *start) { - clock_gettime(CLOCK_MONOTONIC_RAW, &start); + clock_gettime(CLOCK_MONOTONIC_RAW, start); } -static void stop_timing(void) +static void stop_timing(struct timespec *finish) { - clock_gettime(CLOCK_MONOTONIC_RAW, &finish); + clock_gettime(CLOCK_MONOTONIC_RAW, finish); } -static long calc_speed(void) +static long calc_duration(struct timespec *start, struct timespec *finish) { long ms; - ms = (finish.tv_sec - start.tv_sec) * 1000L; - ms += (finish.tv_nsec - start.tv_nsec) / 1000000L; + ms = (finish->tv_sec - start->tv_sec) * 1000L; + ms += (finish->tv_nsec - start->tv_nsec) / 1000000L; + + return ms; +} + +static long calc_speed(struct timespec *start, struct timespec *finish, + int pages_per_set) +{ + long ms = calc_duration(start, finish); + int sets_in_eb = pgcnt / pages_per_set; + size_t sz = pgsize * pages_per_set * sets_in_eb; if (ms <= 0) return 0; - return ((long)goodebcnt * (mtd.eb_size / 1024L) * 1000L) / ms; + return ((long)goodebcnt * (sz / 1024L) * 1000L) / ms; } static void scan_for_bad_eraseblocks(unsigned int eb, int ebcnt, int ebskip) @@ -313,8 +324,34 @@ static int erase_good_eraseblocks(unsigned int eb, int ebcnt, int ebskip) return err; } -#define TIME_OP_PER_PEB( op )\ - start_timing();\ +struct thread_arg { + int (*op)(int peb); + int peb; + struct timespec start; + struct timespec finish; +}; + +static void *op_thread(void *ptr) +{ + struct thread_arg *args = ptr; + unsigned long err = 0; + int i; + + start_timing(&args->start); + for (i = 0; i < count; ++i) { + if (bbt[i]) + continue; + err = args->op(args->peb + i * (skip + 1)); + if (err) + break; + } + stop_timing(&args->finish); + + return (void *)err; +} + +#define TIME_OP_PER_PEB( op, npages ) \ + start_timing(&start);\ for (i = 0; i < count; ++i) {\ if (bbt[i])\ continue;\ @@ -322,8 +359,8 @@ static int erase_good_eraseblocks(unsigned int eb, int ebcnt, int ebskip) if (err)\ goto out;\ }\ - stop_timing();\ - speed = calc_speed() + stop_timing(&finish);\ + speed = calc_speed(&start, &finish, npages) int main(int argc, char **argv) { @@ -343,7 +380,7 @@ int main(int argc, char **argv) puts("not NAND flash, assume page size is 512 bytes."); pgsize = 512; } else { - pgsize = mtd.subpage_size; + pgsize = mtd.min_io_size; } pgcnt = mtd.eb_size / pgsize; @@ -384,13 +421,13 @@ int main(int argc, char **argv) goto out; puts("testing eraseblock write speed"); - TIME_OP_PER_PEB(write_eraseblock); + TIME_OP_PER_PEB(write_eraseblock, 1); printf("eraseblock write speed is %ld KiB/s\n", speed); } /* Read all eraseblocks, 1 eraseblock at a time */ puts("testing eraseblock read speed"); - TIME_OP_PER_PEB(read_eraseblock); + TIME_OP_PER_PEB(read_eraseblock, 1); printf("eraseblock read speed is %ld KiB/s\n", speed); /* Write all eraseblocks, 1 page at a time */ @@ -400,40 +437,55 @@ int main(int argc, char **argv) goto out; puts("testing page write speed"); - TIME_OP_PER_PEB(write_eraseblock_by_page); + TIME_OP_PER_PEB(write_eraseblock_by_page, 1); printf("page write speed is %ld KiB/s\n", speed); } /* Read all eraseblocks, 1 page at a time */ puts("testing page read speed"); - TIME_OP_PER_PEB(read_eraseblock_by_page); + npages = 1; + TIME_OP_PER_PEB(read_eraseblock_by_npages, npages); printf("page read speed is %ld KiB/s\n", speed); - /* Write all eraseblocks, 2 pages at a time */ - if (flags & DESTRUCTIVE) { - err = erase_good_eraseblocks(peb, count, skip); - if (err) - goto out; + if (continuous) { + /* Write all eraseblocks, 2 pages at a time */ + if (flags & DESTRUCTIVE) { + err = erase_good_eraseblocks(peb, count, skip); + if (err) + goto out; - puts("testing 2 page write speed"); - TIME_OP_PER_PEB(write_eraseblock_by_2pages); - printf("2 page write speed is %ld KiB/s\n", speed); - } + puts("testing 2 page write speed"); + TIME_OP_PER_PEB(write_eraseblock_by_2pages, 2); + printf("2 page write speed is %ld KiB/s\n", speed); + } - /* Read all eraseblocks, 2 pages at a time */ - puts("testing 2 page read speed"); - TIME_OP_PER_PEB(read_eraseblock_by_2pages); - printf("2 page read speed is %ld KiB/s\n", speed); + /* Read all eraseblocks, N pages at a time */ + puts("testing multiple pages read speed"); + for (npages = 2; npages <= 16 && npages <= pgcnt; npages++) { + TIME_OP_PER_PEB(read_eraseblock_by_npages, npages); + printf("%d page read speed is %ld KiB/s\n", npages, speed); + } + if (pgcnt >= 32) { + npages = 32; + TIME_OP_PER_PEB(read_eraseblock_by_npages, npages); + printf("%d page read speed is %ld KiB/s\n", npages, speed); + } + if (pgcnt >= 64) { + npages = 64; + TIME_OP_PER_PEB(read_eraseblock_by_npages, npages); + printf("%d page read speed is %ld KiB/s\n", npages, speed); + } + } /* Erase all eraseblocks */ if (flags & DESTRUCTIVE) { puts("Testing erase speed"); - start_timing(); + start_timing(&start); err = erase_good_eraseblocks(peb, count, skip); if (err) goto out; - stop_timing(); - speed = calc_speed(); + stop_timing(&finish); + speed = calc_speed(&start, &finish, 1); printf("erase speed is %ld KiB/s\n", speed); } @@ -442,7 +494,7 @@ int main(int argc, char **argv) for (k = 1; k < 7; ++k) { blocks = 1 << k; printf("Testing %dx multi-block erase speed\n", blocks); - start_timing(); + start_timing(&start); for (i = 0; i < count; ) { for (j = 0; j < blocks && (i + j) < count; ++j) if (bbt[i + j]) @@ -456,13 +508,93 @@ int main(int argc, char **argv) goto out; i += j; } - stop_timing(); - speed = calc_speed(); + stop_timing(&finish); + speed = calc_speed(&start, &finish, 1); printf("%dx multi-block erase speed is %ld KiB/s\n", blocks, speed); } } + /* Write a page and immediately after try to read another page. Report + * the latency difference when performed on different banks (NOR only). + */ + if (speb >= 0 && mtd.subpage_size == 1) { + long rww_duration_w, rww_latency_end; + long rww_duration_rnw, rww_duration_r_end; + bool rww_r_end_first; + struct thread_arg write_args_peb = { + .op = write_eraseblock, + .peb = peb, + }; + struct thread_arg read_args_speb = { + .op = read_eraseblock, + .peb = speb, + }; + struct sched_param param_write, param_read; + pthread_attr_t attr_write, attr_read; + pthread_t write_thread, read_thread; + void *retval; + + puts("testing read while write latency"); + + /* Change scheduling priorities so that the write thread gets + *scheduled more aggressively than the read thread. + */ + pthread_attr_init(&attr_write); + pthread_attr_setinheritsched(&attr_write, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&attr_write, SCHED_FIFO); + param_write.sched_priority = 42; + pthread_attr_setschedparam(&attr_write, ¶m_write); + + pthread_attr_init(&attr_read); + pthread_attr_setinheritsched(&attr_read, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&attr_read, SCHED_FIFO); + param_read.sched_priority = 41; + pthread_attr_setschedparam(&attr_read, ¶m_read); + + err = pthread_create(&write_thread, &attr_write, + (void *)op_thread, &write_args_peb); + if (err) { + errmsg("parallel write pthread create failed"); + goto out; + } + + err = pthread_create(&read_thread, &attr_read, + (void *)op_thread, &read_args_speb); + if (err) { + errmsg("parallel read pthread create failed"); + goto out; + } + + pthread_join(read_thread, &retval); + if ((long)retval) { + errmsg("parallel read pthread failed"); + goto out; + } + + pthread_join(write_thread, &retval); + if ((long)retval) { + errmsg("parallel write pthread failed"); + goto out; + } + + rww_duration_w = calc_duration(&write_args_peb.start, + &write_args_peb.finish); + rww_latency_end = calc_duration(&write_args_peb.finish, + &read_args_speb.finish); + rww_r_end_first = rww_latency_end < 0; + if (rww_r_end_first) + rww_duration_rnw = rww_duration_w; + else + rww_duration_rnw = calc_duration(&write_args_peb.start, + &read_args_speb.finish); + + rww_duration_r_end = calc_duration(&write_args_peb.start, + &read_args_speb.finish); + printf("read while write took %ldms, read ended after %ldms\n", + rww_duration_rnw, rww_duration_r_end); + } + puts("finished"); status = EXIT_SUCCESS; out: diff --git a/tests/mtd-tests/flash_stress.c b/tests/mtd-tests/flash_stress.c index 249d8cb..da39e14 100644 --- a/tests/mtd-tests/flash_stress.c +++ b/tests/mtd-tests/flash_stress.c @@ -126,10 +126,14 @@ static void process_options(int argc, char **argv) } } - if (optind < argc) - mtddev = argv[optind++]; - else + if (optind < argc) { + mtddev = mtd_find_dev_node(argv[optind]); + if (!mtddev) + errmsg_die("Can't find MTD device %s", argv[optind]); + optind++; + } else { errmsg_die("No device specified!\n"); + } if (optind < argc) usage(EXIT_FAILURE); @@ -242,7 +246,7 @@ int main(int argc, char **argv) puts("not NAND flash, assume page size is 512 bytes."); pgsize = 512; } else { - pgsize = mtd.subpage_size; + pgsize = mtd.min_io_size; } pgcnt = mtd.eb_size / pgsize; diff --git a/tests/mtd-tests/flash_torture.c b/tests/mtd-tests/flash_torture.c index 5aad8e0..6363f9e 100644 --- a/tests/mtd-tests/flash_torture.c +++ b/tests/mtd-tests/flash_torture.c @@ -144,10 +144,14 @@ static void process_options(int argc, char **argv) } } - if (optind < argc) - mtddev = argv[optind++]; - else + if (optind < argc) { + mtddev = mtd_find_dev_node(argv[optind]); + if (!mtddev) + errmsg_die("Can't find MTD device %s", argv[optind]); + optind++; + } else { errmsg_die("No device specified!\n"); + } if (optind < argc) usage(EXIT_FAILURE); diff --git a/tests/mtd-tests/nandbiterrs.c b/tests/mtd-tests/nandbiterrs.c index f583c14..424a95f 100644 --- a/tests/mtd-tests/nandbiterrs.c +++ b/tests/mtd-tests/nandbiterrs.c @@ -63,12 +63,13 @@ #define MODE_INCREMENTAL 0x02 #define MODE_OVERWRITE 0x04 #define PAGE_ERASED 0x08 +#define CONTINUOUS_READ 0x10 static int peb = -1, page = -1, max_overwrite = -1, seed = -1; static const char *mtddev; static unsigned char *wbuffer, *rbuffer, *old_data; -static int fd, pagesize, pagecount, flags; +static int fd, pagesize, bs, pagecount, flags; static struct mtd_dev_info mtd; static libmtd_t mtd_desc; @@ -81,6 +82,7 @@ static const struct option options[] = { { "erased", no_argument, NULL, 'e' }, { "writes", required_argument, NULL, 'w' }, { "incremental", no_argument, NULL, 'i' }, + { "continuous", no_argument, NULL, 'c' }, { "overwrite", no_argument, NULL, 'o' }, { NULL, 0, NULL, 0 }, }; @@ -95,7 +97,8 @@ static NORETURN void usage(int status) " -b, --peb <num> Use this physical erase block\n" " -p, --page <num> Use this page within the erase block\n" " -s, --seed <num> Specify seed for PRNG\n" - " -e, --erased Test erased pages instead of written pages\n\n" + " -e, --erased Test erased pages instead of written pages\n" + " -c, --continuous Use two consecutive pages (incremental test only)\n\n" "Options controling test mode:\n" " -i, --incremental Manually insert bit errors until ECC fails\n" " -o, --overwrite Rewrite page until bits flip and ECC fails\n\n" @@ -124,7 +127,7 @@ static void process_options(int argc, char **argv) int c; while (1) { - c = getopt_long(argc, argv, "hkb:p:s:eiow:", options, NULL); + c = getopt_long(argc, argv, "hkb:p:s:eiow:c", options, NULL); if (c == -1) break; @@ -175,6 +178,9 @@ static void process_options(int argc, char **argv) case 'e': flags |= PAGE_ERASED; break; + case 'c': + flags |= CONTINUOUS_READ; + break; case 'h': usage(EXIT_SUCCESS); default: @@ -193,6 +199,9 @@ static void process_options(int argc, char **argv) if (!(flags & (MODE_OVERWRITE|MODE_INCREMENTAL))) errmsg_die("No test mode specified!"); + if (flags & CONTINUOUS_READ && !(flags & MODE_INCREMENTAL)) + errmsg_die("Use --continuous with --incremental only!"); + if ((max_overwrite > 0) && !(flags & MODE_OVERWRITE)) errmsg_die("Write count specified but mode is not --overwrite!"); @@ -235,9 +244,9 @@ static void init_buffer(void) unsigned int i; if (flags & PAGE_ERASED) { - memset(wbuffer, 0xff, pagesize); + memset(wbuffer, 0xff, bs); } else { - for (i = 0; i < pagesize; ++i) + for (i = 0; i < bs; ++i) wbuffer[i] = hash(i+seed); } } @@ -251,7 +260,7 @@ static int write_page(void) goto fail_mode; err = mtd_write(mtd_desc, &mtd, fd, peb, page*pagesize, - wbuffer, pagesize, NULL, 0, 0); + wbuffer, bs, NULL, 0, 0); if (err) fprintf(stderr, "Failed to write page %d in block %d\n", peb, page); @@ -290,7 +299,7 @@ static int read_page(void) if (ioctl(fd, ECCGETSTATS, &old) != 0) goto failstats; - err = mtd_read(&mtd, fd, peb, page*pagesize, rbuffer, pagesize); + err = mtd_read(&mtd, fd, peb, page*pagesize, rbuffer, bs); if (err) { fputs("Read failed!\n", stderr); return -1; @@ -316,7 +325,7 @@ static int verify_page(void) int erased = flags & PAGE_ERASED; unsigned int i, errs = 0; - for (i = 0; i < pagesize; ++i) { + for (i = 0; i < bs; ++i) { if (rbuffer[i] != (erased ? 0xff : hash(i+seed))) ++errs; } @@ -332,7 +341,7 @@ static int insert_biterror(void) { int bit, mask, byte; - for (byte = 0; byte < pagesize; ++byte) { + for (byte = 0; byte < bs; ++byte) { for (bit = 7, mask = 0x80; bit >= 0; bit--, mask >>= 1) { if (wbuffer[byte] & mask) { wbuffer[byte] &= ~mask; @@ -461,6 +470,10 @@ int main(int argc, char **argv) pagesize = mtd.subpage_size; pagecount = mtd.eb_size / pagesize; + if (!(flags & CONTINUOUS_READ)) + bs = pagesize; + else + bs = 2 * pagesize; if (peb >= mtd.eb_cnt) return errmsg("Physical erase block %d is out of range!", peb); @@ -483,13 +496,13 @@ int main(int argc, char **argv) } } - wbuffer = malloc(pagesize); + wbuffer = malloc(bs); if (!wbuffer) { perror(NULL); goto fail_dev; } - rbuffer = malloc(pagesize); + rbuffer = malloc(bs); if (!rbuffer) { perror(NULL); goto fail_rbuffer; |