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