linux/drivers/mtd/tests/mtd_oobtest.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 OOB read and write 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 <asm/div64.h>
  25#include <linux/init.h>
  26#include <linux/module.h>
  27#include <linux/moduleparam.h>
  28#include <linux/err.h>
  29#include <linux/mtd/mtd.h>
  30#include <linux/slab.h>
  31#include <linux/sched.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 struct mtd_info *mtd;
  39static unsigned char *readbuf;
  40static unsigned char *writebuf;
  41static unsigned char *bbt;
  42
  43static int ebcnt;
  44static int pgcnt;
  45static int errcnt;
  46static int use_offset;
  47static int use_len;
  48static int use_len_max;
  49static int vary_offset;
  50static struct rnd_state rnd_state;
  51
  52static int erase_eraseblock(int ebnum)
  53{
  54        int err;
  55        struct erase_info ei;
  56        loff_t addr = ebnum * mtd->erasesize;
  57
  58        memset(&ei, 0, sizeof(struct erase_info));
  59        ei.mtd  = mtd;
  60        ei.addr = addr;
  61        ei.len  = mtd->erasesize;
  62
  63        err = mtd_erase(mtd, &ei);
  64        if (err) {
  65                pr_err("error %d while erasing EB %d\n", err, ebnum);
  66                return err;
  67        }
  68
  69        if (ei.state == MTD_ERASE_FAILED) {
  70                pr_err("some erase error occurred at EB %d\n", ebnum);
  71                return -EIO;
  72        }
  73
  74        return 0;
  75}
  76
  77static int erase_whole_device(void)
  78{
  79        int err;
  80        unsigned int i;
  81
  82        pr_info("erasing whole device\n");
  83        for (i = 0; i < ebcnt; ++i) {
  84                if (bbt[i])
  85                        continue;
  86                err = erase_eraseblock(i);
  87                if (err)
  88                        return err;
  89                cond_resched();
  90        }
  91        pr_info("erased %u eraseblocks\n", i);
  92        return 0;
  93}
  94
  95static void do_vary_offset(void)
  96{
  97        use_len -= 1;
  98        if (use_len < 1) {
  99                use_offset += 1;
 100                if (use_offset >= use_len_max)
 101                        use_offset = 0;
 102                use_len = use_len_max - use_offset;
 103        }
 104}
 105
 106static int write_eraseblock(int ebnum)
 107{
 108        int i;
 109        struct mtd_oob_ops ops;
 110        int err = 0;
 111        loff_t addr = ebnum * mtd->erasesize;
 112
 113        for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
 114                prandom_bytes_state(&rnd_state, writebuf, use_len);
 115                ops.mode      = MTD_OPS_AUTO_OOB;
 116                ops.len       = 0;
 117                ops.retlen    = 0;
 118                ops.ooblen    = use_len;
 119                ops.oobretlen = 0;
 120                ops.ooboffs   = use_offset;
 121                ops.datbuf    = NULL;
 122                ops.oobbuf    = writebuf;
 123                err = mtd_write_oob(mtd, addr, &ops);
 124                if (err || ops.oobretlen != use_len) {
 125                        pr_err("error: writeoob failed at %#llx\n",
 126                               (long long)addr);
 127                        pr_err("error: use_len %d, use_offset %d\n",
 128                               use_len, use_offset);
 129                        errcnt += 1;
 130                        return err ? err : -1;
 131                }
 132                if (vary_offset)
 133                        do_vary_offset();
 134        }
 135
 136        return err;
 137}
 138
 139static int write_whole_device(void)
 140{
 141        int err;
 142        unsigned int i;
 143
 144        pr_info("writing OOBs of whole device\n");
 145        for (i = 0; i < ebcnt; ++i) {
 146                if (bbt[i])
 147                        continue;
 148                err = write_eraseblock(i);
 149                if (err)
 150                        return err;
 151                if (i % 256 == 0)
 152                        pr_info("written up to eraseblock %u\n", i);
 153                cond_resched();
 154        }
 155        pr_info("written %u eraseblocks\n", i);
 156        return 0;
 157}
 158
 159static int verify_eraseblock(int ebnum)
 160{
 161        int i;
 162        struct mtd_oob_ops ops;
 163        int err = 0;
 164        loff_t addr = ebnum * mtd->erasesize;
 165
 166        for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
 167                prandom_bytes_state(&rnd_state, writebuf, use_len);
 168                ops.mode      = MTD_OPS_AUTO_OOB;
 169                ops.len       = 0;
 170                ops.retlen    = 0;
 171                ops.ooblen    = use_len;
 172                ops.oobretlen = 0;
 173                ops.ooboffs   = use_offset;
 174                ops.datbuf    = NULL;
 175                ops.oobbuf    = readbuf;
 176                err = mtd_read_oob(mtd, addr, &ops);
 177                if (err || ops.oobretlen != use_len) {
 178                        pr_err("error: readoob failed at %#llx\n",
 179                               (long long)addr);
 180                        errcnt += 1;
 181                        return err ? err : -1;
 182                }
 183                if (memcmp(readbuf, writebuf, use_len)) {
 184                        pr_err("error: verify failed at %#llx\n",
 185                               (long long)addr);
 186                        errcnt += 1;
 187                        if (errcnt > 1000) {
 188                                pr_err("error: too many errors\n");
 189                                return -1;
 190                        }
 191                }
 192                if (use_offset != 0 || use_len < mtd->ecclayout->oobavail) {
 193                        int k;
 194
 195                        ops.mode      = MTD_OPS_AUTO_OOB;
 196                        ops.len       = 0;
 197                        ops.retlen    = 0;
 198                        ops.ooblen    = mtd->ecclayout->oobavail;
 199                        ops.oobretlen = 0;
 200                        ops.ooboffs   = 0;
 201                        ops.datbuf    = NULL;
 202                        ops.oobbuf    = readbuf;
 203                        err = mtd_read_oob(mtd, addr, &ops);
 204                        if (err || ops.oobretlen != mtd->ecclayout->oobavail) {
 205                                pr_err("error: readoob failed at %#llx\n",
 206                                                (long long)addr);
 207                                errcnt += 1;
 208                                return err ? err : -1;
 209                        }
 210                        if (memcmp(readbuf + use_offset, writebuf, use_len)) {
 211                                pr_err("error: verify failed at %#llx\n",
 212                                                (long long)addr);
 213                                errcnt += 1;
 214                                if (errcnt > 1000) {
 215                                        pr_err("error: too many errors\n");
 216                                        return -1;
 217                                }
 218                        }
 219                        for (k = 0; k < use_offset; ++k)
 220                                if (readbuf[k] != 0xff) {
 221                                        pr_err("error: verify 0xff "
 222                                               "failed at %#llx\n",
 223                                               (long long)addr);
 224                                        errcnt += 1;
 225                                        if (errcnt > 1000) {
 226                                                pr_err("error: too "
 227                                                       "many errors\n");
 228                                                return -1;
 229                                        }
 230                                }
 231                        for (k = use_offset + use_len;
 232                             k < mtd->ecclayout->oobavail; ++k)
 233                                if (readbuf[k] != 0xff) {
 234                                        pr_err("error: verify 0xff "
 235                                               "failed at %#llx\n",
 236                                               (long long)addr);
 237                                        errcnt += 1;
 238                                        if (errcnt > 1000) {
 239                                                pr_err("error: too "
 240                                                       "many errors\n");
 241                                                return -1;
 242                                        }
 243                                }
 244                }
 245                if (vary_offset)
 246                        do_vary_offset();
 247        }
 248        return err;
 249}
 250
 251static int verify_eraseblock_in_one_go(int ebnum)
 252{
 253        struct mtd_oob_ops ops;
 254        int err = 0;
 255        loff_t addr = ebnum * mtd->erasesize;
 256        size_t len = mtd->ecclayout->oobavail * pgcnt;
 257
 258        prandom_bytes_state(&rnd_state, writebuf, len);
 259        ops.mode      = MTD_OPS_AUTO_OOB;
 260        ops.len       = 0;
 261        ops.retlen    = 0;
 262        ops.ooblen    = len;
 263        ops.oobretlen = 0;
 264        ops.ooboffs   = 0;
 265        ops.datbuf    = NULL;
 266        ops.oobbuf    = readbuf;
 267        err = mtd_read_oob(mtd, addr, &ops);
 268        if (err || ops.oobretlen != len) {
 269                pr_err("error: readoob failed at %#llx\n",
 270                       (long long)addr);
 271                errcnt += 1;
 272                return err ? err : -1;
 273        }
 274        if (memcmp(readbuf, writebuf, len)) {
 275                pr_err("error: verify failed at %#llx\n",
 276                       (long long)addr);
 277                errcnt += 1;
 278                if (errcnt > 1000) {
 279                        pr_err("error: too many errors\n");
 280                        return -1;
 281                }
 282        }
 283
 284        return err;
 285}
 286
 287static int verify_all_eraseblocks(void)
 288{
 289        int err;
 290        unsigned int i;
 291
 292        pr_info("verifying all eraseblocks\n");
 293        for (i = 0; i < ebcnt; ++i) {
 294                if (bbt[i])
 295                        continue;
 296                err = verify_eraseblock(i);
 297                if (err)
 298                        return err;
 299                if (i % 256 == 0)
 300                        pr_info("verified up to eraseblock %u\n", i);
 301                cond_resched();
 302        }
 303        pr_info("verified %u eraseblocks\n", i);
 304        return 0;
 305}
 306
 307static int is_block_bad(int ebnum)
 308{
 309        int ret;
 310        loff_t addr = ebnum * mtd->erasesize;
 311
 312        ret = mtd_block_isbad(mtd, addr);
 313        if (ret)
 314                pr_info("block %d is bad\n", ebnum);
 315        return ret;
 316}
 317
 318static int scan_for_bad_eraseblocks(void)
 319{
 320        int i, bad = 0;
 321
 322        bbt = kmalloc(ebcnt, GFP_KERNEL);
 323        if (!bbt) {
 324                pr_err("error: cannot allocate memory\n");
 325                return -ENOMEM;
 326        }
 327
 328        pr_info("scanning for bad eraseblocks\n");
 329        for (i = 0; i < ebcnt; ++i) {
 330                bbt[i] = is_block_bad(i) ? 1 : 0;
 331                if (bbt[i])
 332                        bad += 1;
 333                cond_resched();
 334        }
 335        pr_info("scanned %d eraseblocks, %d are bad\n", i, bad);
 336        return 0;
 337}
 338
 339static int __init mtd_oobtest_init(void)
 340{
 341        int err = 0;
 342        unsigned int i;
 343        uint64_t tmp;
 344        struct mtd_oob_ops ops;
 345        loff_t addr = 0, addr0;
 346
 347        printk(KERN_INFO "\n");
 348        printk(KERN_INFO "=================================================\n");
 349
 350        if (dev < 0) {
 351                pr_info("Please specify a valid mtd-device via module parameter\n");
 352                pr_crit("CAREFUL: This test wipes all data on the specified MTD device!\n");
 353                return -EINVAL;
 354        }
 355
 356        pr_info("MTD device: %d\n", dev);
 357
 358        mtd = get_mtd_device(NULL, dev);
 359        if (IS_ERR(mtd)) {
 360                err = PTR_ERR(mtd);
 361                pr_err("error: cannot get MTD device\n");
 362                return err;
 363        }
 364
 365        if (mtd->type != MTD_NANDFLASH) {
 366                pr_info("this test requires NAND flash\n");
 367                goto out;
 368        }
 369
 370        tmp = mtd->size;
 371        do_div(tmp, mtd->erasesize);
 372        ebcnt = tmp;
 373        pgcnt = mtd->erasesize / mtd->writesize;
 374
 375        pr_info("MTD device size %llu, eraseblock size %u, "
 376               "page size %u, count of eraseblocks %u, pages per "
 377               "eraseblock %u, OOB size %u\n",
 378               (unsigned long long)mtd->size, mtd->erasesize,
 379               mtd->writesize, ebcnt, pgcnt, mtd->oobsize);
 380
 381        err = -ENOMEM;
 382        readbuf = kmalloc(mtd->erasesize, GFP_KERNEL);
 383        if (!readbuf) {
 384                pr_err("error: cannot allocate memory\n");
 385                goto out;
 386        }
 387        writebuf = kmalloc(mtd->erasesize, GFP_KERNEL);
 388        if (!writebuf) {
 389                pr_err("error: cannot allocate memory\n");
 390                goto out;
 391        }
 392
 393        err = scan_for_bad_eraseblocks();
 394        if (err)
 395                goto out;
 396
 397        use_offset = 0;
 398        use_len = mtd->ecclayout->oobavail;
 399        use_len_max = mtd->ecclayout->oobavail;
 400        vary_offset = 0;
 401
 402        /* First test: write all OOB, read it back and verify */
 403        pr_info("test 1 of 5\n");
 404
 405        err = erase_whole_device();
 406        if (err)
 407                goto out;
 408
 409        prandom_seed_state(&rnd_state, 1);
 410        err = write_whole_device();
 411        if (err)
 412                goto out;
 413
 414        prandom_seed_state(&rnd_state, 1);
 415        err = verify_all_eraseblocks();
 416        if (err)
 417                goto out;
 418
 419        /*
 420         * Second test: write all OOB, a block at a time, read it back and
 421         * verify.
 422         */
 423        pr_info("test 2 of 5\n");
 424
 425        err = erase_whole_device();
 426        if (err)
 427                goto out;
 428
 429        prandom_seed_state(&rnd_state, 3);
 430        err = write_whole_device();
 431        if (err)
 432                goto out;
 433
 434        /* Check all eraseblocks */
 435        prandom_seed_state(&rnd_state, 3);
 436        pr_info("verifying all eraseblocks\n");
 437        for (i = 0; i < ebcnt; ++i) {
 438                if (bbt[i])
 439                        continue;
 440                err = verify_eraseblock_in_one_go(i);
 441                if (err)
 442                        goto out;
 443                if (i % 256 == 0)
 444                        pr_info("verified up to eraseblock %u\n", i);
 445                cond_resched();
 446        }
 447        pr_info("verified %u eraseblocks\n", i);
 448
 449        /*
 450         * Third test: write OOB at varying offsets and lengths, read it back
 451         * and verify.
 452         */
 453        pr_info("test 3 of 5\n");
 454
 455        err = erase_whole_device();
 456        if (err)
 457                goto out;
 458
 459        /* Write all eraseblocks */
 460        use_offset = 0;
 461        use_len = mtd->ecclayout->oobavail;
 462        use_len_max = mtd->ecclayout->oobavail;
 463        vary_offset = 1;
 464        prandom_seed_state(&rnd_state, 5);
 465
 466        err = write_whole_device();
 467        if (err)
 468                goto out;
 469
 470        /* Check all eraseblocks */
 471        use_offset = 0;
 472        use_len = mtd->ecclayout->oobavail;
 473        use_len_max = mtd->ecclayout->oobavail;
 474        vary_offset = 1;
 475        prandom_seed_state(&rnd_state, 5);
 476        err = verify_all_eraseblocks();
 477        if (err)
 478                goto out;
 479
 480        use_offset = 0;
 481        use_len = mtd->ecclayout->oobavail;
 482        use_len_max = mtd->ecclayout->oobavail;
 483        vary_offset = 0;
 484
 485        /* Fourth test: try to write off end of device */
 486        pr_info("test 4 of 5\n");
 487
 488        err = erase_whole_device();
 489        if (err)
 490                goto out;
 491
 492        addr0 = 0;
 493        for (i = 0; i < ebcnt && bbt[i]; ++i)
 494                addr0 += mtd->erasesize;
 495
 496        /* Attempt to write off end of OOB */
 497        ops.mode      = MTD_OPS_AUTO_OOB;
 498        ops.len       = 0;
 499        ops.retlen    = 0;
 500        ops.ooblen    = 1;
 501        ops.oobretlen = 0;
 502        ops.ooboffs   = mtd->ecclayout->oobavail;
 503        ops.datbuf    = NULL;
 504        ops.oobbuf    = writebuf;
 505        pr_info("attempting to start write past end of OOB\n");
 506        pr_info("an error is expected...\n");
 507        err = mtd_write_oob(mtd, addr0, &ops);
 508        if (err) {
 509                pr_info("error occurred as expected\n");
 510                err = 0;
 511        } else {
 512                pr_err("error: can write past end of OOB\n");
 513                errcnt += 1;
 514        }
 515
 516        /* Attempt to read off end of OOB */
 517        ops.mode      = MTD_OPS_AUTO_OOB;
 518        ops.len       = 0;
 519        ops.retlen    = 0;
 520        ops.ooblen    = 1;
 521        ops.oobretlen = 0;
 522        ops.ooboffs   = mtd->ecclayout->oobavail;
 523        ops.datbuf    = NULL;
 524        ops.oobbuf    = readbuf;
 525        pr_info("attempting to start read past end of OOB\n");
 526        pr_info("an error is expected...\n");
 527        err = mtd_read_oob(mtd, addr0, &ops);
 528        if (err) {
 529                pr_info("error occurred as expected\n");
 530                err = 0;
 531        } else {
 532                pr_err("error: can read past end of OOB\n");
 533                errcnt += 1;
 534        }
 535
 536        if (bbt[ebcnt - 1])
 537                pr_info("skipping end of device tests because last "
 538                       "block is bad\n");
 539        else {
 540                /* Attempt to write off end of device */
 541                ops.mode      = MTD_OPS_AUTO_OOB;
 542                ops.len       = 0;
 543                ops.retlen    = 0;
 544                ops.ooblen    = mtd->ecclayout->oobavail + 1;
 545                ops.oobretlen = 0;
 546                ops.ooboffs   = 0;
 547                ops.datbuf    = NULL;
 548                ops.oobbuf    = writebuf;
 549                pr_info("attempting to write past end of device\n");
 550                pr_info("an error is expected...\n");
 551                err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops);
 552                if (err) {
 553                        pr_info("error occurred as expected\n");
 554                        err = 0;
 555                } else {
 556                        pr_err("error: wrote past end of device\n");
 557                        errcnt += 1;
 558                }
 559
 560                /* Attempt to read off end of device */
 561                ops.mode      = MTD_OPS_AUTO_OOB;
 562                ops.len       = 0;
 563                ops.retlen    = 0;
 564                ops.ooblen    = mtd->ecclayout->oobavail + 1;
 565                ops.oobretlen = 0;
 566                ops.ooboffs   = 0;
 567                ops.datbuf    = NULL;
 568                ops.oobbuf    = readbuf;
 569                pr_info("attempting to read past end of device\n");
 570                pr_info("an error is expected...\n");
 571                err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops);
 572                if (err) {
 573                        pr_info("error occurred as expected\n");
 574                        err = 0;
 575                } else {
 576                        pr_err("error: read past end of device\n");
 577                        errcnt += 1;
 578                }
 579
 580                err = erase_eraseblock(ebcnt - 1);
 581                if (err)
 582                        goto out;
 583
 584                /* Attempt to write off end of device */
 585                ops.mode      = MTD_OPS_AUTO_OOB;
 586                ops.len       = 0;
 587                ops.retlen    = 0;
 588                ops.ooblen    = mtd->ecclayout->oobavail;
 589                ops.oobretlen = 0;
 590                ops.ooboffs   = 1;
 591                ops.datbuf    = NULL;
 592                ops.oobbuf    = writebuf;
 593                pr_info("attempting to write past end of device\n");
 594                pr_info("an error is expected...\n");
 595                err = mtd_write_oob(mtd, mtd->size - mtd->writesize, &ops);
 596                if (err) {
 597                        pr_info("error occurred as expected\n");
 598                        err = 0;
 599                } else {
 600                        pr_err("error: wrote past end of device\n");
 601                        errcnt += 1;
 602                }
 603
 604                /* Attempt to read off end of device */
 605                ops.mode      = MTD_OPS_AUTO_OOB;
 606                ops.len       = 0;
 607                ops.retlen    = 0;
 608                ops.ooblen    = mtd->ecclayout->oobavail;
 609                ops.oobretlen = 0;
 610                ops.ooboffs   = 1;
 611                ops.datbuf    = NULL;
 612                ops.oobbuf    = readbuf;
 613                pr_info("attempting to read past end of device\n");
 614                pr_info("an error is expected...\n");
 615                err = mtd_read_oob(mtd, mtd->size - mtd->writesize, &ops);
 616                if (err) {
 617                        pr_info("error occurred as expected\n");
 618                        err = 0;
 619                } else {
 620                        pr_err("error: read past end of device\n");
 621                        errcnt += 1;
 622                }
 623        }
 624
 625        /* Fifth test: write / read across block boundaries */
 626        pr_info("test 5 of 5\n");
 627
 628        /* Erase all eraseblocks */
 629        err = erase_whole_device();
 630        if (err)
 631                goto out;
 632
 633        /* Write all eraseblocks */
 634        prandom_seed_state(&rnd_state, 11);
 635        pr_info("writing OOBs of whole device\n");
 636        for (i = 0; i < ebcnt - 1; ++i) {
 637                int cnt = 2;
 638                int pg;
 639                size_t sz = mtd->ecclayout->oobavail;
 640                if (bbt[i] || bbt[i + 1])
 641                        continue;
 642                addr = (i + 1) * mtd->erasesize - mtd->writesize;
 643                for (pg = 0; pg < cnt; ++pg) {
 644                        prandom_bytes_state(&rnd_state, writebuf, sz);
 645                        ops.mode      = MTD_OPS_AUTO_OOB;
 646                        ops.len       = 0;
 647                        ops.retlen    = 0;
 648                        ops.ooblen    = sz;
 649                        ops.oobretlen = 0;
 650                        ops.ooboffs   = 0;
 651                        ops.datbuf    = NULL;
 652                        ops.oobbuf    = writebuf;
 653                        err = mtd_write_oob(mtd, addr, &ops);
 654                        if (err)
 655                                goto out;
 656                        if (i % 256 == 0)
 657                                pr_info("written up to eraseblock %u\n", i);
 658                        cond_resched();
 659                        addr += mtd->writesize;
 660                }
 661        }
 662        pr_info("written %u eraseblocks\n", i);
 663
 664        /* Check all eraseblocks */
 665        prandom_seed_state(&rnd_state, 11);
 666        pr_info("verifying all eraseblocks\n");
 667        for (i = 0; i < ebcnt - 1; ++i) {
 668                if (bbt[i] || bbt[i + 1])
 669                        continue;
 670                prandom_bytes_state(&rnd_state, writebuf,
 671                                        mtd->ecclayout->oobavail * 2);
 672                addr = (i + 1) * mtd->erasesize - mtd->writesize;
 673                ops.mode      = MTD_OPS_AUTO_OOB;
 674                ops.len       = 0;
 675                ops.retlen    = 0;
 676                ops.ooblen    = mtd->ecclayout->oobavail * 2;
 677                ops.oobretlen = 0;
 678                ops.ooboffs   = 0;
 679                ops.datbuf    = NULL;
 680                ops.oobbuf    = readbuf;
 681                err = mtd_read_oob(mtd, addr, &ops);
 682                if (err)
 683                        goto out;
 684                if (memcmp(readbuf, writebuf, mtd->ecclayout->oobavail * 2)) {
 685                        pr_err("error: verify failed at %#llx\n",
 686                               (long long)addr);
 687                        errcnt += 1;
 688                        if (errcnt > 1000) {
 689                                pr_err("error: too many errors\n");
 690                                goto out;
 691                        }
 692                }
 693                if (i % 256 == 0)
 694                        pr_info("verified up to eraseblock %u\n", i);
 695                cond_resched();
 696        }
 697        pr_info("verified %u eraseblocks\n", i);
 698
 699        pr_info("finished with %d errors\n", errcnt);
 700out:
 701        kfree(bbt);
 702        kfree(writebuf);
 703        kfree(readbuf);
 704        put_mtd_device(mtd);
 705        if (err)
 706                pr_info("error %d occurred\n", err);
 707        printk(KERN_INFO "=================================================\n");
 708        return err;
 709}
 710module_init(mtd_oobtest_init);
 711
 712static void __exit mtd_oobtest_exit(void)
 713{
 714        return;
 715}
 716module_exit(mtd_oobtest_exit);
 717
 718MODULE_DESCRIPTION("Out-of-band test module");
 719MODULE_AUTHOR("Adrian Hunter");
 720MODULE_LICENSE("GPL");
 721