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 /* 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 55 static int rand_offs(void) 56 { 57 return get_random_u32_below(bufsize); 58 } 59 60 static int rand_len(int offs) 61 { 62 return get_random_u32_below(bufsize - offs); 63 } 64 65 static 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 82 static 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 119 static int do_operation(void) 120 { 121 if (get_random_u32_below(2)) 122 return do_read(); 123 else 124 return do_write(); 125 } 126 127 static 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 } else 156 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 210 out: 211 kfree(offsets); 212 kfree(bbt); 213 vfree(writebuf); 214 vfree(readbuf); 215 out_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 } 222 module_init(mtd_stresstest_init); 223 224 static void __exit mtd_stresstest_exit(void) 225 { 226 return; 227 } 228 module_exit(mtd_stresstest_exit); 229 230 MODULE_DESCRIPTION("Stress test module"); 231 MODULE_AUTHOR("Adrian Hunter"); 232 MODULE_LICENSE("GPL"); 233