Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

MTD: tests: add mtd_readtest

A simple tests which reads whole MTD device one page at a time.

Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>

+253
+253
drivers/mtd/tests/mtd_readtest.c
··· 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 + * Check MTD device read. 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 + 29 + #define PRINT_PREF KERN_INFO "mtd_readtest: " 30 + 31 + static int dev; 32 + module_param(dev, int, S_IRUGO); 33 + MODULE_PARM_DESC(dev, "MTD device number to use"); 34 + 35 + static struct mtd_info *mtd; 36 + static unsigned char *iobuf; 37 + static unsigned char *iobuf1; 38 + static unsigned char *bbt; 39 + 40 + static int pgsize; 41 + static int ebcnt; 42 + static int pgcnt; 43 + 44 + static int read_eraseblock_by_page(int ebnum) 45 + { 46 + size_t read = 0; 47 + int i, ret, err = 0; 48 + loff_t addr = ebnum * mtd->erasesize; 49 + void *buf = iobuf; 50 + void *oobbuf = iobuf1; 51 + 52 + for (i = 0; i < pgcnt; i++) { 53 + memset(buf, 0 , pgcnt); 54 + ret = mtd->read(mtd, addr, pgsize, &read, buf); 55 + if (ret == -EUCLEAN) 56 + ret = 0; 57 + if (ret || read != pgsize) { 58 + printk(PRINT_PREF "error: read failed at %#llx\n", 59 + (long long)addr); 60 + if (!err) 61 + err = ret; 62 + if (!err) 63 + err = -EINVAL; 64 + } 65 + if (mtd->oobsize) { 66 + struct mtd_oob_ops ops; 67 + 68 + ops.mode = MTD_OOB_PLACE; 69 + ops.len = 0; 70 + ops.retlen = 0; 71 + ops.ooblen = mtd->oobsize; 72 + ops.oobretlen = 0; 73 + ops.ooboffs = 0; 74 + ops.datbuf = 0; 75 + ops.oobbuf = oobbuf; 76 + ret = mtd->read_oob(mtd, addr, &ops); 77 + if (ret || ops.oobretlen != mtd->oobsize) { 78 + printk(PRINT_PREF "error: read oob failed at " 79 + "%#llx\n", (long long)addr); 80 + if (!err) 81 + err = ret; 82 + if (!err) 83 + err = -EINVAL; 84 + } 85 + oobbuf += mtd->oobsize; 86 + } 87 + addr += pgsize; 88 + buf += pgsize; 89 + } 90 + 91 + return err; 92 + } 93 + 94 + static void dump_eraseblock(int ebnum) 95 + { 96 + int i, j, n; 97 + char line[128]; 98 + int pg, oob; 99 + 100 + printk(PRINT_PREF "dumping eraseblock %d\n", ebnum); 101 + n = mtd->erasesize; 102 + for (i = 0; i < n;) { 103 + char *p = line; 104 + 105 + p += sprintf(p, "%05x: ", i); 106 + for (j = 0; j < 32 && i < n; j++, i++) 107 + p += sprintf(p, "%02x", (unsigned int)iobuf[i]); 108 + printk(KERN_CRIT "%s\n", line); 109 + cond_resched(); 110 + } 111 + if (!mtd->oobsize) 112 + return; 113 + printk(PRINT_PREF "dumping oob from eraseblock %d\n", ebnum); 114 + n = mtd->oobsize; 115 + for (pg = 0, i = 0; pg < pgcnt; pg++) 116 + for (oob = 0; oob < n;) { 117 + char *p = line; 118 + 119 + p += sprintf(p, "%05x: ", i); 120 + for (j = 0; j < 32 && oob < n; j++, oob++, i++) 121 + p += sprintf(p, "%02x", 122 + (unsigned int)iobuf1[i]); 123 + printk(KERN_CRIT "%s\n", line); 124 + cond_resched(); 125 + } 126 + } 127 + 128 + static int is_block_bad(int ebnum) 129 + { 130 + loff_t addr = ebnum * mtd->erasesize; 131 + int ret; 132 + 133 + ret = mtd->block_isbad(mtd, addr); 134 + if (ret) 135 + printk(PRINT_PREF "block %d is bad\n", ebnum); 136 + return ret; 137 + } 138 + 139 + static int scan_for_bad_eraseblocks(void) 140 + { 141 + int i, bad = 0; 142 + 143 + bbt = kmalloc(ebcnt, GFP_KERNEL); 144 + if (!bbt) { 145 + printk(PRINT_PREF "error: cannot allocate memory\n"); 146 + return -ENOMEM; 147 + } 148 + memset(bbt, 0 , ebcnt); 149 + 150 + printk(PRINT_PREF "scanning for bad eraseblocks\n"); 151 + for (i = 0; i < ebcnt; ++i) { 152 + bbt[i] = is_block_bad(i) ? 1 : 0; 153 + if (bbt[i]) 154 + bad += 1; 155 + cond_resched(); 156 + } 157 + printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); 158 + return 0; 159 + } 160 + 161 + static int __init mtd_readtest_init(void) 162 + { 163 + uint64_t tmp; 164 + int err, i; 165 + 166 + printk(KERN_INFO "\n"); 167 + printk(KERN_INFO "=================================================\n"); 168 + printk(PRINT_PREF "MTD device: %d\n", dev); 169 + 170 + mtd = get_mtd_device(NULL, dev); 171 + if (IS_ERR(mtd)) { 172 + err = PTR_ERR(mtd); 173 + printk(PRINT_PREF "error: Cannot get MTD device\n"); 174 + return err; 175 + } 176 + 177 + if (mtd->writesize == 1) { 178 + printk(PRINT_PREF "not NAND flash, assume page size is 512 " 179 + "bytes.\n"); 180 + pgsize = 512; 181 + } else 182 + pgsize = mtd->writesize; 183 + 184 + tmp = mtd->size; 185 + do_div(tmp, mtd->erasesize); 186 + ebcnt = tmp; 187 + pgcnt = mtd->erasesize / mtd->writesize; 188 + 189 + printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " 190 + "page size %u, count of eraseblocks %u, pages per " 191 + "eraseblock %u, OOB size %u\n", 192 + (unsigned long long)mtd->size, mtd->erasesize, 193 + pgsize, ebcnt, pgcnt, mtd->oobsize); 194 + 195 + err = -ENOMEM; 196 + iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); 197 + if (!iobuf) { 198 + printk(PRINT_PREF "error: cannot allocate memory\n"); 199 + goto out; 200 + } 201 + iobuf1 = kmalloc(mtd->erasesize, GFP_KERNEL); 202 + if (!iobuf1) { 203 + printk(PRINT_PREF "error: cannot allocate memory\n"); 204 + goto out; 205 + } 206 + 207 + err = scan_for_bad_eraseblocks(); 208 + if (err) 209 + goto out; 210 + 211 + /* Read all eraseblocks 1 page at a time */ 212 + printk(PRINT_PREF "testing page read\n"); 213 + for (i = 0; i < ebcnt; ++i) { 214 + int ret; 215 + 216 + if (bbt[i]) 217 + continue; 218 + ret = read_eraseblock_by_page(i); 219 + if (ret) { 220 + dump_eraseblock(i); 221 + if (!err) 222 + err = ret; 223 + } 224 + cond_resched(); 225 + } 226 + 227 + if (err) 228 + printk(PRINT_PREF "finished with errors\n"); 229 + else 230 + printk(PRINT_PREF "finished\n"); 231 + 232 + out: 233 + 234 + kfree(iobuf); 235 + kfree(iobuf1); 236 + kfree(bbt); 237 + put_mtd_device(mtd); 238 + if (err) 239 + printk(PRINT_PREF "error %d occurred\n", err); 240 + printk(KERN_INFO "=================================================\n"); 241 + return err; 242 + } 243 + module_init(mtd_readtest_init); 244 + 245 + static void __exit mtd_readtest_exit(void) 246 + { 247 + return; 248 + } 249 + module_exit(mtd_readtest_exit); 250 + 251 + MODULE_DESCRIPTION("Read test module"); 252 + MODULE_AUTHOR("Adrian Hunter"); 253 + MODULE_LICENSE("GPL");