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