linux/drivers/mtd/tests/mtd_speedtest.c
<<
>>
Prefs
   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");
 561