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

MTD: tests: add mtd_speedtest

This test examines I/O speed of the flash device.

Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>

+502
+502
drivers/mtd/tests/mtd_speedtest.c
··· 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 + 31 + static int dev; 32 + module_param(dev, int, S_IRUGO); 33 + MODULE_PARM_DESC(dev, "MTD device number to use"); 34 + 35 + static struct mtd_info *mtd; 36 + static unsigned char *iobuf; 37 + static unsigned char *bbt; 38 + 39 + static int pgsize; 40 + static int ebcnt; 41 + static int pgcnt; 42 + static int goodebcnt; 43 + static struct timeval start, finish; 44 + static unsigned long next = 1; 45 + 46 + static inline unsigned int simple_rand(void) 47 + { 48 + next = next * 1103515245 + 12345; 49 + return (unsigned int)((next / 65536) % 32768); 50 + } 51 + 52 + static inline void simple_srand(unsigned long seed) 53 + { 54 + next = seed; 55 + } 56 + 57 + static 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 + 65 + static 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 + 91 + static 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 + 107 + static 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 + 123 + static 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 + 146 + static 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 + 178 + static 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 + 197 + static 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 + 223 + static 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 + 261 + static 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 + 272 + static inline void start_timing(void) 273 + { 274 + do_gettimeofday(&start); 275 + } 276 + 277 + static inline void stop_timing(void) 278 + { 279 + do_gettimeofday(&finish); 280 + } 281 + 282 + static 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 + 293 + static 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 + 316 + static 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"); 483 + out: 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 + } 492 + module_init(mtd_speedtest_init); 493 + 494 + static void __exit mtd_speedtest_exit(void) 495 + { 496 + return; 497 + } 498 + module_exit(mtd_speedtest_exit); 499 + 500 + MODULE_DESCRIPTION("Speed test module"); 501 + MODULE_AUTHOR("Adrian Hunter"); 502 + MODULE_LICENSE("GPL");