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 <[email protected]> 20 */ 21 22 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 23 24 #include <linux/init.h> 25 #include <linux/ktime.h> 26 #include <linux/module.h> 27 #include <linux/moduleparam.h> 28 #include <linux/err.h> 29 #include <linux/mtd/mtd.h> 30 #include <linux/slab.h> 31 #include <linux/sched.h> 32 #include <linux/random.h> 33 34 #include "mtd_test.h" 35 36 static int dev = -EINVAL; 37 module_param(dev, int, S_IRUGO); 38 MODULE_PARM_DESC(dev, "MTD device number to use"); 39 40 static int count; 41 module_param(count, int, S_IRUGO); 42 MODULE_PARM_DESC(count, "Maximum number of eraseblocks to use " 43 "(0 means use all)"); 44 45 static struct mtd_info *mtd; 46 static unsigned char *iobuf; 47 static unsigned char *bbt; 48 49 static int pgsize; 50 static int ebcnt; 51 static int pgcnt; 52 static int goodebcnt; 53 static ktime_t start, finish; 54 55 static int multiblock_erase(int ebnum, int blocks) 56 { 57 int err; 58 struct erase_info ei; 59 loff_t addr = (loff_t)ebnum * mtd->erasesize; 60 61 memset(&ei, 0, sizeof(struct erase_info)); 62 ei.mtd = mtd; 63 ei.addr = addr; 64 ei.len = mtd->erasesize * blocks; 65 66 err = mtd_erase(mtd, &ei); 67 if (err) { 68 pr_err("error %d while erasing EB %d, blocks %d\n", 69 err, ebnum, blocks); 70 return err; 71 } 72 73 return 0; 74 } 75 76 static int write_eraseblock(int ebnum) 77 { 78 loff_t addr = (loff_t)ebnum * mtd->erasesize; 79 80 return mtdtest_write(mtd, addr, mtd->erasesize, iobuf); 81 } 82 83 static int write_eraseblock_by_page(int ebnum) 84 { 85 int i, err = 0; 86 loff_t addr = (loff_t)ebnum * mtd->erasesize; 87 void *buf = iobuf; 88 89 for (i = 0; i < pgcnt; i++) { 90 err = mtdtest_write(mtd, addr, pgsize, buf); 91 if (err) 92 break; 93 addr += pgsize; 94 buf += pgsize; 95 } 96 97 return err; 98 } 99 100 static int write_eraseblock_by_2pages(int ebnum) 101 { 102 size_t sz = pgsize * 2; 103 int i, n = pgcnt / 2, err = 0; 104 loff_t addr = (loff_t)ebnum * mtd->erasesize; 105 void *buf = iobuf; 106 107 for (i = 0; i < n; i++) { 108 err = mtdtest_write(mtd, addr, sz, buf); 109 if (err) 110 return err; 111 addr += sz; 112 buf += sz; 113 } 114 if (pgcnt % 2) 115 err = mtdtest_write(mtd, addr, pgsize, buf); 116 117 return err; 118 } 119 120 static int read_eraseblock(int ebnum) 121 { 122 loff_t addr = (loff_t)ebnum * mtd->erasesize; 123 124 return mtdtest_read(mtd, addr, mtd->erasesize, iobuf); 125 } 126 127 static int read_eraseblock_by_page(int ebnum) 128 { 129 int i, err = 0; 130 loff_t addr = (loff_t)ebnum * mtd->erasesize; 131 void *buf = iobuf; 132 133 for (i = 0; i < pgcnt; i++) { 134 err = mtdtest_read(mtd, addr, pgsize, buf); 135 if (err) 136 break; 137 addr += pgsize; 138 buf += pgsize; 139 } 140 141 return err; 142 } 143 144 static int read_eraseblock_by_2pages(int ebnum) 145 { 146 size_t sz = pgsize * 2; 147 int i, n = pgcnt / 2, err = 0; 148 loff_t addr = (loff_t)ebnum * mtd->erasesize; 149 void *buf = iobuf; 150 151 for (i = 0; i < n; i++) { 152 err = mtdtest_read(mtd, addr, sz, buf); 153 if (err) 154 return err; 155 addr += sz; 156 buf += sz; 157 } 158 if (pgcnt % 2) 159 err = mtdtest_read(mtd, addr, pgsize, buf); 160 161 return err; 162 } 163 164 static inline void start_timing(void) 165 { 166 start = ktime_get(); 167 } 168 169 static inline void stop_timing(void) 170 { 171 finish = ktime_get(); 172 } 173 174 static long calc_speed(void) 175 { 176 uint64_t k; 177 long ms; 178 179 ms = ktime_ms_delta(finish, start); 180 if (ms == 0) 181 return 0; 182 k = (uint64_t)goodebcnt * (mtd->erasesize / 1024) * 1000; 183 do_div(k, ms); 184 return k; 185 } 186 187 static int __init mtd_speedtest_init(void) 188 { 189 int err, i, blocks, j, k; 190 long speed; 191 uint64_t tmp; 192 193 printk(KERN_INFO "\n"); 194 printk(KERN_INFO "=================================================\n"); 195 196 if (dev < 0) { 197 pr_info("Please specify a valid mtd-device via module parameter\n"); 198 pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n"); 199 return -EINVAL; 200 } 201 202 if (count) 203 pr_info("MTD device: %d count: %d\n", dev, count); 204 else 205 pr_info("MTD device: %d\n", dev); 206 207 mtd = get_mtd_device(NULL, dev); 208 if (IS_ERR(mtd)) { 209 err = PTR_ERR(mtd); 210 pr_err("error: cannot get MTD device\n"); 211 return err; 212 } 213 214 if (mtd->writesize == 1) { 215 pr_info("not NAND flash, assume page size is 512 " 216 "bytes.\n"); 217 pgsize = 512; 218 } else 219 pgsize = mtd->writesize; 220 221 tmp = mtd->size; 222 do_div(tmp, mtd->erasesize); 223 ebcnt = tmp; 224 pgcnt = mtd->erasesize / pgsize; 225 226 pr_info("MTD device size %llu, eraseblock size %u, " 227 "page size %u, count of eraseblocks %u, pages per " 228 "eraseblock %u, OOB size %u\n", 229 (unsigned long long)mtd->size, mtd->erasesize, 230 pgsize, ebcnt, pgcnt, mtd->oobsize); 231 232 if (count > 0 && count < ebcnt) 233 ebcnt = count; 234 235 err = -ENOMEM; 236 iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); 237 if (!iobuf) 238 goto out; 239 240 prandom_bytes(iobuf, mtd->erasesize); 241 242 bbt = kzalloc(ebcnt, GFP_KERNEL); 243 if (!bbt) 244 goto out; 245 err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); 246 if (err) 247 goto out; 248 for (i = 0; i < ebcnt; i++) { 249 if (!bbt[i]) 250 goodebcnt++; 251 } 252 253 err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); 254 if (err) 255 goto out; 256 257 /* Write all eraseblocks, 1 eraseblock at a time */ 258 pr_info("testing eraseblock write speed\n"); 259 start_timing(); 260 for (i = 0; i < ebcnt; ++i) { 261 if (bbt[i]) 262 continue; 263 err = write_eraseblock(i); 264 if (err) 265 goto out; 266 267 err = mtdtest_relax(); 268 if (err) 269 goto out; 270 } 271 stop_timing(); 272 speed = calc_speed(); 273 pr_info("eraseblock write speed is %ld KiB/s\n", speed); 274 275 /* Read all eraseblocks, 1 eraseblock at a time */ 276 pr_info("testing eraseblock read speed\n"); 277 start_timing(); 278 for (i = 0; i < ebcnt; ++i) { 279 if (bbt[i]) 280 continue; 281 err = read_eraseblock(i); 282 if (err) 283 goto out; 284 285 err = mtdtest_relax(); 286 if (err) 287 goto out; 288 } 289 stop_timing(); 290 speed = calc_speed(); 291 pr_info("eraseblock read speed is %ld KiB/s\n", speed); 292 293 err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); 294 if (err) 295 goto out; 296 297 /* Write all eraseblocks, 1 page at a time */ 298 pr_info("testing page write speed\n"); 299 start_timing(); 300 for (i = 0; i < ebcnt; ++i) { 301 if (bbt[i]) 302 continue; 303 err = write_eraseblock_by_page(i); 304 if (err) 305 goto out; 306 307 err = mtdtest_relax(); 308 if (err) 309 goto out; 310 } 311 stop_timing(); 312 speed = calc_speed(); 313 pr_info("page write speed is %ld KiB/s\n", speed); 314 315 /* Read all eraseblocks, 1 page at a time */ 316 pr_info("testing page read speed\n"); 317 start_timing(); 318 for (i = 0; i < ebcnt; ++i) { 319 if (bbt[i]) 320 continue; 321 err = read_eraseblock_by_page(i); 322 if (err) 323 goto out; 324 325 err = mtdtest_relax(); 326 if (err) 327 goto out; 328 } 329 stop_timing(); 330 speed = calc_speed(); 331 pr_info("page read speed is %ld KiB/s\n", speed); 332 333 err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); 334 if (err) 335 goto out; 336 337 /* Write all eraseblocks, 2 pages at a time */ 338 pr_info("testing 2 page write speed\n"); 339 start_timing(); 340 for (i = 0; i < ebcnt; ++i) { 341 if (bbt[i]) 342 continue; 343 err = write_eraseblock_by_2pages(i); 344 if (err) 345 goto out; 346 347 err = mtdtest_relax(); 348 if (err) 349 goto out; 350 } 351 stop_timing(); 352 speed = calc_speed(); 353 pr_info("2 page write speed is %ld KiB/s\n", speed); 354 355 /* Read all eraseblocks, 2 pages at a time */ 356 pr_info("testing 2 page read speed\n"); 357 start_timing(); 358 for (i = 0; i < ebcnt; ++i) { 359 if (bbt[i]) 360 continue; 361 err = read_eraseblock_by_2pages(i); 362 if (err) 363 goto out; 364 365 err = mtdtest_relax(); 366 if (err) 367 goto out; 368 } 369 stop_timing(); 370 speed = calc_speed(); 371 pr_info("2 page read speed is %ld KiB/s\n", speed); 372 373 /* Erase all eraseblocks */ 374 pr_info("Testing erase speed\n"); 375 start_timing(); 376 err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); 377 if (err) 378 goto out; 379 stop_timing(); 380 speed = calc_speed(); 381 pr_info("erase speed is %ld KiB/s\n", speed); 382 383 /* Multi-block erase all eraseblocks */ 384 for (k = 1; k < 7; k++) { 385 blocks = 1 << k; 386 pr_info("Testing %dx multi-block erase speed\n", 387 blocks); 388 start_timing(); 389 for (i = 0; i < ebcnt; ) { 390 for (j = 0; j < blocks && (i + j) < ebcnt; j++) 391 if (bbt[i + j]) 392 break; 393 if (j < 1) { 394 i++; 395 continue; 396 } 397 err = multiblock_erase(i, j); 398 if (err) 399 goto out; 400 401 err = mtdtest_relax(); 402 if (err) 403 goto out; 404 405 i += j; 406 } 407 stop_timing(); 408 speed = calc_speed(); 409 pr_info("%dx multi-block erase speed is %ld KiB/s\n", 410 blocks, speed); 411 } 412 pr_info("finished\n"); 413 out: 414 kfree(iobuf); 415 kfree(bbt); 416 put_mtd_device(mtd); 417 if (err) 418 pr_info("error %d occurred\n", err); 419 printk(KERN_INFO "=================================================\n"); 420 return err; 421 } 422 module_init(mtd_speedtest_init); 423 424 static void __exit mtd_speedtest_exit(void) 425 { 426 return; 427 } 428 module_exit(mtd_speedtest_exit); 429 430 MODULE_DESCRIPTION("Speed test module"); 431 MODULE_AUTHOR("Adrian Hunter"); 432 MODULE_LICENSE("GPL"); 433