浏览代码

mtd: cfi: cmdset_0001: Do not allow read/write to suspend erase block.

Currently it is possible to read and/or write to suspend EB's.
Writing /dev/mtdX or /dev/mtdblockX from several processes may
break the flash state machine.

Signed-off-by: Joakim Tjernlund <joakim.tjernlund@infinera.com>
Cc: <stable@vger.kernel.org>
Reviewed-by: Richard Weinberger <richard@nod.at>
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
Joakim Tjernlund 7 年之前
父节点
当前提交
6510bbc88e
共有 2 个文件被更改,包括 12 次插入5 次删除
  1. 11 5
      drivers/mtd/chips/cfi_cmdset_0001.c
  2. 1 0
      include/linux/mtd/flashchip.h

+ 11 - 5
drivers/mtd/chips/cfi_cmdset_0001.c

@@ -831,21 +831,25 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long
 		     (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1))))
 		     (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1))))
 			goto sleep;
 			goto sleep;
 
 
+		/* Do not allow suspend iff read/write to EB address */
+		if ((adr & chip->in_progress_block_mask) ==
+		    chip->in_progress_block_addr)
+			goto sleep;
 
 
 		/* Erase suspend */
 		/* Erase suspend */
-		map_write(map, CMD(0xB0), adr);
+		map_write(map, CMD(0xB0), chip->in_progress_block_addr);
 
 
 		/* If the flash has finished erasing, then 'erase suspend'
 		/* If the flash has finished erasing, then 'erase suspend'
 		 * appears to make some (28F320) flash devices switch to
 		 * appears to make some (28F320) flash devices switch to
 		 * 'read' mode.  Make sure that we switch to 'read status'
 		 * 'read' mode.  Make sure that we switch to 'read status'
 		 * mode so we get the right data. --rmk
 		 * mode so we get the right data. --rmk
 		 */
 		 */
-		map_write(map, CMD(0x70), adr);
+		map_write(map, CMD(0x70), chip->in_progress_block_addr);
 		chip->oldstate = FL_ERASING;
 		chip->oldstate = FL_ERASING;
 		chip->state = FL_ERASE_SUSPENDING;
 		chip->state = FL_ERASE_SUSPENDING;
 		chip->erase_suspended = 1;
 		chip->erase_suspended = 1;
 		for (;;) {
 		for (;;) {
-			status = map_read(map, adr);
+			status = map_read(map, chip->in_progress_block_addr);
 			if (map_word_andequal(map, status, status_OK, status_OK))
 			if (map_word_andequal(map, status, status_OK, status_OK))
 			        break;
 			        break;
 
 
@@ -1041,8 +1045,8 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
 		   sending the 0x70 (Read Status) command to an erasing
 		   sending the 0x70 (Read Status) command to an erasing
 		   chip and expecting it to be ignored, that's what we
 		   chip and expecting it to be ignored, that's what we
 		   do. */
 		   do. */
-		map_write(map, CMD(0xd0), adr);
-		map_write(map, CMD(0x70), adr);
+		map_write(map, CMD(0xd0), chip->in_progress_block_addr);
+		map_write(map, CMD(0x70), chip->in_progress_block_addr);
 		chip->oldstate = FL_READY;
 		chip->oldstate = FL_READY;
 		chip->state = FL_ERASING;
 		chip->state = FL_ERASING;
 		break;
 		break;
@@ -1933,6 +1937,8 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
 	map_write(map, CMD(0xD0), adr);
 	map_write(map, CMD(0xD0), adr);
 	chip->state = FL_ERASING;
 	chip->state = FL_ERASING;
 	chip->erase_suspended = 0;
 	chip->erase_suspended = 0;
+	chip->in_progress_block_addr = adr;
+	chip->in_progress_block_mask = ~(len - 1);
 
 
 	ret = INVAL_CACHE_AND_WAIT(map, chip, adr,
 	ret = INVAL_CACHE_AND_WAIT(map, chip, adr,
 				   adr, len,
 				   adr, len,

+ 1 - 0
include/linux/mtd/flashchip.h

@@ -85,6 +85,7 @@ struct flchip {
 	unsigned int write_suspended:1;
 	unsigned int write_suspended:1;
 	unsigned int erase_suspended:1;
 	unsigned int erase_suspended:1;
 	unsigned long in_progress_block_addr;
 	unsigned long in_progress_block_addr;
+	unsigned long in_progress_block_mask;
 
 
 	struct mutex mutex;
 	struct mutex mutex;
 	wait_queue_head_t wq; /* Wait on here when we're waiting for the chip
 	wait_queue_head_t wq; /* Wait on here when we're waiting for the chip