[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