xref: /linux/drivers/mtd/tests/stresstest.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
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