[PATCH 2/4] mtd: rawnand: sm_common: avoid recursive locking when marking bad blocks
James Hilliard
james.hilliard1 at gmail.com
Sun Nov 8 17:29:21 PST 2026
nand_block_markbad_lowlevel() holds the NAND chip and controller locks
when it invokes the legacy block_markbad callback. sm_block_markbad()
calls mtd_write_oob(), which re-enters nand_write_oob() and waits for the
same locks, deadlocking the bad-block marking operation.
Write the marker through the chip's OOB callback under the existing locks.
Keep the reset, target selection, write-protection check and page-cache
invalidation performed by the OOB write path, including the broken-xD
write-protection exception. Preserve the 0x0f marker and fill the rest of
the OOB with 0xff.
Use the OOB callback's return value directly. This also avoids comparing
an 8-byte OOB write on 256-byte-page cards against the 16-byte sector OOB
size and incorrectly reporting a successful write as an error.
Fixes: 5a0edb251ae9 ("mtd: nand: refactor chip->block_markbad interface")
Cc: stable at vger.kernel.org
Signed-off-by: James Hilliard <james.hilliard1 at gmail.com>
---
drivers/mtd/nand/raw/sm_common.c | 44 ++++++++++++++++++++++++----------------
1 file changed, 26 insertions(+), 18 deletions(-)
diff --git a/drivers/mtd/nand/raw/sm_common.c b/drivers/mtd/nand/raw/sm_common.c
index a3a3a05cc5a4..a6387111d271 100644
--- a/drivers/mtd/nand/raw/sm_common.c
+++ b/drivers/mtd/nand/raw/sm_common.c
@@ -100,30 +100,38 @@ static const struct mtd_ooblayout_ops oob_sm_small_ops = {
static int sm_block_markbad(struct nand_chip *chip, loff_t ofs)
{
struct mtd_info *mtd = nand_to_mtd(chip);
- struct mtd_oob_ops ops = { };
- struct sm_oob oob;
+ int chipnr = ofs >> chip->chip_shift;
+ int page = ofs >> chip->page_shift;
+ u8 status;
int ret;
- memset(&oob, -1, SM_OOB_SIZE);
- oob.block_status = 0x0F;
+ ret = nand_reset(chip, chipnr);
+ if (ret)
+ return ret;
- /* As long as this function is called on erase block boundaries
- it will work correctly for 256 byte nand */
- ops.mode = MTD_OPS_PLACE_OOB;
- ops.ooboffs = 0;
- ops.ooblen = mtd->oobsize;
- ops.oobbuf = (void *)&oob;
- ops.datbuf = NULL;
+ nand_select_target(chip, chipnr);
+ if (!(chip->options & NAND_BROKEN_XD) &&
+ !chip->controller->controller_wp) {
+ ret = nand_status_op(chip, &status);
+ if (ret)
+ goto out;
+ if (!(status & NAND_STATUS_WP)) {
+ ret = -EROFS;
+ goto out;
+ }
+ }
+ if (page == chip->pagecache.page)
+ chip->pagecache.page = -1;
- ret = mtd_write_oob(mtd, ofs, &ops);
- if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) {
- pr_notice("sm_common: can't mark sector at %i as bad\n",
- (int)ofs);
- return -EIO;
- }
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
+ chip->oob_poi[chip->badblockpos] = 0x0f;
- return 0;
+ /* The NAND core already holds the chip and controller locks. */
+ ret = chip->ecc.write_oob(chip, page & chip->pagemask);
+out:
+ nand_deselect_target(chip);
+ return ret;
}
static struct nand_flash_dev nand_smartmedia_flash_ids[] = {
--
2.53.0
More information about the linux-mtd
mailing list