233 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2006-2008 Nokia Corporation4 *5 * Test random reads, writes and erases on MTD device.6 *7 * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>8 */9 10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt11 12#include <linux/init.h>13#include <linux/module.h>14#include <linux/moduleparam.h>15#include <linux/err.h>16#include <linux/mtd/mtd.h>17#include <linux/slab.h>18#include <linux/sched.h>19#include <linux/vmalloc.h>20#include <linux/random.h>21 22#include "mtd_test.h"23 24static int dev = -EINVAL;25module_param(dev, int, S_IRUGO);26MODULE_PARM_DESC(dev, "MTD device number to use");27 28static int count = 10000;29module_param(count, int, S_IRUGO);30MODULE_PARM_DESC(count, "Number of operations to do (default is 10000)");31 32static struct mtd_info *mtd;33static unsigned char *writebuf;34static unsigned char *readbuf;35static unsigned char *bbt;36static int *offsets;37 38static int pgsize;39static int bufsize;40static int ebcnt;41static int pgcnt;42 43static int rand_eb(void)44{45 unsigned int eb;46 47again:48 /* Read or write up 2 eraseblocks at a time - hence 'ebcnt - 1' */49 eb = get_random_u32_below(ebcnt - 1);50 if (bbt[eb])51 goto again;52 return eb;53}54 55static int rand_offs(void)56{57 return get_random_u32_below(bufsize);58}59 60static int rand_len(int offs)61{62 return get_random_u32_below(bufsize - offs);63}64 65static int do_read(void)66{67 int eb = rand_eb();68 int offs = rand_offs();69 int len = rand_len(offs);70 loff_t addr;71 72 if (bbt[eb + 1]) {73 if (offs >= mtd->erasesize)74 offs -= mtd->erasesize;75 if (offs + len > mtd->erasesize)76 len = mtd->erasesize - offs;77 }78 addr = (loff_t)eb * mtd->erasesize + offs;79 return mtdtest_read(mtd, addr, len, readbuf);80}81 82static int do_write(void)83{84 int eb = rand_eb(), offs, err, len;85 loff_t addr;86 87 offs = offsets[eb];88 if (offs >= mtd->erasesize) {89 err = mtdtest_erase_eraseblock(mtd, eb);90 if (err)91 return err;92 offs = offsets[eb] = 0;93 }94 len = rand_len(offs);95 len = ((len + pgsize - 1) / pgsize) * pgsize;96 if (offs + len > mtd->erasesize) {97 if (bbt[eb + 1])98 len = mtd->erasesize - offs;99 else {100 err = mtdtest_erase_eraseblock(mtd, eb + 1);101 if (err)102 return err;103 offsets[eb + 1] = 0;104 }105 }106 addr = (loff_t)eb * mtd->erasesize + offs;107 err = mtdtest_write(mtd, addr, len, writebuf);108 if (unlikely(err))109 return err;110 offs += len;111 while (offs > mtd->erasesize) {112 offsets[eb++] = mtd->erasesize;113 offs -= mtd->erasesize;114 }115 offsets[eb] = offs;116 return 0;117}118 119static int do_operation(void)120{121 if (get_random_u32_below(2))122 return do_read();123 else124 return do_write();125}126 127static int __init mtd_stresstest_init(void)128{129 int err;130 int i, op;131 uint64_t tmp;132 133 printk(KERN_INFO "\n");134 printk(KERN_INFO "=================================================\n");135 136 if (dev < 0) {137 pr_info("Please specify a valid mtd-device via module parameter\n");138 pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");139 return -EINVAL;140 }141 142 pr_info("MTD device: %d\n", dev);143 144 mtd = get_mtd_device(NULL, dev);145 if (IS_ERR(mtd)) {146 err = PTR_ERR(mtd);147 pr_err("error: cannot get MTD device\n");148 return err;149 }150 151 if (mtd->writesize == 1) {152 pr_info("not NAND flash, assume page size is 512 "153 "bytes.\n");154 pgsize = 512;155 } else156 pgsize = mtd->writesize;157 158 tmp = mtd->size;159 do_div(tmp, mtd->erasesize);160 ebcnt = tmp;161 pgcnt = mtd->erasesize / pgsize;162 163 pr_info("MTD device size %llu, eraseblock size %u, "164 "page size %u, count of eraseblocks %u, pages per "165 "eraseblock %u, OOB size %u\n",166 (unsigned long long)mtd->size, mtd->erasesize,167 pgsize, ebcnt, pgcnt, mtd->oobsize);168 169 if (ebcnt < 2) {170 pr_err("error: need at least 2 eraseblocks\n");171 err = -ENOSPC;172 goto out_put_mtd;173 }174 175 /* Read or write up 2 eraseblocks at a time */176 bufsize = mtd->erasesize * 2;177 178 err = -ENOMEM;179 readbuf = vmalloc(bufsize);180 writebuf = vmalloc(bufsize);181 offsets = kmalloc_array(ebcnt, sizeof(int), GFP_KERNEL);182 if (!readbuf || !writebuf || !offsets)183 goto out;184 for (i = 0; i < ebcnt; i++)185 offsets[i] = mtd->erasesize;186 get_random_bytes(writebuf, bufsize);187 188 bbt = kzalloc(ebcnt, GFP_KERNEL);189 if (!bbt)190 goto out;191 err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);192 if (err)193 goto out;194 195 /* Do operations */196 pr_info("doing operations\n");197 for (op = 0; op < count; op++) {198 if ((op & 1023) == 0)199 pr_info("%d operations done\n", op);200 err = do_operation();201 if (err)202 goto out;203 204 err = mtdtest_relax();205 if (err)206 goto out;207 }208 pr_info("finished, %d operations done\n", op);209 210out:211 kfree(offsets);212 kfree(bbt);213 vfree(writebuf);214 vfree(readbuf);215out_put_mtd:216 put_mtd_device(mtd);217 if (err)218 pr_info("error %d occurred\n", err);219 printk(KERN_INFO "=================================================\n");220 return err;221}222module_init(mtd_stresstest_init);223 224static void __exit mtd_stresstest_exit(void)225{226 return;227}228module_exit(mtd_stresstest_exit);229 230MODULE_DESCRIPTION("Stress test module");231MODULE_AUTHOR("Adrian Hunter");232MODULE_LICENSE("GPL");233