Browse Source

stm32: Change flash IRQ priority from 2 to 6 to prevent preemption.

The flash-IRQ handler is used to flush the storage cache, ie write
outstanding block data from RAM to flash.  This is triggered by a timeout,
or by a direct call to flush all storage caches.

Prior to this commit, a timeout could trigger the cache flushing to occur
during the execution of a read/write to external SPI flash storage.  In
such a case the storage subsystem would break down.

SPI storage transfers are already protected against USB IRQs, so by
changing the priority of the flash IRQ to that of the USB IRQ (what is
done in this commit) the SPI transfers can be protected against any
timeouts triggering a cache flush (the cache flush would be postponed until
after the transfer finished, but note that in the case of SPI writes the
timeout is rescheduled after the transfer finishes).

The handling of internal flash sync'ing needs to be changed to directly
call flash_bdev_irq_handler() sync may be called with the IRQ priority
already raised (eg when called from a USB MSC IRQ handler).
pull/4133/head
Damien George 6 years ago
parent
commit
0941a467e7
  1. 7
      ports/stm32/flashbdev.c
  2. 11
      ports/stm32/irq.h
  3. 9
      ports/stm32/spibdev.c
  4. 3
      ports/stm32/storage.c

7
ports/stm32/flashbdev.c

@ -143,14 +143,17 @@ int32_t flash_bdev_ioctl(uint32_t op, uint32_t arg) {
flash_bdev_irq_handler();
return 0;
case BDEV_IOCTL_SYNC:
case BDEV_IOCTL_SYNC: {
uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access
if (flash_flags & FLASH_FLAG_DIRTY) {
flash_flags |= FLASH_FLAG_FORCE_WRITE;
while (flash_flags & FLASH_FLAG_DIRTY) {
NVIC->STIR = FLASH_IRQn;
flash_bdev_irq_handler();
}
}
restore_irq_pri(basepri);
return 0;
}
}
return -MP_EINVAL;
}

11
ports/stm32/irq.h

@ -106,9 +106,9 @@ MP_DECLARE_CONST_FUN_OBJ_0(pyb_irq_stats_obj);
//#def IRQ_PRI_SYSTICK 0
#define IRQ_PRI_UART 1
#define IRQ_PRI_FLASH 1
#define IRQ_PRI_SDIO 1
#define IRQ_PRI_DMA 1
#define IRQ_PRI_FLASH 2
#define IRQ_PRI_OTG_FS 2
#define IRQ_PRI_OTG_HS 2
#define IRQ_PRI_TIM5 2
@ -126,10 +126,6 @@ MP_DECLARE_CONST_FUN_OBJ_0(pyb_irq_stats_obj);
// get dropped. The handling for each character only consumes about 0.5 usec
#define IRQ_PRI_UART NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 1, 0)
// Flash IRQ must be higher priority than interrupts of all those components
// that rely on the flash storage.
#define IRQ_PRI_FLASH NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 2, 0)
// SDIO must be higher priority than DMA for SDIO DMA transfers to work.
#define IRQ_PRI_SDIO NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 4, 0)
@ -137,6 +133,11 @@ MP_DECLARE_CONST_FUN_OBJ_0(pyb_irq_stats_obj);
// into the sdcard driver which waits for the DMA to complete.
#define IRQ_PRI_DMA NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 5, 0)
// Flash IRQ (used for flushing storage cache) must be at the same priority as
// the USB IRQs, so that the IRQ priority can be raised to this level to disable
// both the USB and cache flushing, when storage transfers are in progress.
#define IRQ_PRI_FLASH NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 6, 0)
#define IRQ_PRI_OTG_FS NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 6, 0)
#define IRQ_PRI_OTG_HS NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 6, 0)
#define IRQ_PRI_TIM5 NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 6, 0)

9
ports/stm32/spibdev.c

@ -49,8 +49,7 @@ int32_t spi_bdev_ioctl(spi_bdev_t *bdev, uint32_t op, uint32_t arg) {
case BDEV_IOCTL_SYNC:
if (bdev->spiflash.flags & 1) {
// we must disable USB irqs to prevent MSC contention with SPI flash
uint32_t basepri = raise_irq_pri(IRQ_PRI_OTG_FS);
uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access
mp_spiflash_cache_flush(&bdev->spiflash);
led_state(PYB_LED_RED, 0); // indicate a clean cache with LED off
restore_irq_pri(basepri);
@ -61,8 +60,7 @@ int32_t spi_bdev_ioctl(spi_bdev_t *bdev, uint32_t op, uint32_t arg) {
}
int spi_bdev_readblocks(spi_bdev_t *bdev, uint8_t *dest, uint32_t block_num, uint32_t num_blocks) {
// we must disable USB irqs to prevent MSC contention with SPI flash
uint32_t basepri = raise_irq_pri(IRQ_PRI_OTG_FS);
uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access
mp_spiflash_cached_read(&bdev->spiflash, block_num * FLASH_BLOCK_SIZE, num_blocks * FLASH_BLOCK_SIZE, dest);
restore_irq_pri(basepri);
@ -70,8 +68,7 @@ int spi_bdev_readblocks(spi_bdev_t *bdev, uint8_t *dest, uint32_t block_num, uin
}
int spi_bdev_writeblocks(spi_bdev_t *bdev, const uint8_t *src, uint32_t block_num, uint32_t num_blocks) {
// we must disable USB irqs to prevent MSC contention with SPI flash
uint32_t basepri = raise_irq_pri(IRQ_PRI_OTG_FS);
uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access
int ret = mp_spiflash_cached_write(&bdev->spiflash, block_num * FLASH_BLOCK_SIZE, num_blocks * FLASH_BLOCK_SIZE, src);
if (bdev->spiflash.flags & 1) {
led_state(PYB_LED_RED, 1); // indicate a dirty cache with LED on

3
ports/stm32/storage.c

@ -55,8 +55,7 @@ void storage_init(void) {
#endif
// Enable the flash IRQ, which is used to also call our storage IRQ handler
// It needs to go at a higher priority than all those components that rely on
// the flash storage (eg higher than USB MSC).
// It must go at the same priority as USB (see comment in irq.h).
NVIC_SetPriority(FLASH_IRQn, IRQ_PRI_FLASH);
HAL_NVIC_EnableIRQ(FLASH_IRQn);
}

Loading…
Cancel
Save