linux/drivers/mtd/tests/mtd_stresstest.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2006-2008 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 random reads, writes and erases on MTD device.
  18 *
  19 * Author: Adrian Hunter <ext-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/vmalloc.h>
  32#include <linux/random.h>
  33
  34static int dev = -EINVAL;
  35module_param(dev, int, S_IRUGO);
  36MODULE_PARM_DESC(dev, "MTD device number to use");
  37
  38static int count = 10000;
  39module_param(count, int, S_IRUGO);
  40MODULE_PARM_DESC(count, "Number of operations to do (default is 10000)");
  41
  42static struct mtd_info *mtd;
  43static unsigned char *writebuf;
  44static unsigned char *readbuf;
  45static unsigned char *bbt;
  46static int *offsets;
  47
  48static int pgsize;
  49static int bufsize;
  50static int ebcnt;
  51static int pgcnt;
  52
  53static int rand_eb(void)
  54{
  55        unsigned int eb;
  56
  57again:
  58        eb = prandom_u32();
  59        /* Read or write up 2 eraseblocks at a time - hence 'ebcnt - 1' */
  60        eb %= (ebcnt - 1);
  61        if (bbt[eb])
  62                goto again;
  63        return eb;
  64}
  65
  66static int rand_offs(void)
  67{
  68        unsigned int offs;
  69
  70        offs = prandom_u32();
  71        offs %= bufsize;
  72        return offs;
  73}
  74
  75static int rand_len(int offs)
  76{
  77        unsigned int len;
  78
  79        len = prandom_u32();
  80        len %= (bufsize - offs);
  81        return len;
  82}
  83
  84static int erase_eraseblock(int ebnum)
  85{
  86        int err;
  87        struct erase_info ei;
  88        loff_t addr = ebnum * mtd->erasesize;
  89
  90        memset(&ei, 0, sizeof(struct erase_info));
  91        ei.mtd  = mtd;
  92        ei.addr = addr;
  93        ei.len  = mtd->erasesize;
  94
  95        err = mtd_erase(mtd, &ei);
  96        if (unlikely(err)) {
  97                pr_err("error %d while erasing EB %d\n", err, ebnum);
  98                return err;
  99        }
 100
 101        if (unlikely(ei.state == MTD_ERASE_FAILED)) {
 102                pr_err("some erase error occurred at EB %d\n",
 103                       ebnum);
 104                return -EIO;
 105        }
 106
 107        return 0;
 108}
 109
 110static int is_block_bad(int ebnum)
 111{
 112        loff_t addr = ebnum * mtd->erasesize;
 113        int ret;
 114
 115        ret = mtd_block_isbad(mtd, addr);
 116        if (ret)
 117                pr_info("block %d is bad\n", ebnum);
 118        return ret;
 119}
 120
 121static int do_read(void)
 122{
 123        size_t read;
 124        int eb = rand_eb();
 125        int offs = rand_offs();
 126        int len = rand_len(offs), err;
 127        loff_t addr;
 128
 129        if (bbt[eb + 1]) {
 130                if (offs >= mtd->erasesize)
 131                        offs -= mtd->erasesize;
 132                if (offs + len > mtd->erasesize)
 133                        len = mtd->erasesize - offs;
 134        }
 135        addr = eb * mtd->erasesize + offs;
 136        err = mtd_read(mtd, addr, len, &read, readbuf);
 137        if (mtd_is_bitflip(err))
 138                err = 0;
 139        if (unlikely(err || read != len)) {
 140                pr_err("error: read failed at 0x%llx\n",
 141                       (long long)addr);
 142                if (!err)
 143                        err = -EINVAL;
 144                return err;
 145        }
 146        return 0;
 147}
 148
 149static int do_write(void)
 150{
 151        int eb = rand_eb(), offs, err, len;
 152        size_t written;
 153        loff_t addr;
 154
 155        offs = offsets[eb];
 156        if (offs >= mtd->erasesize) {
 157                err = erase_eraseblock(eb);
 158                if (err)
 159                        return err;
 160                offs = offsets[eb] = 0;
 161        }
 162        len = rand_len(offs);
 163        len = ((len + pgsize - 1) / pgsize) * pgsize;
 164        if (offs + len > mtd->erasesize) {
 165                if (bbt[eb + 1])
 166                        len = mtd->erasesize - offs;
 167                else {
 168                        err = erase_eraseblock(eb + 1);
 169                        if (err)
 170                                return err;
 171                        offsets[eb + 1] = 0;
 172                }
 173        }
 174        addr = eb * mtd->erasesize + offs;
 175        err = mtd_write(mtd, addr, len, &written, writebuf);
 176        if (unlikely(err || written != len)) {
 177                pr_err("error: write failed at 0x%llx\n",
 178                       (long long)addr);
 179                if (!err)
 180                        err = -EINVAL;
 181                return err;
 182        }
 183        offs += len;
 184        while (offs > mtd->erasesize) {
 185                offsets[eb++] = mtd->erasesize;
 186                offs -= mtd->erasesize;
 187        }
 188        offsets[eb] = offs;
 189        return 0;
 190}
 191
 192static int do_operation(void)
 193{
 194        if (prandom_u32() & 1)
 195                return do_read();
 196        else
 197                return do_write();
 198}
 199
 200static int scan_for_bad_eraseblocks(void)
 201{
 202        int i, bad = 0;
 203
 204        bbt = kzalloc(ebcnt, GFP_KERNEL);
 205        if (!bbt) {
 206                pr_err("error: cannot allocate memory\n");
 207                return -ENOMEM;
 208        }
 209
 210        if (!mtd_can_have_bb(mtd))
 211                return 0;
 212
 213        pr_info("scanning for bad eraseblocks\n");
 214        for (i = 0; i < ebcnt; ++i) {
 215                bbt[i] = is_block_bad(i) ? 1 : 0;
 216                if (bbt[i])
 217                        bad += 1;
 218                cond_resched();
 219        }
 220        pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
 221        return 0;
 222}
 223
 224static int __init mtd_stresstest_init(void)
 225{
 226        int err;
 227        int i, op;
 228        uint64_t tmp;
 229
 230        printk(KERN_INFO "\n");
 231        printk(KERN_INFO "=================================================\n");
 232
 233        if (dev < 0) {
 234                pr_info("Please specify a valid mtd-device via module parameter\n");
 235                pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
 236                return -EINVAL;
 237        }
 238
 239        pr_info("MTD device: %d\n", dev);
 240
 241        mtd = get_mtd_device(NULL, dev);
 242        if (IS_ERR(mtd)) {
 243                err = PTR_ERR(mtd);
 244                pr_err("error: cannot get MTD device\n");
 245                return err;
 246        }
 247
 248        if (mtd->writesize == 1) {
 249                pr_info("not NAND flash, assume page size is 512 "
 250                       "bytes.\n");
 251                pgsize = 512;
 252        } else
 253                pgsize = mtd->writesize;
 254
 255        tmp = mtd->size;
 256        do_div(tmp, mtd->erasesize);
 257        ebcnt = tmp;
 258        pgcnt = mtd->erasesize / pgsize;
 259
 260        pr_info("MTD device size %llu, eraseblock size %u, "
 261               "page size %u, count of eraseblocks %u, pages per "
 262               "eraseblock %u, OOB size %u\n",
 263               (unsigned long long)mtd->size, mtd->erasesize,
 264               pgsize, ebcnt, pgcnt, mtd->oobsize);
 265
 266        if (ebcnt < 2) {
 267                pr_err("error: need at least 2 eraseblocks\n");
 268                err = -ENOSPC;
 269                goto out_put_mtd;
 270        }
 271
 272        /* Read or write up 2 eraseblocks at a time */
 273        bufsize = mtd->erasesize * 2;
 274
 275        err = -ENOMEM;
 276        readbuf = vmalloc(bufsize);
 277        writebuf = vmalloc(bufsize);
 278        offsets = kmalloc(ebcnt * sizeof(int), GFP_KERNEL);
 279        if (!readbuf || !writebuf || !offsets) {
 280                pr_err("error: cannot allocate memory\n");
 281                goto out;
 282        }
 283        for (i = 0; i < ebcnt; i++)
 284                offsets[i] = mtd->erasesize;
 285        prandom_bytes(writebuf, bufsize);
 286
 287        err = scan_for_bad_eraseblocks();
 288        if (err)
 289                goto out;
 290
 291        /* Do operations */
 292        pr_info("doing operations\n");
 293        for (op = 0; op < count; op++) {
 294                if ((op & 1023) == 0)
 295                        pr_info("%d operations done\n", op);
 296                err = do_operation();
 297                if (err)
 298                        goto out;
 299                cond_resched();
 300        }
 301        pr_info("finished, %d operations done\n", op);
 302
 303out:
 304        kfree(offsets);
 305        kfree(bbt);
 306        vfree(writebuf);
 307        vfree(readbuf);
 308out_put_mtd:
 309        put_mtd_device(mtd);
 310        if (err)
 311                pr_info("error %d occurred\n", err);
 312        printk(KERN_INFO "=================================================\n");
 313        return err;
 314}
 315module_init(mtd_stresstest_init);
 316
 317static void __exit mtd_stresstest_exit(void)
 318{
 319        return;
 320}
 321module_exit(mtd_stresstest_exit);
 322
 323MODULE_DESCRIPTION("Stress test module");
 324MODULE_AUTHOR("Adrian Hunter");
 325MODULE_LICENSE("GPL");
 326