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