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