linux/drivers/mtd/tests/subpagetest.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2006-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 sub-page read and write on MTD device.
  18 * Author: Adrian Hunter <ext-adrian.hunter@nokia.com>
  19 *
  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
  33#include "mtd_test.h"
  34
  35static int dev = -EINVAL;
  36module_param(dev, int, S_IRUGO);
  37MODULE_PARM_DESC(dev, "MTD device number to use");
  38
  39static struct mtd_info *mtd;
  40static unsigned char *writebuf;
  41static unsigned char *readbuf;
  42static unsigned char *bbt;
  43
  44static int subpgsize;
  45static int bufsize;
  46static int ebcnt;
  47static int pgcnt;
  48static int errcnt;
  49static struct rnd_state rnd_state;
  50
  51static inline void clear_data(unsigned char *buf, size_t len)
  52{
  53        memset(buf, 0, len);
  54}
  55
  56static int write_eraseblock(int ebnum)
  57{
  58        size_t written;
  59        int err = 0;
  60        loff_t addr = ebnum * mtd->erasesize;
  61
  62        prandom_bytes_state(&rnd_state, writebuf, subpgsize);
  63        err = mtd_write(mtd, addr, subpgsize, &written, writebuf);
  64        if (unlikely(err || written != subpgsize)) {
  65                pr_err("error: write failed at %#llx\n",
  66                       (long long)addr);
  67                if (written != subpgsize) {
  68                        pr_err("  write size: %#x\n", subpgsize);
  69                        pr_err("  written: %#zx\n", written);
  70                }
  71                return err ? err : -1;
  72        }
  73
  74        addr += subpgsize;
  75
  76        prandom_bytes_state(&rnd_state, writebuf, subpgsize);
  77        err = mtd_write(mtd, addr, subpgsize, &written, writebuf);
  78        if (unlikely(err || written != subpgsize)) {
  79                pr_err("error: write failed at %#llx\n",
  80                       (long long)addr);
  81                if (written != subpgsize) {
  82                        pr_err("  write size: %#x\n", subpgsize);
  83                        pr_err("  written: %#zx\n", written);
  84                }
  85                return err ? err : -1;
  86        }
  87
  88        return err;
  89}
  90
  91static int write_eraseblock2(int ebnum)
  92{
  93        size_t written;
  94        int err = 0, k;
  95        loff_t addr = ebnum * mtd->erasesize;
  96
  97        for (k = 1; k < 33; ++k) {
  98                if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize)
  99                        break;
 100                prandom_bytes_state(&rnd_state, writebuf, subpgsize * k);
 101                err = mtd_write(mtd, addr, subpgsize * k, &written, writebuf);
 102                if (unlikely(err || written != subpgsize * k)) {
 103                        pr_err("error: write failed at %#llx\n",
 104                               (long long)addr);
 105                        if (written != subpgsize) {
 106                                pr_err("  write size: %#x\n",
 107                                       subpgsize * k);
 108                                pr_err("  written: %#08zx\n",
 109                                       written);
 110                        }
 111                        return err ? err : -1;
 112                }
 113                addr += subpgsize * k;
 114        }
 115
 116        return err;
 117}
 118
 119static void print_subpage(unsigned char *p)
 120{
 121        int i, j;
 122
 123        for (i = 0; i < subpgsize; ) {
 124                for (j = 0; i < subpgsize && j < 32; ++i, ++j)
 125                        printk("%02x", *p++);
 126                printk("\n");
 127        }
 128}
 129
 130static int verify_eraseblock(int ebnum)
 131{
 132        size_t read;
 133        int err = 0;
 134        loff_t addr = ebnum * mtd->erasesize;
 135
 136        prandom_bytes_state(&rnd_state, writebuf, subpgsize);
 137        clear_data(readbuf, subpgsize);
 138        err = mtd_read(mtd, addr, subpgsize, &read, readbuf);
 139        if (unlikely(err || read != subpgsize)) {
 140                if (mtd_is_bitflip(err) && read == subpgsize) {
 141                        pr_info("ECC correction at %#llx\n",
 142                               (long long)addr);
 143                        err = 0;
 144                } else {
 145                        pr_err("error: read failed at %#llx\n",
 146                               (long long)addr);
 147                        return err ? err : -1;
 148                }
 149        }
 150        if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
 151                pr_err("error: verify failed at %#llx\n",
 152                       (long long)addr);
 153                pr_info("------------- written----------------\n");
 154                print_subpage(writebuf);
 155                pr_info("------------- read ------------------\n");
 156                print_subpage(readbuf);
 157                pr_info("-------------------------------------\n");
 158                errcnt += 1;
 159        }
 160
 161        addr += subpgsize;
 162
 163        prandom_bytes_state(&rnd_state, writebuf, subpgsize);
 164        clear_data(readbuf, subpgsize);
 165        err = mtd_read(mtd, addr, subpgsize, &read, readbuf);
 166        if (unlikely(err || read != subpgsize)) {
 167                if (mtd_is_bitflip(err) && read == subpgsize) {
 168                        pr_info("ECC correction at %#llx\n",
 169                               (long long)addr);
 170                        err = 0;
 171                } else {
 172                        pr_err("error: read failed at %#llx\n",
 173                               (long long)addr);
 174                        return err ? err : -1;
 175                }
 176        }
 177        if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
 178                pr_info("error: verify failed at %#llx\n",
 179                       (long long)addr);
 180                pr_info("------------- written----------------\n");
 181                print_subpage(writebuf);
 182                pr_info("------------- read ------------------\n");
 183                print_subpage(readbuf);
 184                pr_info("-------------------------------------\n");
 185                errcnt += 1;
 186        }
 187
 188        return err;
 189}
 190
 191static int verify_eraseblock2(int ebnum)
 192{
 193        size_t read;
 194        int err = 0, k;
 195        loff_t addr = ebnum * mtd->erasesize;
 196
 197        for (k = 1; k < 33; ++k) {
 198                if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize)
 199                        break;
 200                prandom_bytes_state(&rnd_state, writebuf, subpgsize * k);
 201                clear_data(readbuf, subpgsize * k);
 202                err = mtd_read(mtd, addr, subpgsize * k, &read, readbuf);
 203                if (unlikely(err || read != subpgsize * k)) {
 204                        if (mtd_is_bitflip(err) && read == subpgsize * k) {
 205                                pr_info("ECC correction at %#llx\n",
 206                                       (long long)addr);
 207                                err = 0;
 208                        } else {
 209                                pr_err("error: read failed at "
 210                                       "%#llx\n", (long long)addr);
 211                                return err ? err : -1;
 212                        }
 213                }
 214                if (unlikely(memcmp(readbuf, writebuf, subpgsize * k))) {
 215                        pr_err("error: verify failed at %#llx\n",
 216                               (long long)addr);
 217                        errcnt += 1;
 218                }
 219                addr += subpgsize * k;
 220        }
 221
 222        return err;
 223}
 224
 225static int verify_eraseblock_ff(int ebnum)
 226{
 227        uint32_t j;
 228        size_t read;
 229        int err = 0;
 230        loff_t addr = ebnum * mtd->erasesize;
 231
 232        memset(writebuf, 0xff, subpgsize);
 233        for (j = 0; j < mtd->erasesize / subpgsize; ++j) {
 234                clear_data(readbuf, subpgsize);
 235                err = mtd_read(mtd, addr, subpgsize, &read, readbuf);
 236                if (unlikely(err || read != subpgsize)) {
 237                        if (mtd_is_bitflip(err) && read == subpgsize) {
 238                                pr_info("ECC correction at %#llx\n",
 239                                       (long long)addr);
 240                                err = 0;
 241                        } else {
 242                                pr_err("error: read failed at "
 243                                       "%#llx\n", (long long)addr);
 244                                return err ? err : -1;
 245                        }
 246                }
 247                if (unlikely(memcmp(readbuf, writebuf, subpgsize))) {
 248                        pr_err("error: verify 0xff failed at "
 249                               "%#llx\n", (long long)addr);
 250                        errcnt += 1;
 251                }
 252                addr += subpgsize;
 253        }
 254
 255        return err;
 256}
 257
 258static int verify_all_eraseblocks_ff(void)
 259{
 260        int err;
 261        unsigned int i;
 262
 263        pr_info("verifying all eraseblocks for 0xff\n");
 264        for (i = 0; i < ebcnt; ++i) {
 265                if (bbt[i])
 266                        continue;
 267                err = verify_eraseblock_ff(i);
 268                if (err)
 269                        return err;
 270                if (i % 256 == 0)
 271                        pr_info("verified up to eraseblock %u\n", i);
 272                cond_resched();
 273        }
 274        pr_info("verified %u eraseblocks\n", i);
 275        return 0;
 276}
 277
 278static int __init mtd_subpagetest_init(void)
 279{
 280        int err = 0;
 281        uint32_t i;
 282        uint64_t tmp;
 283
 284        printk(KERN_INFO "\n");
 285        printk(KERN_INFO "=================================================\n");
 286
 287        if (dev < 0) {
 288                pr_info("Please specify a valid mtd-device via module parameter\n");
 289                pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
 290                return -EINVAL;
 291        }
 292
 293        pr_info("MTD device: %d\n", dev);
 294
 295        mtd = get_mtd_device(NULL, dev);
 296        if (IS_ERR(mtd)) {
 297                err = PTR_ERR(mtd);
 298                pr_err("error: cannot get MTD device\n");
 299                return err;
 300        }
 301
 302        if (!mtd_type_is_nand(mtd)) {
 303                pr_info("this test requires NAND flash\n");
 304                goto out;
 305        }
 306
 307        subpgsize = mtd->writesize >> mtd->subpage_sft;
 308        tmp = mtd->size;
 309        do_div(tmp, mtd->erasesize);
 310        ebcnt = tmp;
 311        pgcnt = mtd->erasesize / mtd->writesize;
 312
 313        pr_info("MTD device size %llu, eraseblock size %u, "
 314               "page size %u, subpage size %u, count of eraseblocks %u, "
 315               "pages per eraseblock %u, OOB size %u\n",
 316               (unsigned long long)mtd->size, mtd->erasesize,
 317               mtd->writesize, subpgsize, ebcnt, pgcnt, mtd->oobsize);
 318
 319        err = -ENOMEM;
 320        bufsize = subpgsize * 32;
 321        writebuf = kmalloc(bufsize, GFP_KERNEL);
 322        if (!writebuf)
 323                goto out;
 324        readbuf = kmalloc(bufsize, GFP_KERNEL);
 325        if (!readbuf)
 326                goto out;
 327        bbt = kzalloc(ebcnt, GFP_KERNEL);
 328        if (!bbt)
 329                goto out;
 330
 331        err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt);
 332        if (err)
 333                goto out;
 334
 335        err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
 336        if (err)
 337                goto out;
 338
 339        pr_info("writing whole device\n");
 340        prandom_seed_state(&rnd_state, 1);
 341        for (i = 0; i < ebcnt; ++i) {
 342                if (bbt[i])
 343                        continue;
 344                err = write_eraseblock(i);
 345                if (unlikely(err))
 346                        goto out;
 347                if (i % 256 == 0)
 348                        pr_info("written up to eraseblock %u\n", i);
 349                cond_resched();
 350        }
 351        pr_info("written %u eraseblocks\n", i);
 352
 353        prandom_seed_state(&rnd_state, 1);
 354        pr_info("verifying all eraseblocks\n");
 355        for (i = 0; i < ebcnt; ++i) {
 356                if (bbt[i])
 357                        continue;
 358                err = verify_eraseblock(i);
 359                if (unlikely(err))
 360                        goto out;
 361                if (i % 256 == 0)
 362                        pr_info("verified up to eraseblock %u\n", i);
 363                cond_resched();
 364        }
 365        pr_info("verified %u eraseblocks\n", i);
 366
 367        err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
 368        if (err)
 369                goto out;
 370
 371        err = verify_all_eraseblocks_ff();
 372        if (err)
 373                goto out;
 374
 375        /* Write all eraseblocks */
 376        prandom_seed_state(&rnd_state, 3);
 377        pr_info("writing whole device\n");
 378        for (i = 0; i < ebcnt; ++i) {
 379                if (bbt[i])
 380                        continue;
 381                err = write_eraseblock2(i);
 382                if (unlikely(err))
 383                        goto out;
 384                if (i % 256 == 0)
 385                        pr_info("written up to eraseblock %u\n", i);
 386                cond_resched();
 387        }
 388        pr_info("written %u eraseblocks\n", i);
 389
 390        /* Check all eraseblocks */
 391        prandom_seed_state(&rnd_state, 3);
 392        pr_info("verifying all eraseblocks\n");
 393        for (i = 0; i < ebcnt; ++i) {
 394                if (bbt[i])
 395                        continue;
 396                err = verify_eraseblock2(i);
 397                if (unlikely(err))
 398                        goto out;
 399                if (i % 256 == 0)
 400                        pr_info("verified up to eraseblock %u\n", i);
 401                cond_resched();
 402        }
 403        pr_info("verified %u eraseblocks\n", i);
 404
 405        err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt);
 406        if (err)
 407                goto out;
 408
 409        err = verify_all_eraseblocks_ff();
 410        if (err)
 411                goto out;
 412
 413        pr_info("finished with %d errors\n", errcnt);
 414
 415out:
 416        kfree(bbt);
 417        kfree(readbuf);
 418        kfree(writebuf);
 419        put_mtd_device(mtd);
 420        if (err)
 421                pr_info("error %d occurred\n", err);
 422        printk(KERN_INFO "=================================================\n");
 423        return err;
 424}
 425module_init(mtd_subpagetest_init);
 426
 427static void __exit mtd_subpagetest_exit(void)
 428{
 429        return;
 430}
 431module_exit(mtd_subpagetest_exit);
 432
 433MODULE_DESCRIPTION("Subpage test module");
 434MODULE_AUTHOR("Adrian Hunter");
 435MODULE_LICENSE("GPL");
 436