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