Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.31-rc2 330 lines 7.4 kB view raw
1/* 2 * Copyright (C) 2006-2008 Nokia Corporation 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 as published by 6 * the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, but WITHOUT 9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 * more details. 12 * 13 * You should have received a copy of the GNU General Public License along with 14 * this program; see the file COPYING. If not, write to the Free Software 15 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 16 * 17 * Test random reads, writes and erases on MTD device. 18 * 19 * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> 20 */ 21 22#include <linux/init.h> 23#include <linux/module.h> 24#include <linux/moduleparam.h> 25#include <linux/err.h> 26#include <linux/mtd/mtd.h> 27#include <linux/sched.h> 28#include <linux/vmalloc.h> 29 30#define PRINT_PREF KERN_INFO "mtd_stresstest: " 31 32static int dev; 33module_param(dev, int, S_IRUGO); 34MODULE_PARM_DESC(dev, "MTD device number to use"); 35 36static int count = 10000; 37module_param(count, int, S_IRUGO); 38MODULE_PARM_DESC(count, "Number of operations to do (default is 10000)"); 39 40static struct mtd_info *mtd; 41static unsigned char *writebuf; 42static unsigned char *readbuf; 43static unsigned char *bbt; 44static int *offsets; 45 46static int pgsize; 47static int bufsize; 48static int ebcnt; 49static int pgcnt; 50static unsigned long next = 1; 51 52static inline unsigned int simple_rand(void) 53{ 54 next = next * 1103515245 + 12345; 55 return (unsigned int)((next / 65536) % 32768); 56} 57 58static inline void simple_srand(unsigned long seed) 59{ 60 next = seed; 61} 62 63static int rand_eb(void) 64{ 65 int eb; 66 67again: 68 if (ebcnt < 32768) 69 eb = simple_rand(); 70 else 71 eb = (simple_rand() << 15) | simple_rand(); 72 /* Read or write up 2 eraseblocks at a time - hence 'ebcnt - 1' */ 73 eb %= (ebcnt - 1); 74 if (bbt[eb]) 75 goto again; 76 return eb; 77} 78 79static int rand_offs(void) 80{ 81 int offs; 82 83 if (bufsize < 32768) 84 offs = simple_rand(); 85 else 86 offs = (simple_rand() << 15) | simple_rand(); 87 offs %= bufsize; 88 return offs; 89} 90 91static int rand_len(int offs) 92{ 93 int len; 94 95 if (bufsize < 32768) 96 len = simple_rand(); 97 else 98 len = (simple_rand() << 15) | simple_rand(); 99 len %= (bufsize - offs); 100 return len; 101} 102 103static int erase_eraseblock(int ebnum) 104{ 105 int err; 106 struct erase_info ei; 107 loff_t addr = ebnum * mtd->erasesize; 108 109 memset(&ei, 0, sizeof(struct erase_info)); 110 ei.mtd = mtd; 111 ei.addr = addr; 112 ei.len = mtd->erasesize; 113 114 err = mtd->erase(mtd, &ei); 115 if (unlikely(err)) { 116 printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); 117 return err; 118 } 119 120 if (unlikely(ei.state == MTD_ERASE_FAILED)) { 121 printk(PRINT_PREF "some erase error occurred at EB %d\n", 122 ebnum); 123 return -EIO; 124 } 125 126 return 0; 127} 128 129static int is_block_bad(int ebnum) 130{ 131 loff_t addr = ebnum * mtd->erasesize; 132 int ret; 133 134 ret = mtd->block_isbad(mtd, addr); 135 if (ret) 136 printk(PRINT_PREF "block %d is bad\n", ebnum); 137 return ret; 138} 139 140static int do_read(void) 141{ 142 size_t read = 0; 143 int eb = rand_eb(); 144 int offs = rand_offs(); 145 int len = rand_len(offs), err; 146 loff_t addr; 147 148 if (bbt[eb + 1]) { 149 if (offs >= mtd->erasesize) 150 offs -= mtd->erasesize; 151 if (offs + len > mtd->erasesize) 152 len = mtd->erasesize - offs; 153 } 154 addr = eb * mtd->erasesize + offs; 155 err = mtd->read(mtd, addr, len, &read, readbuf); 156 if (err == -EUCLEAN) 157 err = 0; 158 if (unlikely(err || read != len)) { 159 printk(PRINT_PREF "error: read failed at 0x%llx\n", 160 (long long)addr); 161 if (!err) 162 err = -EINVAL; 163 return err; 164 } 165 return 0; 166} 167 168static int do_write(void) 169{ 170 int eb = rand_eb(), offs, err, len; 171 size_t written = 0; 172 loff_t addr; 173 174 offs = offsets[eb]; 175 if (offs >= mtd->erasesize) { 176 err = erase_eraseblock(eb); 177 if (err) 178 return err; 179 offs = offsets[eb] = 0; 180 } 181 len = rand_len(offs); 182 len = ((len + pgsize - 1) / pgsize) * pgsize; 183 if (offs + len > mtd->erasesize) { 184 if (bbt[eb + 1]) 185 len = mtd->erasesize - offs; 186 else { 187 err = erase_eraseblock(eb + 1); 188 if (err) 189 return err; 190 offsets[eb + 1] = 0; 191 } 192 } 193 addr = eb * mtd->erasesize + offs; 194 err = mtd->write(mtd, addr, len, &written, writebuf); 195 if (unlikely(err || written != len)) { 196 printk(PRINT_PREF "error: write failed at 0x%llx\n", 197 (long long)addr); 198 if (!err) 199 err = -EINVAL; 200 return err; 201 } 202 offs += len; 203 while (offs > mtd->erasesize) { 204 offsets[eb++] = mtd->erasesize; 205 offs -= mtd->erasesize; 206 } 207 offsets[eb] = offs; 208 return 0; 209} 210 211static int do_operation(void) 212{ 213 if (simple_rand() & 1) 214 return do_read(); 215 else 216 return do_write(); 217} 218 219static int scan_for_bad_eraseblocks(void) 220{ 221 int i, bad = 0; 222 223 bbt = kmalloc(ebcnt, GFP_KERNEL); 224 if (!bbt) { 225 printk(PRINT_PREF "error: cannot allocate memory\n"); 226 return -ENOMEM; 227 } 228 memset(bbt, 0 , ebcnt); 229 230 printk(PRINT_PREF "scanning for bad eraseblocks\n"); 231 for (i = 0; i < ebcnt; ++i) { 232 bbt[i] = is_block_bad(i) ? 1 : 0; 233 if (bbt[i]) 234 bad += 1; 235 cond_resched(); 236 } 237 printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); 238 return 0; 239} 240 241static int __init mtd_stresstest_init(void) 242{ 243 int err; 244 int i, op; 245 uint64_t tmp; 246 247 printk(KERN_INFO "\n"); 248 printk(KERN_INFO "=================================================\n"); 249 printk(PRINT_PREF "MTD device: %d\n", dev); 250 251 mtd = get_mtd_device(NULL, dev); 252 if (IS_ERR(mtd)) { 253 err = PTR_ERR(mtd); 254 printk(PRINT_PREF "error: cannot get MTD device\n"); 255 return err; 256 } 257 258 if (mtd->writesize == 1) { 259 printk(PRINT_PREF "not NAND flash, assume page size is 512 " 260 "bytes.\n"); 261 pgsize = 512; 262 } else 263 pgsize = mtd->writesize; 264 265 tmp = mtd->size; 266 do_div(tmp, mtd->erasesize); 267 ebcnt = tmp; 268 pgcnt = mtd->erasesize / mtd->writesize; 269 270 printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " 271 "page size %u, count of eraseblocks %u, pages per " 272 "eraseblock %u, OOB size %u\n", 273 (unsigned long long)mtd->size, mtd->erasesize, 274 pgsize, ebcnt, pgcnt, mtd->oobsize); 275 276 /* Read or write up 2 eraseblocks at a time */ 277 bufsize = mtd->erasesize * 2; 278 279 err = -ENOMEM; 280 readbuf = vmalloc(bufsize); 281 writebuf = vmalloc(bufsize); 282 offsets = kmalloc(ebcnt * sizeof(int), GFP_KERNEL); 283 if (!readbuf || !writebuf || !offsets) { 284 printk(PRINT_PREF "error: cannot allocate memory\n"); 285 goto out; 286 } 287 for (i = 0; i < ebcnt; i++) 288 offsets[i] = mtd->erasesize; 289 simple_srand(current->pid); 290 for (i = 0; i < bufsize; i++) 291 writebuf[i] = simple_rand(); 292 293 err = scan_for_bad_eraseblocks(); 294 if (err) 295 goto out; 296 297 /* Do operations */ 298 printk(PRINT_PREF "doing operations\n"); 299 for (op = 0; op < count; op++) { 300 if ((op & 1023) == 0) 301 printk(PRINT_PREF "%d operations done\n", op); 302 err = do_operation(); 303 if (err) 304 goto out; 305 cond_resched(); 306 } 307 printk(PRINT_PREF "finished, %d operations done\n", op); 308 309out: 310 kfree(offsets); 311 kfree(bbt); 312 vfree(writebuf); 313 vfree(readbuf); 314 put_mtd_device(mtd); 315 if (err) 316 printk(PRINT_PREF "error %d occurred\n", err); 317 printk(KERN_INFO "=================================================\n"); 318 return err; 319} 320module_init(mtd_stresstest_init); 321 322static void __exit mtd_stresstest_exit(void) 323{ 324 return; 325} 326module_exit(mtd_stresstest_exit); 327 328MODULE_DESCRIPTION("Stress test module"); 329MODULE_AUTHOR("Adrian Hunter"); 330MODULE_LICENSE("GPL");