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