1*4cd10358SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 27163cea1SArtem Bityutskiy /* 37163cea1SArtem Bityutskiy * Copyright (C) 2006-2008 Nokia Corporation 47163cea1SArtem Bityutskiy * 57163cea1SArtem Bityutskiy * Test random reads, writes and erases on MTD device. 67163cea1SArtem Bityutskiy * 77163cea1SArtem Bityutskiy * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> 87163cea1SArtem Bityutskiy */ 97163cea1SArtem Bityutskiy 10ae0086cfSVikram Narayanan #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 11ae0086cfSVikram Narayanan 127163cea1SArtem Bityutskiy #include <linux/init.h> 137163cea1SArtem Bityutskiy #include <linux/module.h> 147163cea1SArtem Bityutskiy #include <linux/moduleparam.h> 157163cea1SArtem Bityutskiy #include <linux/err.h> 167163cea1SArtem Bityutskiy #include <linux/mtd/mtd.h> 175a0e3ad6STejun Heo #include <linux/slab.h> 187163cea1SArtem Bityutskiy #include <linux/sched.h> 197163cea1SArtem Bityutskiy #include <linux/vmalloc.h> 20bfea1d4eSArtem Bityutskiy #include <linux/random.h> 217163cea1SArtem Bityutskiy 225a78df69SAkinobu Mita #include "mtd_test.h" 235a78df69SAkinobu Mita 247406060eSWolfram Sang static int dev = -EINVAL; 257163cea1SArtem Bityutskiy module_param(dev, int, S_IRUGO); 267163cea1SArtem Bityutskiy MODULE_PARM_DESC(dev, "MTD device number to use"); 277163cea1SArtem Bityutskiy 287163cea1SArtem Bityutskiy static int count = 10000; 297163cea1SArtem Bityutskiy module_param(count, int, S_IRUGO); 307163cea1SArtem Bityutskiy MODULE_PARM_DESC(count, "Number of operations to do (default is 10000)"); 317163cea1SArtem Bityutskiy 327163cea1SArtem Bityutskiy static struct mtd_info *mtd; 337163cea1SArtem Bityutskiy static unsigned char *writebuf; 347163cea1SArtem Bityutskiy static unsigned char *readbuf; 357163cea1SArtem Bityutskiy static unsigned char *bbt; 367163cea1SArtem Bityutskiy static int *offsets; 377163cea1SArtem Bityutskiy 387163cea1SArtem Bityutskiy static int pgsize; 397163cea1SArtem Bityutskiy static int bufsize; 407163cea1SArtem Bityutskiy static int ebcnt; 417163cea1SArtem Bityutskiy static int pgcnt; 427163cea1SArtem Bityutskiy 437163cea1SArtem Bityutskiy static int rand_eb(void) 447163cea1SArtem Bityutskiy { 45bfea1d4eSArtem Bityutskiy unsigned int eb; 467163cea1SArtem Bityutskiy 477163cea1SArtem Bityutskiy again: 48aca662a3SAkinobu Mita eb = prandom_u32(); 497163cea1SArtem Bityutskiy /* Read or write up 2 eraseblocks at a time - hence 'ebcnt - 1' */ 507163cea1SArtem Bityutskiy eb %= (ebcnt - 1); 517163cea1SArtem Bityutskiy if (bbt[eb]) 527163cea1SArtem Bityutskiy goto again; 537163cea1SArtem Bityutskiy return eb; 547163cea1SArtem Bityutskiy } 557163cea1SArtem Bityutskiy 567163cea1SArtem Bityutskiy static int rand_offs(void) 577163cea1SArtem Bityutskiy { 58bfea1d4eSArtem Bityutskiy unsigned int offs; 597163cea1SArtem Bityutskiy 60aca662a3SAkinobu Mita offs = prandom_u32(); 617163cea1SArtem Bityutskiy offs %= bufsize; 627163cea1SArtem Bityutskiy return offs; 637163cea1SArtem Bityutskiy } 647163cea1SArtem Bityutskiy 657163cea1SArtem Bityutskiy static int rand_len(int offs) 667163cea1SArtem Bityutskiy { 67bfea1d4eSArtem Bityutskiy unsigned int len; 687163cea1SArtem Bityutskiy 69aca662a3SAkinobu Mita len = prandom_u32(); 707163cea1SArtem Bityutskiy len %= (bufsize - offs); 717163cea1SArtem Bityutskiy return len; 727163cea1SArtem Bityutskiy } 737163cea1SArtem Bityutskiy 747163cea1SArtem Bityutskiy static int do_read(void) 757163cea1SArtem Bityutskiy { 767163cea1SArtem Bityutskiy int eb = rand_eb(); 777163cea1SArtem Bityutskiy int offs = rand_offs(); 78abc173adSAkinobu Mita int len = rand_len(offs); 797163cea1SArtem Bityutskiy loff_t addr; 807163cea1SArtem Bityutskiy 817163cea1SArtem Bityutskiy if (bbt[eb + 1]) { 827163cea1SArtem Bityutskiy if (offs >= mtd->erasesize) 837163cea1SArtem Bityutskiy offs -= mtd->erasesize; 847163cea1SArtem Bityutskiy if (offs + len > mtd->erasesize) 857163cea1SArtem Bityutskiy len = mtd->erasesize - offs; 867163cea1SArtem Bityutskiy } 87b9da8baeSBrian Norris addr = (loff_t)eb * mtd->erasesize + offs; 88abc173adSAkinobu Mita return mtdtest_read(mtd, addr, len, readbuf); 897163cea1SArtem Bityutskiy } 907163cea1SArtem Bityutskiy 917163cea1SArtem Bityutskiy static int do_write(void) 927163cea1SArtem Bityutskiy { 937163cea1SArtem Bityutskiy int eb = rand_eb(), offs, err, len; 947163cea1SArtem Bityutskiy loff_t addr; 957163cea1SArtem Bityutskiy 967163cea1SArtem Bityutskiy offs = offsets[eb]; 977163cea1SArtem Bityutskiy if (offs >= mtd->erasesize) { 985a78df69SAkinobu Mita err = mtdtest_erase_eraseblock(mtd, eb); 997163cea1SArtem Bityutskiy if (err) 1007163cea1SArtem Bityutskiy return err; 1017163cea1SArtem Bityutskiy offs = offsets[eb] = 0; 1027163cea1SArtem Bityutskiy } 1037163cea1SArtem Bityutskiy len = rand_len(offs); 1047163cea1SArtem Bityutskiy len = ((len + pgsize - 1) / pgsize) * pgsize; 1057163cea1SArtem Bityutskiy if (offs + len > mtd->erasesize) { 1067163cea1SArtem Bityutskiy if (bbt[eb + 1]) 1077163cea1SArtem Bityutskiy len = mtd->erasesize - offs; 1087163cea1SArtem Bityutskiy else { 1095a78df69SAkinobu Mita err = mtdtest_erase_eraseblock(mtd, eb + 1); 1107163cea1SArtem Bityutskiy if (err) 1117163cea1SArtem Bityutskiy return err; 1127163cea1SArtem Bityutskiy offsets[eb + 1] = 0; 1137163cea1SArtem Bityutskiy } 1147163cea1SArtem Bityutskiy } 115b9da8baeSBrian Norris addr = (loff_t)eb * mtd->erasesize + offs; 1165a78df69SAkinobu Mita err = mtdtest_write(mtd, addr, len, writebuf); 1178a9f4aa3SAkinobu Mita if (unlikely(err)) 1187163cea1SArtem Bityutskiy return err; 1197163cea1SArtem Bityutskiy offs += len; 1207163cea1SArtem Bityutskiy while (offs > mtd->erasesize) { 1217163cea1SArtem Bityutskiy offsets[eb++] = mtd->erasesize; 1227163cea1SArtem Bityutskiy offs -= mtd->erasesize; 1237163cea1SArtem Bityutskiy } 1247163cea1SArtem Bityutskiy offsets[eb] = offs; 1257163cea1SArtem Bityutskiy return 0; 1267163cea1SArtem Bityutskiy } 1277163cea1SArtem Bityutskiy 1287163cea1SArtem Bityutskiy static int do_operation(void) 1297163cea1SArtem Bityutskiy { 130aca662a3SAkinobu Mita if (prandom_u32() & 1) 1317163cea1SArtem Bityutskiy return do_read(); 1327163cea1SArtem Bityutskiy else 1337163cea1SArtem Bityutskiy return do_write(); 1347163cea1SArtem Bityutskiy } 1357163cea1SArtem Bityutskiy 1367163cea1SArtem Bityutskiy static int __init mtd_stresstest_init(void) 1377163cea1SArtem Bityutskiy { 1387163cea1SArtem Bityutskiy int err; 1397163cea1SArtem Bityutskiy int i, op; 1407163cea1SArtem Bityutskiy uint64_t tmp; 1417163cea1SArtem Bityutskiy 1427163cea1SArtem Bityutskiy printk(KERN_INFO "\n"); 1437163cea1SArtem Bityutskiy printk(KERN_INFO "=================================================\n"); 1447406060eSWolfram Sang 1457406060eSWolfram Sang if (dev < 0) { 146064a7694SMasanari Iida pr_info("Please specify a valid mtd-device via module parameter\n"); 147ae0086cfSVikram Narayanan pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); 1487406060eSWolfram Sang return -EINVAL; 1497406060eSWolfram Sang } 1507406060eSWolfram Sang 151ae0086cfSVikram Narayanan pr_info("MTD device: %d\n", dev); 1527163cea1SArtem Bityutskiy 1537163cea1SArtem Bityutskiy mtd = get_mtd_device(NULL, dev); 1547163cea1SArtem Bityutskiy if (IS_ERR(mtd)) { 1557163cea1SArtem Bityutskiy err = PTR_ERR(mtd); 156ae0086cfSVikram Narayanan pr_err("error: cannot get MTD device\n"); 1577163cea1SArtem Bityutskiy return err; 1587163cea1SArtem Bityutskiy } 1597163cea1SArtem Bityutskiy 1607163cea1SArtem Bityutskiy if (mtd->writesize == 1) { 161ae0086cfSVikram Narayanan pr_info("not NAND flash, assume page size is 512 " 1627163cea1SArtem Bityutskiy "bytes.\n"); 1637163cea1SArtem Bityutskiy pgsize = 512; 1647163cea1SArtem Bityutskiy } else 1657163cea1SArtem Bityutskiy pgsize = mtd->writesize; 1667163cea1SArtem Bityutskiy 1677163cea1SArtem Bityutskiy tmp = mtd->size; 1687163cea1SArtem Bityutskiy do_div(tmp, mtd->erasesize); 1697163cea1SArtem Bityutskiy ebcnt = tmp; 170f5e2bae0SMorten Thunberg Svendsen pgcnt = mtd->erasesize / pgsize; 1717163cea1SArtem Bityutskiy 172ae0086cfSVikram Narayanan pr_info("MTD device size %llu, eraseblock size %u, " 1737163cea1SArtem Bityutskiy "page size %u, count of eraseblocks %u, pages per " 1747163cea1SArtem Bityutskiy "eraseblock %u, OOB size %u\n", 1757163cea1SArtem Bityutskiy (unsigned long long)mtd->size, mtd->erasesize, 1767163cea1SArtem Bityutskiy pgsize, ebcnt, pgcnt, mtd->oobsize); 1777163cea1SArtem Bityutskiy 1782f4478ccSWolfram Sang if (ebcnt < 2) { 179ae0086cfSVikram Narayanan pr_err("error: need at least 2 eraseblocks\n"); 1802f4478ccSWolfram Sang err = -ENOSPC; 1812f4478ccSWolfram Sang goto out_put_mtd; 1822f4478ccSWolfram Sang } 1832f4478ccSWolfram Sang 1847163cea1SArtem Bityutskiy /* Read or write up 2 eraseblocks at a time */ 1857163cea1SArtem Bityutskiy bufsize = mtd->erasesize * 2; 1867163cea1SArtem Bityutskiy 1877163cea1SArtem Bityutskiy err = -ENOMEM; 1887163cea1SArtem Bityutskiy readbuf = vmalloc(bufsize); 1897163cea1SArtem Bityutskiy writebuf = vmalloc(bufsize); 1906da2ec56SKees Cook offsets = kmalloc_array(ebcnt, sizeof(int), GFP_KERNEL); 19133777e66SBrian Norris if (!readbuf || !writebuf || !offsets) 1927163cea1SArtem Bityutskiy goto out; 1937163cea1SArtem Bityutskiy for (i = 0; i < ebcnt; i++) 1947163cea1SArtem Bityutskiy offsets[i] = mtd->erasesize; 1954debec7aSAkinobu Mita prandom_bytes(writebuf, bufsize); 1967163cea1SArtem Bityutskiy 1975a78df69SAkinobu Mita bbt = kzalloc(ebcnt, GFP_KERNEL); 1985a78df69SAkinobu Mita if (!bbt) 1995a78df69SAkinobu Mita goto out; 2005a78df69SAkinobu Mita err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); 2017163cea1SArtem Bityutskiy if (err) 2027163cea1SArtem Bityutskiy goto out; 2037163cea1SArtem Bityutskiy 2047163cea1SArtem Bityutskiy /* Do operations */ 205ae0086cfSVikram Narayanan pr_info("doing operations\n"); 2067163cea1SArtem Bityutskiy for (op = 0; op < count; op++) { 2077163cea1SArtem Bityutskiy if ((op & 1023) == 0) 208ae0086cfSVikram Narayanan pr_info("%d operations done\n", op); 2097163cea1SArtem Bityutskiy err = do_operation(); 2107163cea1SArtem Bityutskiy if (err) 2117163cea1SArtem Bityutskiy goto out; 2122a6a28e7SRichard Weinberger 2132a6a28e7SRichard Weinberger err = mtdtest_relax(); 2142a6a28e7SRichard Weinberger if (err) 2152a6a28e7SRichard Weinberger goto out; 2167163cea1SArtem Bityutskiy } 217ae0086cfSVikram Narayanan pr_info("finished, %d operations done\n", op); 2187163cea1SArtem Bityutskiy 2197163cea1SArtem Bityutskiy out: 2207163cea1SArtem Bityutskiy kfree(offsets); 2217163cea1SArtem Bityutskiy kfree(bbt); 2227163cea1SArtem Bityutskiy vfree(writebuf); 2237163cea1SArtem Bityutskiy vfree(readbuf); 2242f4478ccSWolfram Sang out_put_mtd: 2257163cea1SArtem Bityutskiy put_mtd_device(mtd); 2267163cea1SArtem Bityutskiy if (err) 227ae0086cfSVikram Narayanan pr_info("error %d occurred\n", err); 2287163cea1SArtem Bityutskiy printk(KERN_INFO "=================================================\n"); 2297163cea1SArtem Bityutskiy return err; 2307163cea1SArtem Bityutskiy } 2317163cea1SArtem Bityutskiy module_init(mtd_stresstest_init); 2327163cea1SArtem Bityutskiy 2337163cea1SArtem Bityutskiy static void __exit mtd_stresstest_exit(void) 2347163cea1SArtem Bityutskiy { 2357163cea1SArtem Bityutskiy return; 2367163cea1SArtem Bityutskiy } 2377163cea1SArtem Bityutskiy module_exit(mtd_stresstest_exit); 2387163cea1SArtem Bityutskiy 2397163cea1SArtem Bityutskiy MODULE_DESCRIPTION("Stress test module"); 2407163cea1SArtem Bityutskiy MODULE_AUTHOR("Adrian Hunter"); 2417163cea1SArtem Bityutskiy MODULE_LICENSE("GPL"); 242