| From: Joakim Tjernlund <joakim.tjernlund@transmode.se> |
| Date: Thu, 1 Mar 2018 14:39:39 +0100 |
| Subject: mtd: cfi: cmdset_0001: Do not allow read/write to suspend erase |
| block. |
| |
| commit 6510bbc88e3258631831ade49033537081950605 upstream. |
| |
| 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> |
| Reviewed-by: Richard Weinberger <richard@nod.at> |
| Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| drivers/mtd/chips/cfi_cmdset_0001.c | 16 +++++++++++----- |
| include/linux/mtd/flashchip.h | 1 + |
| 2 files changed, 12 insertions(+), 5 deletions(-) |
| |
| --- a/drivers/mtd/chips/cfi_cmdset_0001.c |
| +++ b/drivers/mtd/chips/cfi_cmdset_0001.c |
| @@ -825,21 +825,25 @@ static int chip_ready (struct map_info * |
| (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1)))) |
| 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 */ |
| - 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' |
| * appears to make some (28F320) flash devices switch to |
| * 'read' mode. Make sure that we switch to 'read status' |
| * 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->state = FL_ERASE_SUSPENDING; |
| chip->erase_suspended = 1; |
| 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)) |
| break; |
| |
| @@ -1035,8 +1039,8 @@ static void put_chip(struct map_info *ma |
| sending the 0x70 (Read Status) command to an erasing |
| chip and expecting it to be ignored, that's what we |
| 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->state = FL_ERASING; |
| break; |
| @@ -1927,6 +1931,8 @@ static int __xipram do_erase_oneblock(st |
| map_write(map, CMD(0xD0), adr); |
| chip->state = FL_ERASING; |
| 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, |
| adr, len, |
| --- a/include/linux/mtd/flashchip.h |
| +++ b/include/linux/mtd/flashchip.h |
| @@ -85,6 +85,7 @@ struct flchip { |
| unsigned int write_suspended:1; |
| unsigned int erase_suspended:1; |
| unsigned long in_progress_block_addr; |
| + unsigned long in_progress_block_mask; |
| |
| struct mutex mutex; |
| wait_queue_head_t wq; /* Wait on here when we're waiting for the chip |