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

mtd: nand: write BBM to OOB even with flash-based BBT

Currently, the flash-based BBT implementation writes bad block data only
to its flash-based table and not to the OOB marker area. Then, as new bad
blocks are marked over time, the OOB markers become incomplete and the
flash-based table becomes the only source of current bad block
information. This becomes an obvious problem when, for example:

* bootloader cannot read the flash-based BBT format
* BBT is corrupted and the flash must be rescanned for bad
blocks; we want to remember bad blocks that were marked from Linux

So to keep the bad block markers in sync with the flash-based BBT, this
patch changes the default so that we write bad block markers to the proper
OOB area on each block in addition to flash-based BBT. Comments are
updated, expanded, and/or relocated as necessary.

The new flash-based BBT procedure for marking bad blocks:
(1) erase the affected block, to allow OOB marker to be written cleanly
(2) update in-memory BBT
(3) write bad block marker to OOB area of affected block
(4) update flash-based BBT
Note that we retain the first error encountered in (3) or (4), finish the
procedures, and dump the error in the end.

This should handle power cuts gracefully enough. (1) and (2) are mostly
harmless (note that (1) will not erase an already-recognized bad block).
The OOB and BBT may be "out of sync" if we experience power loss bewteen
(3) and (4), but we can reasonably expect that on next boot, subsequent
I/O operations will discover that the block should be marked bad again,
thus re-syncing the OOB and BBT.

Note that this is a change from the previous default flash-based BBT
behavior. If your system cannot support writing bad block markers to OOB,
use the new NAND_BBT_NO_OOB_BBM option (in combination with
NAND_BBT_USE_FLASH and NAND_BBT_NO_OOB).

Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>

authored by

Brian Norris and committed by
David Woodhouse
e2414f4c 050c0c1b

+36 -15
+31 -15
drivers/mtd/nand/nand_base.c
··· 392 392 * @ofs: offset from device start 393 393 * 394 394 * This is the default implementation, which can be overridden by a hardware 395 - * specific driver. 395 + * specific driver. We try operations in the following order, according to our 396 + * bbt_options (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH): 397 + * (1) erase the affected block, to allow OOB marker to be written cleanly 398 + * (2) update in-memory BBT 399 + * (3) write bad block marker to OOB area of affected block 400 + * (4) update flash-based BBT 401 + * Note that we retain the first error encountered in (3) or (4), finish the 402 + * procedures, and dump the error in the end. 396 403 */ 397 404 static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) 398 405 { 399 406 struct nand_chip *chip = mtd->priv; 400 407 uint8_t buf[2] = { 0, 0 }; 401 - int block, ret, i = 0; 408 + int block, res, ret = 0, i = 0; 409 + int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM); 402 410 403 - if (!(chip->bbt_options & NAND_BBT_USE_FLASH)) { 411 + if (write_oob) { 404 412 struct erase_info einfo; 405 413 406 414 /* Attempt erase before marking OOB */ ··· 421 413 422 414 /* Get block number */ 423 415 block = (int)(ofs >> chip->bbt_erase_shift); 416 + /* Mark block bad in memory-based BBT */ 424 417 if (chip->bbt) 425 418 chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); 426 419 427 - /* Do we have a flash based bad block table? */ 428 - if (chip->bbt_options & NAND_BBT_USE_FLASH) 429 - ret = nand_update_bbt(mtd, ofs); 430 - else { 420 + /* Write bad block marker to OOB */ 421 + if (write_oob) { 431 422 struct mtd_oob_ops ops; 432 423 loff_t wr_ofs = ofs; 433 424 434 425 nand_get_device(chip, mtd, FL_WRITING); 435 426 436 - /* 437 - * Write to first/last page(s) if necessary. If we write to more 438 - * than one location, the first error encountered quits the 439 - * procedure. 440 - */ 441 427 ops.datbuf = NULL; 442 428 ops.oobbuf = buf; 443 429 ops.ooboffs = chip->badblockpos; ··· 443 441 } 444 442 ops.mode = MTD_OPS_PLACE_OOB; 445 443 444 + /* Write to first/last page(s) if necessary */ 446 445 if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) 447 446 wr_ofs += mtd->erasesize - mtd->writesize; 448 447 do { 449 - ret = nand_do_write_oob(mtd, wr_ofs, &ops); 448 + res = nand_do_write_oob(mtd, wr_ofs, &ops); 449 + if (!ret) 450 + ret = res; 450 451 451 452 i++; 452 453 wr_ofs += mtd->writesize; 453 - } while (!ret && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && 454 - i < 2); 454 + } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); 455 455 456 456 nand_release_device(mtd); 457 457 } 458 + 459 + /* Update flash-based bad block table */ 460 + if (chip->bbt_options & NAND_BBT_USE_FLASH) { 461 + res = nand_update_bbt(mtd, ofs); 462 + if (!ret) 463 + ret = res; 464 + } 465 + 458 466 if (!ret) 459 467 mtd->ecc_stats.badblocks++; 460 468 ··· 3271 3259 { 3272 3260 int i; 3273 3261 struct nand_chip *chip = mtd->priv; 3262 + 3263 + /* New bad blocks should be marked in OOB, flash-based BBT, or both */ 3264 + BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && 3265 + !(chip->bbt_options & NAND_BBT_USE_FLASH)); 3274 3266 3275 3267 if (!(chip->options & NAND_OWN_BUFFERS)) 3276 3268 chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
+5
include/linux/mtd/bbm.h
··· 112 112 #define NAND_BBT_USE_FLASH 0x00020000 113 113 /* Do not store flash based bad block table in OOB area; store it in-band */ 114 114 #define NAND_BBT_NO_OOB 0x00040000 115 + /* 116 + * Do not write new bad block markers to OOB; useful, e.g., when ECC covers 117 + * entire spare area. Must be used with NAND_BBT_USE_FLASH. 118 + */ 119 + #define NAND_BBT_NO_OOB_BBM 0x00080000 115 120 116 121 /* 117 122 * Flag set by nand_create_default_bbt_descr(), marking that the nand_bbt_descr