Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.31-rc9 502 lines 11 kB view raw
1/* 2 * Copyright (C) 2007 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 read and write speed of a 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 29#define PRINT_PREF KERN_INFO "mtd_speedtest: " 30 31static int dev; 32module_param(dev, int, S_IRUGO); 33MODULE_PARM_DESC(dev, "MTD device number to use"); 34 35static struct mtd_info *mtd; 36static unsigned char *iobuf; 37static unsigned char *bbt; 38 39static int pgsize; 40static int ebcnt; 41static int pgcnt; 42static int goodebcnt; 43static struct timeval start, finish; 44static unsigned long next = 1; 45 46static inline unsigned int simple_rand(void) 47{ 48 next = next * 1103515245 + 12345; 49 return (unsigned int)((next / 65536) % 32768); 50} 51 52static inline void simple_srand(unsigned long seed) 53{ 54 next = seed; 55} 56 57static void set_random_data(unsigned char *buf, size_t len) 58{ 59 size_t i; 60 61 for (i = 0; i < len; ++i) 62 buf[i] = simple_rand(); 63} 64 65static int erase_eraseblock(int ebnum) 66{ 67 int err; 68 struct erase_info ei; 69 loff_t addr = ebnum * mtd->erasesize; 70 71 memset(&ei, 0, sizeof(struct erase_info)); 72 ei.mtd = mtd; 73 ei.addr = addr; 74 ei.len = mtd->erasesize; 75 76 err = mtd->erase(mtd, &ei); 77 if (err) { 78 printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); 79 return err; 80 } 81 82 if (ei.state == MTD_ERASE_FAILED) { 83 printk(PRINT_PREF "some erase error occurred at EB %d\n", 84 ebnum); 85 return -EIO; 86 } 87 88 return 0; 89} 90 91static int erase_whole_device(void) 92{ 93 int err; 94 unsigned int i; 95 96 for (i = 0; i < ebcnt; ++i) { 97 if (bbt[i]) 98 continue; 99 err = erase_eraseblock(i); 100 if (err) 101 return err; 102 cond_resched(); 103 } 104 return 0; 105} 106 107static int write_eraseblock(int ebnum) 108{ 109 size_t written = 0; 110 int err = 0; 111 loff_t addr = ebnum * mtd->erasesize; 112 113 err = mtd->write(mtd, addr, mtd->erasesize, &written, iobuf); 114 if (err || written != mtd->erasesize) { 115 printk(PRINT_PREF "error: write failed at %#llx\n", addr); 116 if (!err) 117 err = -EINVAL; 118 } 119 120 return err; 121} 122 123static int write_eraseblock_by_page(int ebnum) 124{ 125 size_t written = 0; 126 int i, err = 0; 127 loff_t addr = ebnum * mtd->erasesize; 128 void *buf = iobuf; 129 130 for (i = 0; i < pgcnt; i++) { 131 err = mtd->write(mtd, addr, pgsize, &written, buf); 132 if (err || written != pgsize) { 133 printk(PRINT_PREF "error: write failed at %#llx\n", 134 addr); 135 if (!err) 136 err = -EINVAL; 137 break; 138 } 139 addr += pgsize; 140 buf += pgsize; 141 } 142 143 return err; 144} 145 146static int write_eraseblock_by_2pages(int ebnum) 147{ 148 size_t written = 0, sz = pgsize * 2; 149 int i, n = pgcnt / 2, err = 0; 150 loff_t addr = ebnum * mtd->erasesize; 151 void *buf = iobuf; 152 153 for (i = 0; i < n; i++) { 154 err = mtd->write(mtd, addr, sz, &written, buf); 155 if (err || written != sz) { 156 printk(PRINT_PREF "error: write failed at %#llx\n", 157 addr); 158 if (!err) 159 err = -EINVAL; 160 return err; 161 } 162 addr += sz; 163 buf += sz; 164 } 165 if (pgcnt % 2) { 166 err = mtd->write(mtd, addr, pgsize, &written, buf); 167 if (err || written != pgsize) { 168 printk(PRINT_PREF "error: write failed at %#llx\n", 169 addr); 170 if (!err) 171 err = -EINVAL; 172 } 173 } 174 175 return err; 176} 177 178static int read_eraseblock(int ebnum) 179{ 180 size_t read = 0; 181 int err = 0; 182 loff_t addr = ebnum * mtd->erasesize; 183 184 err = mtd->read(mtd, addr, mtd->erasesize, &read, iobuf); 185 /* Ignore corrected ECC errors */ 186 if (err == -EUCLEAN) 187 err = 0; 188 if (err || read != mtd->erasesize) { 189 printk(PRINT_PREF "error: read failed at %#llx\n", addr); 190 if (!err) 191 err = -EINVAL; 192 } 193 194 return err; 195} 196 197static int read_eraseblock_by_page(int ebnum) 198{ 199 size_t read = 0; 200 int i, err = 0; 201 loff_t addr = ebnum * mtd->erasesize; 202 void *buf = iobuf; 203 204 for (i = 0; i < pgcnt; i++) { 205 err = mtd->read(mtd, addr, pgsize, &read, buf); 206 /* Ignore corrected ECC errors */ 207 if (err == -EUCLEAN) 208 err = 0; 209 if (err || read != pgsize) { 210 printk(PRINT_PREF "error: read failed at %#llx\n", 211 addr); 212 if (!err) 213 err = -EINVAL; 214 break; 215 } 216 addr += pgsize; 217 buf += pgsize; 218 } 219 220 return err; 221} 222 223static int read_eraseblock_by_2pages(int ebnum) 224{ 225 size_t read = 0, sz = pgsize * 2; 226 int i, n = pgcnt / 2, err = 0; 227 loff_t addr = ebnum * mtd->erasesize; 228 void *buf = iobuf; 229 230 for (i = 0; i < n; i++) { 231 err = mtd->read(mtd, addr, sz, &read, buf); 232 /* Ignore corrected ECC errors */ 233 if (err == -EUCLEAN) 234 err = 0; 235 if (err || read != sz) { 236 printk(PRINT_PREF "error: read failed at %#llx\n", 237 addr); 238 if (!err) 239 err = -EINVAL; 240 return err; 241 } 242 addr += sz; 243 buf += sz; 244 } 245 if (pgcnt % 2) { 246 err = mtd->read(mtd, addr, pgsize, &read, buf); 247 /* Ignore corrected ECC errors */ 248 if (err == -EUCLEAN) 249 err = 0; 250 if (err || read != pgsize) { 251 printk(PRINT_PREF "error: read failed at %#llx\n", 252 addr); 253 if (!err) 254 err = -EINVAL; 255 } 256 } 257 258 return err; 259} 260 261static int is_block_bad(int ebnum) 262{ 263 loff_t addr = ebnum * mtd->erasesize; 264 int ret; 265 266 ret = mtd->block_isbad(mtd, addr); 267 if (ret) 268 printk(PRINT_PREF "block %d is bad\n", ebnum); 269 return ret; 270} 271 272static inline void start_timing(void) 273{ 274 do_gettimeofday(&start); 275} 276 277static inline void stop_timing(void) 278{ 279 do_gettimeofday(&finish); 280} 281 282static long calc_speed(void) 283{ 284 long ms, k, speed; 285 286 ms = (finish.tv_sec - start.tv_sec) * 1000 + 287 (finish.tv_usec - start.tv_usec) / 1000; 288 k = goodebcnt * mtd->erasesize / 1024; 289 speed = (k * 1000) / ms; 290 return speed; 291} 292 293static int scan_for_bad_eraseblocks(void) 294{ 295 int i, bad = 0; 296 297 bbt = kmalloc(ebcnt, GFP_KERNEL); 298 if (!bbt) { 299 printk(PRINT_PREF "error: cannot allocate memory\n"); 300 return -ENOMEM; 301 } 302 memset(bbt, 0 , ebcnt); 303 304 printk(PRINT_PREF "scanning for bad eraseblocks\n"); 305 for (i = 0; i < ebcnt; ++i) { 306 bbt[i] = is_block_bad(i) ? 1 : 0; 307 if (bbt[i]) 308 bad += 1; 309 cond_resched(); 310 } 311 printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); 312 goodebcnt = ebcnt - bad; 313 return 0; 314} 315 316static int __init mtd_speedtest_init(void) 317{ 318 int err, i; 319 long speed; 320 uint64_t tmp; 321 322 printk(KERN_INFO "\n"); 323 printk(KERN_INFO "=================================================\n"); 324 printk(PRINT_PREF "MTD device: %d\n", dev); 325 326 mtd = get_mtd_device(NULL, dev); 327 if (IS_ERR(mtd)) { 328 err = PTR_ERR(mtd); 329 printk(PRINT_PREF "error: cannot get MTD device\n"); 330 return err; 331 } 332 333 if (mtd->writesize == 1) { 334 printk(PRINT_PREF "not NAND flash, assume page size is 512 " 335 "bytes.\n"); 336 pgsize = 512; 337 } else 338 pgsize = mtd->writesize; 339 340 tmp = mtd->size; 341 do_div(tmp, mtd->erasesize); 342 ebcnt = tmp; 343 pgcnt = mtd->erasesize / mtd->writesize; 344 345 printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " 346 "page size %u, count of eraseblocks %u, pages per " 347 "eraseblock %u, OOB size %u\n", 348 (unsigned long long)mtd->size, mtd->erasesize, 349 pgsize, ebcnt, pgcnt, mtd->oobsize); 350 351 err = -ENOMEM; 352 iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); 353 if (!iobuf) { 354 printk(PRINT_PREF "error: cannot allocate memory\n"); 355 goto out; 356 } 357 358 simple_srand(1); 359 set_random_data(iobuf, mtd->erasesize); 360 361 err = scan_for_bad_eraseblocks(); 362 if (err) 363 goto out; 364 365 err = erase_whole_device(); 366 if (err) 367 goto out; 368 369 /* Write all eraseblocks, 1 eraseblock at a time */ 370 printk(PRINT_PREF "testing eraseblock write speed\n"); 371 start_timing(); 372 for (i = 0; i < ebcnt; ++i) { 373 if (bbt[i]) 374 continue; 375 err = write_eraseblock(i); 376 if (err) 377 goto out; 378 cond_resched(); 379 } 380 stop_timing(); 381 speed = calc_speed(); 382 printk(PRINT_PREF "eraseblock write speed is %ld KiB/s\n", speed); 383 384 /* Read all eraseblocks, 1 eraseblock at a time */ 385 printk(PRINT_PREF "testing eraseblock read speed\n"); 386 start_timing(); 387 for (i = 0; i < ebcnt; ++i) { 388 if (bbt[i]) 389 continue; 390 err = read_eraseblock(i); 391 if (err) 392 goto out; 393 cond_resched(); 394 } 395 stop_timing(); 396 speed = calc_speed(); 397 printk(PRINT_PREF "eraseblock read speed is %ld KiB/s\n", speed); 398 399 err = erase_whole_device(); 400 if (err) 401 goto out; 402 403 /* Write all eraseblocks, 1 page at a time */ 404 printk(PRINT_PREF "testing page write speed\n"); 405 start_timing(); 406 for (i = 0; i < ebcnt; ++i) { 407 if (bbt[i]) 408 continue; 409 err = write_eraseblock_by_page(i); 410 if (err) 411 goto out; 412 cond_resched(); 413 } 414 stop_timing(); 415 speed = calc_speed(); 416 printk(PRINT_PREF "page write speed is %ld KiB/s\n", speed); 417 418 /* Read all eraseblocks, 1 page at a time */ 419 printk(PRINT_PREF "testing page read speed\n"); 420 start_timing(); 421 for (i = 0; i < ebcnt; ++i) { 422 if (bbt[i]) 423 continue; 424 err = read_eraseblock_by_page(i); 425 if (err) 426 goto out; 427 cond_resched(); 428 } 429 stop_timing(); 430 speed = calc_speed(); 431 printk(PRINT_PREF "page read speed is %ld KiB/s\n", speed); 432 433 err = erase_whole_device(); 434 if (err) 435 goto out; 436 437 /* Write all eraseblocks, 2 pages at a time */ 438 printk(PRINT_PREF "testing 2 page write speed\n"); 439 start_timing(); 440 for (i = 0; i < ebcnt; ++i) { 441 if (bbt[i]) 442 continue; 443 err = write_eraseblock_by_2pages(i); 444 if (err) 445 goto out; 446 cond_resched(); 447 } 448 stop_timing(); 449 speed = calc_speed(); 450 printk(PRINT_PREF "2 page write speed is %ld KiB/s\n", speed); 451 452 /* Read all eraseblocks, 2 pages at a time */ 453 printk(PRINT_PREF "testing 2 page read speed\n"); 454 start_timing(); 455 for (i = 0; i < ebcnt; ++i) { 456 if (bbt[i]) 457 continue; 458 err = read_eraseblock_by_2pages(i); 459 if (err) 460 goto out; 461 cond_resched(); 462 } 463 stop_timing(); 464 speed = calc_speed(); 465 printk(PRINT_PREF "2 page read speed is %ld KiB/s\n", speed); 466 467 /* Erase all eraseblocks */ 468 printk(PRINT_PREF "Testing erase speed\n"); 469 start_timing(); 470 for (i = 0; i < ebcnt; ++i) { 471 if (bbt[i]) 472 continue; 473 err = erase_eraseblock(i); 474 if (err) 475 goto out; 476 cond_resched(); 477 } 478 stop_timing(); 479 speed = calc_speed(); 480 printk(PRINT_PREF "erase speed is %ld KiB/s\n", speed); 481 482 printk(PRINT_PREF "finished\n"); 483out: 484 kfree(iobuf); 485 kfree(bbt); 486 put_mtd_device(mtd); 487 if (err) 488 printk(PRINT_PREF "error %d occurred\n", err); 489 printk(KERN_INFO "=================================================\n"); 490 return err; 491} 492module_init(mtd_speedtest_init); 493 494static void __exit mtd_speedtest_exit(void) 495{ 496 return; 497} 498module_exit(mtd_speedtest_exit); 499 500MODULE_DESCRIPTION("Speed test module"); 501MODULE_AUTHOR("Adrian Hunter"); 502MODULE_LICENSE("GPL");