Blame view
drivers/mtd/tests/subpagetest.c
10.6 KB
bf60862a5
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/* * Copyright (C) 2006-2007 Nokia Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Test sub-page read and write on MTD device. * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> * */ |
cd66a2df7
|
21 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
bf60862a5
|
22 23 24 25 26 |
#include <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/err.h> #include <linux/mtd/mtd.h> |
5a0e3ad6a
|
27 |
#include <linux/slab.h> |
bf60862a5
|
28 |
#include <linux/sched.h> |
a312b78b0
|
29 |
#include <linux/random.h> |
bf60862a5
|
30 |
|
725cd71c0
|
31 |
#include "mtd_test.h" |
7406060e2
|
32 |
static int dev = -EINVAL; |
bf60862a5
|
33 34 35 36 37 38 39 40 41 42 43 44 45 |
module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); static struct mtd_info *mtd; static unsigned char *writebuf; static unsigned char *readbuf; static unsigned char *bbt; static int subpgsize; static int bufsize; static int ebcnt; static int pgcnt; static int errcnt; |
a312b78b0
|
46 |
static struct rnd_state rnd_state; |
bf60862a5
|
47 48 49 50 51 |
static inline void clear_data(unsigned char *buf, size_t len) { memset(buf, 0, len); } |
bf60862a5
|
52 53 |
static int write_eraseblock(int ebnum) { |
30fa98480
|
54 |
size_t written; |
bf60862a5
|
55 |
int err = 0; |
1001ff7a4
|
56 |
loff_t addr = (loff_t)ebnum * mtd->erasesize; |
bf60862a5
|
57 |
|
a312b78b0
|
58 |
prandom_bytes_state(&rnd_state, writebuf, subpgsize); |
eda95cbf7
|
59 |
err = mtd_write(mtd, addr, subpgsize, &written, writebuf); |
bf60862a5
|
60 |
if (unlikely(err || written != subpgsize)) { |
cd66a2df7
|
61 62 |
pr_err("error: write failed at %#llx ", |
bf60862a5
|
63 64 |
(long long)addr); if (written != subpgsize) { |
cd66a2df7
|
65 66 67 68 |
pr_err(" write size: %#x ", subpgsize); pr_err(" written: %#zx ", written); |
bf60862a5
|
69 70 71 72 73 |
} return err ? err : -1; } addr += subpgsize; |
a312b78b0
|
74 |
prandom_bytes_state(&rnd_state, writebuf, subpgsize); |
eda95cbf7
|
75 |
err = mtd_write(mtd, addr, subpgsize, &written, writebuf); |
bf60862a5
|
76 |
if (unlikely(err || written != subpgsize)) { |
cd66a2df7
|
77 78 |
pr_err("error: write failed at %#llx ", |
bf60862a5
|
79 80 |
(long long)addr); if (written != subpgsize) { |
cd66a2df7
|
81 82 83 84 |
pr_err(" write size: %#x ", subpgsize); pr_err(" written: %#zx ", written); |
bf60862a5
|
85 86 87 88 89 90 91 92 93 |
} return err ? err : -1; } return err; } static int write_eraseblock2(int ebnum) { |
30fa98480
|
94 |
size_t written; |
bf60862a5
|
95 |
int err = 0, k; |
1001ff7a4
|
96 |
loff_t addr = (loff_t)ebnum * mtd->erasesize; |
bf60862a5
|
97 98 99 100 |
for (k = 1; k < 33; ++k) { if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize) break; |
a312b78b0
|
101 |
prandom_bytes_state(&rnd_state, writebuf, subpgsize * k); |
eda95cbf7
|
102 |
err = mtd_write(mtd, addr, subpgsize * k, &written, writebuf); |
bf60862a5
|
103 |
if (unlikely(err || written != subpgsize * k)) { |
cd66a2df7
|
104 105 |
pr_err("error: write failed at %#llx ", |
bf60862a5
|
106 107 |
(long long)addr); if (written != subpgsize) { |
cd66a2df7
|
108 109 |
pr_err(" write size: %#x ", |
bf60862a5
|
110 |
subpgsize * k); |
cd66a2df7
|
111 112 |
pr_err(" written: %#08zx ", |
bf60862a5
|
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
written); } return err ? err : -1; } addr += subpgsize * k; } return err; } static void print_subpage(unsigned char *p) { int i, j; for (i = 0; i < subpgsize; ) { for (j = 0; i < subpgsize && j < 32; ++i, ++j) printk("%02x", *p++); printk(" "); } } static int verify_eraseblock(int ebnum) { |
30fa98480
|
137 |
size_t read; |
bf60862a5
|
138 |
int err = 0; |
1001ff7a4
|
139 |
loff_t addr = (loff_t)ebnum * mtd->erasesize; |
bf60862a5
|
140 |
|
a312b78b0
|
141 |
prandom_bytes_state(&rnd_state, writebuf, subpgsize); |
bf60862a5
|
142 |
clear_data(readbuf, subpgsize); |
329ad399a
|
143 |
err = mtd_read(mtd, addr, subpgsize, &read, readbuf); |
bf60862a5
|
144 |
if (unlikely(err || read != subpgsize)) { |
d57f40544
|
145 |
if (mtd_is_bitflip(err) && read == subpgsize) { |
cd66a2df7
|
146 147 |
pr_info("ECC correction at %#llx ", |
bf60862a5
|
148 149 150 |
(long long)addr); err = 0; } else { |
cd66a2df7
|
151 152 |
pr_err("error: read failed at %#llx ", |
bf60862a5
|
153 154 155 156 157 |
(long long)addr); return err ? err : -1; } } if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { |
cd66a2df7
|
158 159 |
pr_err("error: verify failed at %#llx ", |
bf60862a5
|
160 |
(long long)addr); |
cd66a2df7
|
161 162 |
pr_info("------------- written---------------- "); |
bf60862a5
|
163 |
print_subpage(writebuf); |
cd66a2df7
|
164 165 |
pr_info("------------- read ------------------ "); |
bf60862a5
|
166 |
print_subpage(readbuf); |
cd66a2df7
|
167 168 |
pr_info("------------------------------------- "); |
bf60862a5
|
169 170 171 172 |
errcnt += 1; } addr += subpgsize; |
a312b78b0
|
173 |
prandom_bytes_state(&rnd_state, writebuf, subpgsize); |
bf60862a5
|
174 |
clear_data(readbuf, subpgsize); |
329ad399a
|
175 |
err = mtd_read(mtd, addr, subpgsize, &read, readbuf); |
bf60862a5
|
176 |
if (unlikely(err || read != subpgsize)) { |
d57f40544
|
177 |
if (mtd_is_bitflip(err) && read == subpgsize) { |
cd66a2df7
|
178 179 |
pr_info("ECC correction at %#llx ", |
bf60862a5
|
180 181 182 |
(long long)addr); err = 0; } else { |
cd66a2df7
|
183 184 |
pr_err("error: read failed at %#llx ", |
bf60862a5
|
185 186 187 188 189 |
(long long)addr); return err ? err : -1; } } if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { |
cd66a2df7
|
190 191 |
pr_info("error: verify failed at %#llx ", |
bf60862a5
|
192 |
(long long)addr); |
cd66a2df7
|
193 194 |
pr_info("------------- written---------------- "); |
bf60862a5
|
195 |
print_subpage(writebuf); |
cd66a2df7
|
196 197 |
pr_info("------------- read ------------------ "); |
bf60862a5
|
198 |
print_subpage(readbuf); |
cd66a2df7
|
199 200 |
pr_info("------------------------------------- "); |
bf60862a5
|
201 202 203 204 205 206 207 208 |
errcnt += 1; } return err; } static int verify_eraseblock2(int ebnum) { |
30fa98480
|
209 |
size_t read; |
bf60862a5
|
210 |
int err = 0, k; |
1001ff7a4
|
211 |
loff_t addr = (loff_t)ebnum * mtd->erasesize; |
bf60862a5
|
212 213 214 215 |
for (k = 1; k < 33; ++k) { if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize) break; |
a312b78b0
|
216 |
prandom_bytes_state(&rnd_state, writebuf, subpgsize * k); |
bf60862a5
|
217 |
clear_data(readbuf, subpgsize * k); |
329ad399a
|
218 |
err = mtd_read(mtd, addr, subpgsize * k, &read, readbuf); |
bf60862a5
|
219 |
if (unlikely(err || read != subpgsize * k)) { |
d57f40544
|
220 |
if (mtd_is_bitflip(err) && read == subpgsize * k) { |
cd66a2df7
|
221 222 |
pr_info("ECC correction at %#llx ", |
bf60862a5
|
223 224 225 |
(long long)addr); err = 0; } else { |
cd66a2df7
|
226 |
pr_err("error: read failed at " |
bf60862a5
|
227 228 229 230 231 232 |
"%#llx ", (long long)addr); return err ? err : -1; } } if (unlikely(memcmp(readbuf, writebuf, subpgsize * k))) { |
cd66a2df7
|
233 234 |
pr_err("error: verify failed at %#llx ", |
bf60862a5
|
235 236 237 238 239 240 241 242 243 244 245 246 |
(long long)addr); errcnt += 1; } addr += subpgsize * k; } return err; } static int verify_eraseblock_ff(int ebnum) { uint32_t j; |
30fa98480
|
247 |
size_t read; |
bf60862a5
|
248 |
int err = 0; |
1001ff7a4
|
249 |
loff_t addr = (loff_t)ebnum * mtd->erasesize; |
bf60862a5
|
250 251 252 253 |
memset(writebuf, 0xff, subpgsize); for (j = 0; j < mtd->erasesize / subpgsize; ++j) { clear_data(readbuf, subpgsize); |
329ad399a
|
254 |
err = mtd_read(mtd, addr, subpgsize, &read, readbuf); |
bf60862a5
|
255 |
if (unlikely(err || read != subpgsize)) { |
d57f40544
|
256 |
if (mtd_is_bitflip(err) && read == subpgsize) { |
cd66a2df7
|
257 258 |
pr_info("ECC correction at %#llx ", |
bf60862a5
|
259 260 261 |
(long long)addr); err = 0; } else { |
cd66a2df7
|
262 |
pr_err("error: read failed at " |
bf60862a5
|
263 264 265 266 267 268 |
"%#llx ", (long long)addr); return err ? err : -1; } } if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { |
cd66a2df7
|
269 |
pr_err("error: verify 0xff failed at " |
bf60862a5
|
270 271 272 273 274 275 276 277 278 279 280 281 282 283 |
"%#llx ", (long long)addr); errcnt += 1; } addr += subpgsize; } return err; } static int verify_all_eraseblocks_ff(void) { int err; unsigned int i; |
cd66a2df7
|
284 285 |
pr_info("verifying all eraseblocks for 0xff "); |
bf60862a5
|
286 287 288 289 290 291 292 |
for (i = 0; i < ebcnt; ++i) { if (bbt[i]) continue; err = verify_eraseblock_ff(i); if (err) return err; if (i % 256 == 0) |
cd66a2df7
|
293 294 |
pr_info("verified up to eraseblock %u ", i); |
bf60862a5
|
295 296 |
cond_resched(); } |
cd66a2df7
|
297 298 |
pr_info("verified %u eraseblocks ", i); |
bf60862a5
|
299 300 |
return 0; } |
bf60862a5
|
301 302 303 304 305 306 307 308 309 310 |
static int __init mtd_subpagetest_init(void) { int err = 0; uint32_t i; uint64_t tmp; printk(KERN_INFO " "); printk(KERN_INFO "================================================= "); |
7406060e2
|
311 312 |
if (dev < 0) { |
064a7694b
|
313 314 |
pr_info("Please specify a valid mtd-device via module parameter "); |
cd66a2df7
|
315 316 |
pr_crit("CAREFUL: This test wipes all data on the specified MTD device! "); |
7406060e2
|
317 318 |
return -EINVAL; } |
cd66a2df7
|
319 320 |
pr_info("MTD device: %d ", dev); |
bf60862a5
|
321 322 323 324 |
mtd = get_mtd_device(NULL, dev); if (IS_ERR(mtd)) { err = PTR_ERR(mtd); |
cd66a2df7
|
325 326 |
pr_err("error: cannot get MTD device "); |
bf60862a5
|
327 328 |
return err; } |
818b97392
|
329 |
if (!mtd_type_is_nand(mtd)) { |
cd66a2df7
|
330 331 |
pr_info("this test requires NAND flash "); |
bf60862a5
|
332 333 334 335 |
goto out; } subpgsize = mtd->writesize >> mtd->subpage_sft; |
7b7e905ec
|
336 337 338 339 |
tmp = mtd->size; do_div(tmp, mtd->erasesize); ebcnt = tmp; pgcnt = mtd->erasesize / mtd->writesize; |
cd66a2df7
|
340 |
pr_info("MTD device size %llu, eraseblock size %u, " |
bf60862a5
|
341 342 343 344 345 346 347 348 349 |
"page size %u, subpage size %u, count of eraseblocks %u, " "pages per eraseblock %u, OOB size %u ", (unsigned long long)mtd->size, mtd->erasesize, mtd->writesize, subpgsize, ebcnt, pgcnt, mtd->oobsize); err = -ENOMEM; bufsize = subpgsize * 32; writebuf = kmalloc(bufsize, GFP_KERNEL); |
33777e667
|
350 |
if (!writebuf) |
bf60862a5
|
351 |
goto out; |
bf60862a5
|
352 |
readbuf = kmalloc(bufsize, GFP_KERNEL); |
33777e667
|
353 |
if (!readbuf) |
bf60862a5
|
354 |
goto out; |
725cd71c0
|
355 356 357 |
bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) goto out; |
bf60862a5
|
358 |
|
725cd71c0
|
359 |
err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); |
bf60862a5
|
360 361 |
if (err) goto out; |
725cd71c0
|
362 |
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); |
bf60862a5
|
363 364 |
if (err) goto out; |
cd66a2df7
|
365 366 |
pr_info("writing whole device "); |
a312b78b0
|
367 |
prandom_seed_state(&rnd_state, 1); |
bf60862a5
|
368 369 370 371 372 373 374 |
for (i = 0; i < ebcnt; ++i) { if (bbt[i]) continue; err = write_eraseblock(i); if (unlikely(err)) goto out; if (i % 256 == 0) |
cd66a2df7
|
375 376 |
pr_info("written up to eraseblock %u ", i); |
bf60862a5
|
377 378 |
cond_resched(); } |
cd66a2df7
|
379 380 |
pr_info("written %u eraseblocks ", i); |
bf60862a5
|
381 |
|
a312b78b0
|
382 |
prandom_seed_state(&rnd_state, 1); |
cd66a2df7
|
383 384 |
pr_info("verifying all eraseblocks "); |
bf60862a5
|
385 386 387 388 389 390 391 |
for (i = 0; i < ebcnt; ++i) { if (bbt[i]) continue; err = verify_eraseblock(i); if (unlikely(err)) goto out; if (i % 256 == 0) |
cd66a2df7
|
392 393 |
pr_info("verified up to eraseblock %u ", i); |
bf60862a5
|
394 395 |
cond_resched(); } |
cd66a2df7
|
396 397 |
pr_info("verified %u eraseblocks ", i); |
bf60862a5
|
398 |
|
725cd71c0
|
399 |
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); |
bf60862a5
|
400 401 402 403 404 405 406 407 |
if (err) goto out; err = verify_all_eraseblocks_ff(); if (err) goto out; /* Write all eraseblocks */ |
a312b78b0
|
408 |
prandom_seed_state(&rnd_state, 3); |
cd66a2df7
|
409 410 |
pr_info("writing whole device "); |
bf60862a5
|
411 412 413 414 415 416 417 |
for (i = 0; i < ebcnt; ++i) { if (bbt[i]) continue; err = write_eraseblock2(i); if (unlikely(err)) goto out; if (i % 256 == 0) |
cd66a2df7
|
418 419 |
pr_info("written up to eraseblock %u ", i); |
bf60862a5
|
420 421 |
cond_resched(); } |
cd66a2df7
|
422 423 |
pr_info("written %u eraseblocks ", i); |
bf60862a5
|
424 425 |
/* Check all eraseblocks */ |
a312b78b0
|
426 |
prandom_seed_state(&rnd_state, 3); |
cd66a2df7
|
427 428 |
pr_info("verifying all eraseblocks "); |
bf60862a5
|
429 430 431 432 433 434 435 |
for (i = 0; i < ebcnt; ++i) { if (bbt[i]) continue; err = verify_eraseblock2(i); if (unlikely(err)) goto out; if (i % 256 == 0) |
cd66a2df7
|
436 437 |
pr_info("verified up to eraseblock %u ", i); |
bf60862a5
|
438 439 |
cond_resched(); } |
cd66a2df7
|
440 441 |
pr_info("verified %u eraseblocks ", i); |
bf60862a5
|
442 |
|
725cd71c0
|
443 |
err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); |
bf60862a5
|
444 445 446 447 448 449 |
if (err) goto out; err = verify_all_eraseblocks_ff(); if (err) goto out; |
cd66a2df7
|
450 451 |
pr_info("finished with %d errors ", errcnt); |
bf60862a5
|
452 453 454 455 456 457 458 |
out: kfree(bbt); kfree(readbuf); kfree(writebuf); put_mtd_device(mtd); if (err) |
cd66a2df7
|
459 460 |
pr_info("error %d occurred ", err); |
bf60862a5
|
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 |
printk(KERN_INFO "================================================= "); return err; } module_init(mtd_subpagetest_init); static void __exit mtd_subpagetest_exit(void) { return; } module_exit(mtd_subpagetest_exit); MODULE_DESCRIPTION("Subpage test module"); MODULE_AUTHOR("Adrian Hunter"); MODULE_LICENSE("GPL"); |