aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIwo Mergler <Iwo.Mergler@netcommwireless.com>2012-08-30 18:59:48 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2012-09-29 10:46:58 -0400
commit3cf06f4f85aea715e8caf8540760faff2fbf86d6 (patch)
tree23edc4b1b32ac093fb90672b81519f8901b14864
parent7baf04261062826ea225ab23e07c541e279143fa (diff)
mtd: tests: test for multi-bit error correction
This tests ECC biterror recovery on a single NAND page. Mostly intended to test ECC hardware and low-level NAND driver. There are two test modes: 0 - artificially inserting bit errors until the ECC fails This is the default method and fairly quick. It should be independent of the quality of the FLASH. 1 - re-writing the same pattern repeatedly until the ECC fails. This method relies on the physics of NAND FLASH to eventually generate '0' bits if '1' has been written sufficient times. Depending on the NAND, the first bit errors will appear after 1000 or more writes and then will usually snowball, reaching the limits of the ECC quickly. The test stops after 10000 cycles, should your FLASH be exceptionally good and not generate bit errors before that. Try a different page offset in that case. Please note that neither of these tests will significantly 'use up' any FLASH endurance. Only a maximum of two erase operations will be performed. Signed-off-by: Iwo Mergler <Iwo.Mergler@netcommwireless.com.au> Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-rw-r--r--drivers/mtd/tests/Makefile1
-rw-r--r--drivers/mtd/tests/mtd_nandbiterrs.c460
2 files changed, 461 insertions, 0 deletions
diff --git a/drivers/mtd/tests/Makefile b/drivers/mtd/tests/Makefile
index b44dcab940d8..bd0065c0d359 100644
--- a/drivers/mtd/tests/Makefile
+++ b/drivers/mtd/tests/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_MTD_TESTS) += mtd_stresstest.o
6obj-$(CONFIG_MTD_TESTS) += mtd_subpagetest.o 6obj-$(CONFIG_MTD_TESTS) += mtd_subpagetest.o
7obj-$(CONFIG_MTD_TESTS) += mtd_torturetest.o 7obj-$(CONFIG_MTD_TESTS) += mtd_torturetest.o
8obj-$(CONFIG_MTD_TESTS) += mtd_nandecctest.o 8obj-$(CONFIG_MTD_TESTS) += mtd_nandecctest.o
9obj-$(CONFIG_MTD_TESTS) += mtd_nandbiterrs.o
diff --git a/drivers/mtd/tests/mtd_nandbiterrs.c b/drivers/mtd/tests/mtd_nandbiterrs.c
new file mode 100644
index 000000000000..cc8d62cb280c
--- /dev/null
+++ b/drivers/mtd/tests/mtd_nandbiterrs.c
@@ -0,0 +1,460 @@
1/*
2 * Copyright © 2012 NetCommWireless
3 * Iwo Mergler <Iwo.Mergler@netcommwireless.com.au>
4 *
5 * Test for multi-bit error recovery on a NAND page This mostly tests the
6 * ECC controller / driver.
7 *
8 * There are two test modes:
9 *
10 * 0 - artificially inserting bit errors until the ECC fails
11 * This is the default method and fairly quick. It should
12 * be independent of the quality of the FLASH.
13 *
14 * 1 - re-writing the same pattern repeatedly until the ECC fails.
15 * This method relies on the physics of NAND FLASH to eventually
16 * generate '0' bits if '1' has been written sufficient times.
17 * Depending on the NAND, the first bit errors will appear after
18 * 1000 or more writes and then will usually snowball, reaching the
19 * limits of the ECC quickly.
20 *
21 * The test stops after 10000 cycles, should your FLASH be
22 * exceptionally good and not generate bit errors before that. Try
23 * a different page in that case.
24 *
25 * Please note that neither of these tests will significantly 'use up' any
26 * FLASH endurance. Only a maximum of two erase operations will be performed.
27 *
28 *
29 * This program is free software; you can redistribute it and/or modify it
30 * under the terms of the GNU General Public License version 2 as published by
31 * the Free Software Foundation.
32 *
33 * This program is distributed in the hope that it will be useful, but WITHOUT
34 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
35 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
36 * more details.
37 *
38 * You should have received a copy of the GNU General Public License along with
39 * this program; see the file COPYING. If not, write to the Free Software
40 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
41 */
42#include <linux/init.h>
43#include <linux/module.h>
44#include <linux/moduleparam.h>
45#include <linux/mtd/mtd.h>
46#include <linux/err.h>
47#include <linux/mtd/nand.h>
48#include <linux/slab.h>
49
50#define msg(FMT, VA...) pr_info("mtd_nandbiterrs: "FMT, ##VA)
51
52static int dev;
53module_param(dev, int, S_IRUGO);
54MODULE_PARM_DESC(dev, "MTD device number to use");
55
56static unsigned page_offset;
57module_param(page_offset, uint, S_IRUGO);
58MODULE_PARM_DESC(page_offset, "Page number relative to dev start");
59
60static unsigned seed;
61module_param(seed, uint, S_IRUGO);
62MODULE_PARM_DESC(seed, "Random seed");
63
64static int mode;
65module_param(mode, int, S_IRUGO);
66MODULE_PARM_DESC(mode, "0=incremental errors, 1=overwrite test");
67
68static unsigned max_overwrite = 10000;
69
70static loff_t offset; /* Offset of the page we're using. */
71static unsigned eraseblock; /* Eraseblock number for our page. */
72
73/* We assume that the ECC can correct up to a certain number
74 * of biterrors per subpage. */
75static unsigned subsize; /* Size of subpages */
76static unsigned subcount; /* Number of subpages per page */
77
78static struct mtd_info *mtd; /* MTD device */
79
80static uint8_t *wbuffer; /* One page write / compare buffer */
81static uint8_t *rbuffer; /* One page read buffer */
82
83/* 'random' bytes from known offsets */
84static uint8_t hash(unsigned offset)
85{
86 unsigned v = offset;
87 unsigned char c;
88 v ^= 0x7f7edfd3;
89 v = v ^ (v >> 3);
90 v = v ^ (v >> 5);
91 v = v ^ (v >> 13);
92 c = v & 0xFF;
93 /* Reverse bits of result. */
94 c = (c & 0x0F) << 4 | (c & 0xF0) >> 4;
95 c = (c & 0x33) << 2 | (c & 0xCC) >> 2;
96 c = (c & 0x55) << 1 | (c & 0xAA) >> 1;
97 return c;
98}
99
100static int erase_block(void)
101{
102 int err;
103 struct erase_info ei;
104 loff_t addr = eraseblock * mtd->erasesize;
105
106 msg("erase_block\n");
107
108 memset(&ei, 0, sizeof(struct erase_info));
109 ei.mtd = mtd;
110 ei.addr = addr;
111 ei.len = mtd->erasesize;
112
113 err = mtd_erase(mtd, &ei);
114 if (err || ei.state == MTD_ERASE_FAILED) {
115 msg("error %d while erasing\n", err);
116 if (!err)
117 err = -EIO;
118 return err;
119 }
120
121 return 0;
122}
123
124/* Writes wbuffer to page */
125static int write_page(int log)
126{
127 int err = 0;
128 size_t written;
129
130 if (log)
131 msg("write_page\n");
132
133 err = mtd_write(mtd, offset, mtd->writesize, &written, wbuffer);
134 if (err || written != mtd->writesize) {
135 msg("error: write failed at %#llx\n", (long long)offset);
136 if (!err)
137 err = -EIO;
138 }
139
140 return err;
141}
142
143/* Re-writes the data area while leaving the OOB alone. */
144static int rewrite_page(int log)
145{
146 int err = 0;
147 struct mtd_oob_ops ops;
148
149 if (log)
150 msg("rewrite page\n");
151
152 ops.mode = MTD_OPS_RAW; /* No ECC */
153 ops.len = mtd->writesize;
154 ops.retlen = 0;
155 ops.ooblen = 0;
156 ops.oobretlen = 0;
157 ops.ooboffs = 0;
158 ops.datbuf = wbuffer;
159 ops.oobbuf = NULL;
160
161 err = mtd_write_oob(mtd, offset, &ops);
162 if (err || ops.retlen != mtd->writesize) {
163 msg("error: write_oob failed (%d)\n", err);
164 if (!err)
165 err = -EIO;
166 }
167
168 return err;
169}
170
171/* Reads page into rbuffer. Returns number of corrected bit errors (>=0)
172 * or error (<0) */
173static int read_page(int log)
174{
175 int err = 0;
176 size_t read;
177 struct mtd_ecc_stats oldstats;
178
179 if (log)
180 msg("read_page\n");
181
182 /* Saving last mtd stats */
183 memcpy(&oldstats, &mtd->ecc_stats, sizeof(oldstats));
184
185 err = mtd_read(mtd, offset, mtd->writesize, &read, rbuffer);
186 if (err == -EUCLEAN)
187 err = mtd->ecc_stats.corrected - oldstats.corrected;
188
189 if (err < 0 || read != mtd->writesize) {
190 msg("error: read failed at %#llx\n", (long long)offset);
191 if (err >= 0)
192 err = -EIO;
193 }
194
195 return err;
196}
197
198/* Verifies rbuffer against random sequence */
199static int verify_page(int log)
200{
201 unsigned i, errs = 0;
202
203 if (log)
204 msg("verify_page\n");
205
206 for (i = 0; i < mtd->writesize; i++) {
207 if (rbuffer[i] != hash(i+seed)) {
208 msg("Error: page offset %u, expected %02x, got %02x\n",
209 i, hash(i+seed), rbuffer[i]);
210 errs++;
211 }
212 }
213
214 if (errs)
215 return -EIO;
216 else
217 return 0;
218}
219
220#define CBIT(v, n) ((v) & (1 << (n)))
221#define BCLR(v, n) ((v) = (v) & ~(1 << (n)))
222
223/* Finds the first '1' bit in wbuffer starting at offset 'byte'
224 * and sets it to '0'. */
225static int insert_biterror(unsigned byte)
226{
227 int bit;
228
229 while (byte < mtd->writesize) {
230 for (bit = 7; bit >= 0; bit--) {
231 if (CBIT(wbuffer[byte], bit)) {
232 BCLR(wbuffer[byte], bit);
233 msg("Inserted biterror @ %u/%u\n", byte, bit);
234 return 0;
235 }
236 }
237 byte++;
238 }
239 msg("biterror: Failed to find a '1' bit\n");
240 return -EIO;
241}
242
243/* Writes 'random' data to page and then introduces deliberate bit
244 * errors into the page, while verifying each step. */
245static int incremental_errors_test(void)
246{
247 int err = 0;
248 unsigned i;
249 unsigned errs_per_subpage = 0;
250
251 msg("incremental biterrors test\n");
252
253 for (i = 0; i < mtd->writesize; i++)
254 wbuffer[i] = hash(i+seed);
255
256 err = write_page(1);
257 if (err)
258 goto exit;
259
260 while (1) {
261
262 err = rewrite_page(1);
263 if (err)
264 goto exit;
265
266 err = read_page(1);
267 if (err > 0)
268 msg("Read reported %d corrected bit errors\n", err);
269 if (err < 0) {
270 msg("After %d biterrors per subpage, read reported error %d\n",
271 errs_per_subpage, err);
272 err = 0;
273 goto exit;
274 }
275
276 err = verify_page(1);
277 if (err) {
278 msg("ECC failure, read data is incorrect despite read success\n");
279 goto exit;
280 }
281
282 msg("Successfully corrected %d bit errors per subpage\n",
283 errs_per_subpage);
284
285 for (i = 0; i < subcount; i++) {
286 err = insert_biterror(i * subsize);
287 if (err < 0)
288 goto exit;
289 }
290 errs_per_subpage++;
291 }
292
293exit:
294 return err;
295}
296
297
298/* Writes 'random' data to page and then re-writes that same data repeatedly.
299 This eventually develops bit errors (bits written as '1' will slowly become
300 '0'), which are corrected as far as the ECC is capable of. */
301static int overwrite_test(void)
302{
303 int err = 0;
304 unsigned i;
305 unsigned max_corrected = 0;
306 unsigned opno = 0;
307 /* We don't expect more than this many correctable bit errors per
308 * page. */
309 #define MAXBITS 512
310 static unsigned bitstats[MAXBITS]; /* bit error histogram. */
311
312 memset(bitstats, 0, sizeof(bitstats));
313
314 msg("overwrite biterrors test\n");
315
316 for (i = 0; i < mtd->writesize; i++)
317 wbuffer[i] = hash(i+seed);
318
319 err = write_page(1);
320 if (err)
321 goto exit;
322
323 while (opno < max_overwrite) {
324
325 err = rewrite_page(0);
326 if (err)
327 break;
328
329 err = read_page(0);
330 if (err >= 0) {
331 if (err >= MAXBITS) {
332 msg("Implausible number of bit errors corrected\n");
333 err = -EIO;
334 break;
335 }
336 bitstats[err]++;
337 if (err > max_corrected) {
338 max_corrected = err;
339 msg("Read reported %d corrected bit errors\n",
340 err);
341 }
342 } else { /* err < 0 */
343 msg("Read reported error %d\n", err);
344 err = 0;
345 break;
346 }
347
348 err = verify_page(0);
349 if (err) {
350 bitstats[max_corrected] = opno;
351 msg("ECC failure, read data is incorrect despite read success\n");
352 break;
353 }
354
355 opno++;
356 }
357
358 /* At this point bitstats[0] contains the number of ops with no bit
359 * errors, bitstats[1] the number of ops with 1 bit error, etc. */
360 msg("Bit error histogram (%d operations total):\n", opno);
361 for (i = 0; i < max_corrected; i++)
362 msg("Page reads with %3d corrected bit errors: %d\n",
363 i, bitstats[i]);
364
365exit:
366 return err;
367}
368
369static int __init mtd_nandbiterrs_init(void)
370{
371 int err = 0;
372
373 msg("\n");
374 msg("==================================================\n");
375 msg("MTD device: %d\n", dev);
376
377 mtd = get_mtd_device(NULL, dev);
378 if (IS_ERR(mtd)) {
379 err = PTR_ERR(mtd);
380 msg("error: cannot get MTD device\n");
381 goto exit_mtddev;
382 }
383
384 if (mtd->type != MTD_NANDFLASH) {
385 msg("this test requires NAND flash\n");
386 err = -ENODEV;
387 goto exit_nand;
388 }
389
390 msg("MTD device size %llu, eraseblock=%u, page=%u, oob=%u\n",
391 (unsigned long long)mtd->size, mtd->erasesize,
392 mtd->writesize, mtd->oobsize);
393
394 subsize = mtd->writesize >> mtd->subpage_sft;
395 subcount = mtd->writesize / subsize;
396
397 msg("Device uses %d subpages of %d bytes\n", subcount, subsize);
398
399 offset = page_offset * mtd->writesize;
400 eraseblock = mtd_div_by_eb(offset, mtd);
401
402 msg("Using page=%u, offset=%llu, eraseblock=%u\n",
403 page_offset, offset, eraseblock);
404
405 wbuffer = kmalloc(mtd->writesize, GFP_KERNEL);
406 if (!wbuffer) {
407 err = -ENOMEM;
408 goto exit_wbuffer;
409 }
410
411 rbuffer = kmalloc(mtd->writesize, GFP_KERNEL);
412 if (!rbuffer) {
413 err = -ENOMEM;
414 goto exit_rbuffer;
415 }
416
417 err = erase_block();
418 if (err)
419 goto exit_error;
420
421 if (mode == 0)
422 err = incremental_errors_test();
423 else
424 err = overwrite_test();
425
426 if (err)
427 goto exit_error;
428
429 /* We leave the block un-erased in case of test failure. */
430 err = erase_block();
431 if (err)
432 goto exit_error;
433
434 err = -EIO;
435 msg("finished successfully.\n");
436 msg("==================================================\n");
437
438exit_error:
439 kfree(rbuffer);
440exit_rbuffer:
441 kfree(wbuffer);
442exit_wbuffer:
443 /* Nothing */
444exit_nand:
445 put_mtd_device(mtd);
446exit_mtddev:
447 return err;
448}
449
450static void __exit mtd_nandbiterrs_exit(void)
451{
452 return;
453}
454
455module_init(mtd_nandbiterrs_init);
456module_exit(mtd_nandbiterrs_exit);
457
458MODULE_DESCRIPTION("NAND bit error recovery test");
459MODULE_AUTHOR("Iwo Mergler");
460MODULE_LICENSE("GPL");