From 8e4bff4cab0ddac6060645b0715210484d02ff40 Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Wed, 30 Aug 2017 08:25:59 +0000 Subject: Initial file dump from open source release --- .../sprd_mtdtest/nandflash_bitfliptest/Makefile | 1 + .../nandflash_bitfliptest/nandflash_bitfliptest.c | 364 +++++++++++++++++++++ .../sprd_mtdtest/nandflash_bitfliptest/readme.txt | 9 + 3 files changed, 374 insertions(+) create mode 100755 drivers/mtd/tests/sprd_mtdtest/nandflash_bitfliptest/Makefile create mode 100755 drivers/mtd/tests/sprd_mtdtest/nandflash_bitfliptest/nandflash_bitfliptest.c create mode 100755 drivers/mtd/tests/sprd_mtdtest/nandflash_bitfliptest/readme.txt (limited to 'drivers/mtd/tests/sprd_mtdtest/nandflash_bitfliptest') diff --git a/drivers/mtd/tests/sprd_mtdtest/nandflash_bitfliptest/Makefile b/drivers/mtd/tests/sprd_mtdtest/nandflash_bitfliptest/Makefile new file mode 100755 index 00000000..ba65c86b --- /dev/null +++ b/drivers/mtd/tests/sprd_mtdtest/nandflash_bitfliptest/Makefile @@ -0,0 +1 @@ +obj-m += nandflash_bitfliptest.o diff --git a/drivers/mtd/tests/sprd_mtdtest/nandflash_bitfliptest/nandflash_bitfliptest.c b/drivers/mtd/tests/sprd_mtdtest/nandflash_bitfliptest/nandflash_bitfliptest.c new file mode 100755 index 00000000..a7c926bb --- /dev/null +++ b/drivers/mtd/tests/sprd_mtdtest/nandflash_bitfliptest/nandflash_bitfliptest.c @@ -0,0 +1,364 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef pr_fmt +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +struct mtd_part { + struct mtd_info mtd; + struct mtd_info *master; + loff_t offset; + struct list_head list; +}; +static int dev = 4; +static int threshold = 100; +module_param(dev, int, S_IRUGO); +module_param(threshold, int, S_IRUGO); +MODULE_PARM_DESC(dev, "MTD device number to use"); +MODULE_PARM_DESC(threshold, "the threshold of bit-flip num"); + +static struct mtd_info *mtd = NULL; +static struct mtd_part *mtd_part = NULL; +static unsigned char *bbt = NULL; + +static int ebcnt; +static int pgcnt; +static int errcnt; +static int rdcnt; +static int rdpgcnt; +static int pgsize; +static int bufsize; +static int *offsets; +static unsigned char *readbuf; +static unsigned char * writebuf; + +static int mtd_test_erase_eraseblock(int ebnum, int blocks) +{ + int err = 0; + struct erase_info ei = {0}; + + if(blocks == 0) + blocks = 1; + + ei.mtd = mtd; + ei.addr = ebnum * mtd->erasesize; + ei.len = mtd->erasesize * blocks; + + err = mtd_erase(mtd, &ei); + if (err) { + pr_err("error %d while erasing EB %d\n", err, ebnum); + return err; + } + + if (ei.state == MTD_ERASE_FAILED) { + pr_err("Some erase error occurred at EB %d\n", ebnum); + return -EIO; + } + + return 0; +} + +static int mtd_test_erase_whole_device(void) +{ + int err = 0; + unsigned int i = 0; + + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = mtd_test_erase_eraseblock(i, 0); + if (err) + return err; + cond_resched(); + } + return 0; +} + +static int mtd_test_scan_bad_block(void) +{ + unsigned int i = 0, bad = 0; + + bbt = (unsigned char *)vmalloc(ebcnt); + if (!bbt) { + pr_info("error: cannot allocate memory\n"); + return -ENOMEM; + } + + pr_info("scanning for bad eraseblocks first\n"); + for (i = 0; i < ebcnt; ++i) { + bbt[i] = mtd_block_isbad(mtd, i * mtd->erasesize) ? 1 : 0; + if (bbt[i]) + bad += 1; + cond_resched(); + } + pr_info("scanned %d eraseblocks, %d are bad\n", ebcnt, bad); + return 0; +} + +static int bitfliptest_rand_eb(void) +{ + unsigned int eb = 0; + +again: + eb = prandom_u32(); + /* Read or write up 2 eraseblocks at a time - hence 'ebcnt - 1' */ + eb %= (ebcnt - 1); + if (bbt[eb]) + goto again; + return eb; +} + +static int bitfliptest_rand_offs(void) +{ + unsigned int offs = 0; + + offs = prandom_u32(); + offs %= bufsize; + return offs; +} + +static int bitfliptest_rand_len(int offs) +{ + unsigned int len = 0; + + len = prandom_u32(); + len %= (bufsize - offs); + return len; +} + +static int bitfliptest_do_read(void) +{ + int err = 0; + size_t read = 0; + loff_t addr = 0; + unsigned int corrected_num = 0; + unsigned int eb = bitfliptest_rand_eb(); + unsigned int offs = bitfliptest_rand_offs(); + unsigned int len = bitfliptest_rand_len(offs); + struct mtd_ecc_stats stats= {0}; + + if (bbt[eb + 1]) { + if (offs >= mtd->erasesize) + offs -= mtd->erasesize; + if (offs + len > mtd->erasesize) + len = mtd->erasesize - offs; + } + addr = eb * mtd->erasesize + offs; + +// pr_info("read, addr: 0x%.8x len: %d\n", (unsigned int)addr, len); + stats = mtd_part->master->ecc_stats; + err = mtd_read(mtd, addr, len, &read, readbuf); + if (mtd_is_bitflip(err)) + err = 0; + if (unlikely(err || read != len)) { + pr_err("error: read failed at 0x%llx, err:%d len: %dread:%d\n", + (long long)addr, err, len, read); + if (!err) + err = -EINVAL; + return err; + } + corrected_num = mtd_part->master->ecc_stats.corrected - stats.corrected; + if(corrected_num != 0) { + errcnt += corrected_num; + pr_info("When we read addr:0x%.8x:0x%.8x, we found %d(%d) bit-flip!\n", (unsigned int)addr, len, corrected_num, errcnt); + } + rdpgcnt += len/mtd->writesize; + rdcnt ++; + return 0; +} + +static int bitfliptest_do_write(void) +{ + int err = 0; + loff_t addr = 0; + size_t written = 0; + int eb = bitfliptest_rand_eb(); + unsigned int offs = 0, len = 0; + + offs = offsets[eb]; + if (offs >= mtd->erasesize) { + err = mtd_test_erase_eraseblock(eb, 0); + if (err) + return err; + offs = offsets[eb] = 0; + } + len = bitfliptest_rand_len(offs); + len = ((len + pgsize - 1) / pgsize) * pgsize; + if (offs + len > mtd->erasesize) { + if (bbt[eb + 1]) + len = mtd->erasesize - offs; + else { +// pr_info("erase, block: %d\n", eb + 1); + err = mtd_test_erase_eraseblock(eb + 1, 0); + if (err) + return err; + offsets[eb + 1] = 0; + } + } + addr = eb * mtd->erasesize + offs; +// pr_info("write, addr: 0x%.8x len: %d\n", (unsigned int)addr, len); + err = mtd_write(mtd, addr, len, &written, writebuf); + if (unlikely(err || written != len)) { + pr_err("error: write failed at 0x%llx\n", + (long long)addr); + if (!err) + err = -EINVAL; + return err; + } + offs += len; + while (offs > mtd->erasesize) { + offsets[eb++] = mtd->erasesize; + offs -= mtd->erasesize; + } + offsets[eb] = offs; + return 0; +} + +static int bitfliptest_do_operation(void) +{ + if (prandom_u32() & 1) { + return bitfliptest_do_read(); + } else { + return bitfliptest_do_write(); + } +} + +static int mtd_test_bitfliptest(void) +{ + int err = 0; + unsigned int i = 0, op = 0; + + bufsize = mtd->erasesize * 2; + readbuf = vmalloc(bufsize); + if(!readbuf) { + pr_err("%s, alloc readbuf failed!\n", __func__); + return -ENOMEM; + } + writebuf = vmalloc(bufsize); + if(!writebuf) { + pr_err("%s, alloc writebuf failed!\n", __func__); + vfree(readbuf); + return -ENOMEM; + } + offsets = vmalloc(ebcnt * sizeof(int)); + if(!offsets) { + pr_err("%s, alloc offsets failed!\n", __func__); + vfree(readbuf); + vfree(writebuf); + return -ENOMEM; + } + pr_info("bitfliptest begin!\n"); + + for (i = 0; i < ebcnt; i++) + offsets[i] = mtd->erasesize; + prandom_bytes(writebuf, bufsize); + + err = mtd_test_erase_whole_device(); + if(err) + goto out; + + while(1) { + if ((op & 1023) == 0) + pr_info("%d operations done\n", op); + err = bitfliptest_do_operation(); + if (err) + goto out; + if(errcnt >= threshold) { + pr_info("Test times: %d, read times: %d, read pages: %d, bit-flip: %d.\n", op, rdcnt, rdpgcnt, errcnt); + break; + } + cond_resched(); + op += 1; + } + +out: + vfree(offsets); + vfree(readbuf); + vfree(writebuf); + pr_info("bitfliptest finished, %d operations done!\n", op); + return err; +} + +static int __init nandflash_bitfliptest_init(void) +{ + int err = 0; + uint64_t tmp = 0; + + pr_info(KERN_INFO "\n"); + pr_info(KERN_INFO "=================================================\n"); + + if (dev < 0) { + pr_info("Please specify a valid mtd-device via module parameter\n"); + return -EINVAL; + } + + pr_info("MTD device: %d\n", dev); + + mtd = get_mtd_device(NULL, dev); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + pr_info("error: cannot get MTD device\n"); + return err; + } + mtd_part = (struct mtd_part *)mtd; + + if (mtd->type != MTD_NANDFLASH) { + pr_info("this test requires NAND flash\n"); + goto out; + } + + tmp = mtd->size; + do_div(tmp, mtd->erasesize); + ebcnt = tmp; + pgcnt = mtd->erasesize / mtd->writesize; + pgsize = mtd->writesize; + + pr_info("MTD device size %llu, eraseblock size %u, " + "page size %u, count of eraseblocks %u, pages per " + "eraseblock %u, OOB size %u\n", + (unsigned long long)mtd->size, mtd->erasesize, + mtd->writesize, ebcnt, pgcnt, mtd->oobsize); + + err = mtd_test_scan_bad_block(); + if (err) + goto out; + + err = mtd_test_bitfliptest(); + if(err) { + pr_err("bitfliptest failed!\n"); + goto out; + } + +out: + pr_info(KERN_INFO "=================================================\n"); + if(bbt != NULL) + vfree(bbt); + put_mtd_device(mtd); + + return -1; +} + +static void __exit nandflash_bitfliptest_exit(void) +{ + return; +} + +module_init(nandflash_bitfliptest_init); +module_exit(nandflash_bitfliptest_exit); + +MODULE_DESCRIPTION("mtd_bitflip_test"); +MODULE_AUTHOR("ming.tang@spreadtrum.com"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/sprd_mtdtest/nandflash_bitfliptest/readme.txt b/drivers/mtd/tests/sprd_mtdtest/nandflash_bitfliptest/readme.txt new file mode 100755 index 00000000..760e164f --- /dev/null +++ b/drivers/mtd/tests/sprd_mtdtest/nandflash_bitfliptest/readme.txt @@ -0,0 +1,9 @@ +bitfliptest——位翻转测试,用于测试当前系统在做数据存取时发生bit-flip的概率,测试时长由当前系统的稳定性和设定的阀值共同决定。 + +参数说明: +dev:用于指定测试使用的mtd分区,默认值为4 +threshold:用于指定bit-flip的阀值,即当bit-flip的总数达到阀值即会停止,默认值为100。 + +使用方法: +root后加载测试模块并传入参数,测试结果通过kernel log输出; +cmd:insmod nandflash_bitfliptest.ko dev=4 threshold=100 \ No newline at end of file -- cgit v1.3.1