linux/drivers/mtd/tests/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
  34#include "mtd_test.h"
  35
  36static int dev = -EINVAL;
  37module_param(dev, int, S_IRUGO);
  38MODULE_PARM_DESC(dev, "MTD device number to use");
  39
  40static int count = 10000;
  41module_param(count, int, S_IRUGO);
  42MODULE_PARM_DESC(count, "Number of operations to do (default is 10000)");
  43
  44static struct mtd_info *mtd;
  45static unsigned char *writebuf;
  46static unsigned char *readbuf;
  47static unsigned char *bbt;
  48static int *offsets;
  49
  50static int pgsize;
  51static int bufsize;
  52static int ebcnt;
  53static int pgcnt;
  54
  55static int rand_eb(void)
  56{
  57        unsigned int eb;
  58
  59again:
  60        eb = prandom_u32();
  61        /* Read or write up 2 eraseblocks at a time - hence 'ebcnt - 1' */
  62        eb %= (ebcnt - 1);
  63        if (bbt[eb])
  64                goto again;
  65        return eb;
  66}
  67
  68static int rand_offs(void)
  69{
  70        unsigned int offs;
  71
  72        offs = prandom_u32();
  73        offs %= bufsize;
  74        return offs;
  75}
  76
  77static int rand_len(int offs)
  78{
  79        unsigned int len;
  80
  81        len = prandom_u32();
  82        len %= (bufsize - offs);
  83        return len;
  84}
  85
  86static int do_read(void)
  87{
  88        int eb = rand_eb();
  89        int offs = rand_offs();
  90        int len = rand_len(offs);
  91        loff_t addr;
  92
  93        if (bbt[eb + 1]) {
  94                if (offs >= mtd->erasesize)
  95                        offs -= mtd->erasesize;
  96                if (offs + len > mtd->erasesize)
  97                        len = mtd->erasesize - offs;
  98        }
  99        addr = eb * mtd->erasesize + offs;
 100        return mtdtest_read(mtd, addr, len, readbuf);
 101}
 102
 103static int do_write(void)
 104{
 105        int eb = rand_eb(), offs, err, len;
 106        loff_t addr;
 107
 108        offs = offsets[eb];
 109        if (offs >= mtd->erasesize) {
 110                err = mtdtest_erase_eraseblock(mtd, eb);
 111                if (err)
 112                        return err;
 113                offs = offsets[eb] = 0;
 114        }
 115        len = rand_len(offs);
 116        len = ((len + pgsize - 1) / pgsize) * pgsize;
 117        if (offs + len > mtd->erasesize) {
 118                if (bbt[eb + 1])
 119                        len = mtd->erasesize - offs;
 120                else {
 121                        err = mtdtest_erase_eraseblock(mtd, eb + 1);
 122                        if (err)
 123                                return err;
 124                        offsets[eb + 1] = 0;
 125                }
 126        }
 127        addr = eb * mtd->erasesize + offs;
 128        err = mtdtest_write(mtd, addr, len, writebuf);
 129        if (unlikely(err))
 130                return err;
 131        offs += len;
 132        while (offs > mtd->erasesize) {
 133                offsets[eb++] = mtd->erasesize;
 134                offs -= mtd->erasesize;
 135        }
 136        offsets[eb] = offs;
 137        return 0;
 138}
 139
 140static int do_operation(void)
 141{
 142        if (prandom_u32() & 1)
 143                return do_read();
 144        else
 145                return do_write();
 146}
 147
 148static int __init mtd_stresstest_init(void)
 149{
 150        int err;
 151        int i, op;
 152        uint64_t tmp;
 153
 154        printk(KERN_INFO "\n");
 155        printk(KERN_INFO "=================================================\n");
 156
 157        if (dev < 0) {
 158                pr_info("Please specify a valid mtd-device via module parameter\n");
 159                pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
 160                return -EINVAL;
 161        }
 162
 163        pr_info("MTD device: %d\n", dev);
 164
 165        mtd = get_mtd_device(NULL, dev);
 166        if (IS_ERR(mtd)) {
 167                err = PTR_ERR(mtd);
 168                pr_err("error: cannot get MTD device\n");
 169                return err;
 170        }
 171
 172        if (mtd->writesize == 1) {
 173                pr_info("not NAND flash, assume page size is 512 "
 174                       "bytes.\n");
 175                pgsize = 512;
 176        } else
 177                pgsize = mtd->writesize;
 178
 179        tmp = mtd->size;
 180        do_div(tmp, mtd->erasesize);
 181        ebcnt = tmp;
 182        pgcnt = mtd->erasesize / pgsize;
 183
 184        pr_info("MTD device size %llu, eraseblock size %u, "
 185               "page size %u, count of eraseblocks %u, pages per "
 186               "eraseblock %u, OOB size %u\n",
 187               (unsigned long long)mtd->size, mtd->erasesize,
 188               pgsize, ebcnt, pgcnt, mtd->oobsize);
 189
 190        if (ebcnt < 2) {
 191                pr_err("error: need at least 2 eraseblocks\n");
 192                err = -ENOSPC;
 193                goto out_put_mtd;
 194        }
 195
 196        /* Read or write up 2 eraseblocks at a time */
 197        bufsize = mtd->erasesize * 2;
 198
 199        err = -ENOMEM;
 200        readbuf = vmalloc(bufsize);
 201        writebuf = vmalloc(bufsize);
 202        offsets = kmalloc(ebcnt * sizeof(int), GFP_KERNEL);
 203        if (!readbuf || !writebuf || !offsets)
 204                goto out;
 205        for (i = 0; i < ebcnt; i++)
 206                offsets[i] = mtd->erasesize;
 207        prandom_bytes(writebuf, bufsize);
 208
 209        bbt = kzalloc(ebcnt, GFP_KERNEL);
 210        if (!bbt)
 211                goto out;
 212        err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
 213        if (err)
 214                goto out;
 215
 216        /* Do operations */
 217        pr_info("doing operations\n");
 218        for (op = 0; op < count; op++) {
 219                if ((op & 1023) == 0)
 220                        pr_info("%d operations done\n", op);
 221                err = do_operation();
 222                if (err)
 223                        goto out;
 224                cond_resched();
 225        }
 226        pr_info("finished, %d operations done\n", op);
 227
 228out:
 229        kfree(offsets);
 230        kfree(bbt);
 231        vfree(writebuf);
 232        vfree(readbuf);
 233out_put_mtd:
 234        put_mtd_device(mtd);
 235        if (err)
 236                pr_info("error %d occurred\n", err);
 237        printk(KERN_INFO "=================================================\n");
 238        return err;
 239}
 240module_init(mtd_stresstest_init);
 241
 242static void __exit mtd_stresstest_exit(void)
 243{
 244        return;
 245}
 246module_exit(mtd_stresstest_exit);
 247
 248MODULE_DESCRIPTION("Stress test module");
 249MODULE_AUTHOR("Adrian Hunter");
 250MODULE_LICENSE("GPL");
 251