mirror of
https://github.com/espressif/esp-idf.git
synced 2025-12-16 04:22:22 +00:00
spi_flash: fix race condition when doing operations in unpinned tasks
spi_flash_enable_interrupts_caches_and_other_cpu function used to enable non-IRAM interrupts after giving up flash operation lock, which would cause problems if another task was waiting on the lock to start a flash operation. In fact, non-IRAM interrupts should be re-enabled before the task scheduler is resumed. Otherwise non-pinned task can be moved to the other CPU due to preemption, causing esp_intr_noniram_enable to be called on the other CPU, causing an abort to be triggered. Fixes the issue reported in https://github.com/espressif/esp-idf/pull/258
This commit is contained in:
@@ -41,6 +41,9 @@ static uint32_t s_flash_op_cache_state[2];
|
||||
static SemaphoreHandle_t s_flash_op_mutex;
|
||||
static volatile bool s_flash_op_can_start = false;
|
||||
static volatile bool s_flash_op_complete = false;
|
||||
#ifndef NDEBUG
|
||||
static volatile int s_flash_op_cpu = -1;
|
||||
#endif
|
||||
|
||||
void spi_flash_init_lock()
|
||||
{
|
||||
@@ -85,6 +88,11 @@ void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu()
|
||||
|
||||
const uint32_t cpuid = xPortGetCoreID();
|
||||
const uint32_t other_cpuid = (cpuid == 0) ? 1 : 0;
|
||||
#ifndef NDEBUG
|
||||
// For sanity check later: record the CPU which has started doing flash operation
|
||||
assert(s_flash_op_cpu == -1);
|
||||
s_flash_op_cpu = cpuid;
|
||||
#endif
|
||||
|
||||
if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
|
||||
// Scheduler hasn't been started yet, it means that spi_flash API is being
|
||||
@@ -98,12 +106,13 @@ void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu()
|
||||
// disable cache there and block other tasks from executing.
|
||||
s_flash_op_can_start = false;
|
||||
s_flash_op_complete = false;
|
||||
esp_ipc_call(other_cpuid, &spi_flash_op_block_func, (void*) other_cpuid);
|
||||
esp_err_t ret = esp_ipc_call(other_cpuid, &spi_flash_op_block_func, (void*) other_cpuid);
|
||||
assert(ret == ESP_OK);
|
||||
while (!s_flash_op_can_start) {
|
||||
// Busy loop and wait for spi_flash_op_block_func to disable cache
|
||||
// on the other CPU
|
||||
}
|
||||
// Disable scheduler on CPU cpuid
|
||||
// Disable scheduler on the current CPU
|
||||
vTaskSuspendAll();
|
||||
// This is guaranteed to run on CPU <cpuid> because the other CPU is now
|
||||
// occupied by highest priority task
|
||||
@@ -119,6 +128,11 @@ void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu()
|
||||
{
|
||||
const uint32_t cpuid = xPortGetCoreID();
|
||||
const uint32_t other_cpuid = (cpuid == 0) ? 1 : 0;
|
||||
#ifndef NDEBUG
|
||||
// Sanity check: flash operation ends on the same CPU as it has started
|
||||
assert(cpuid == s_flash_op_cpu);
|
||||
s_flash_op_cpu = -1;
|
||||
#endif
|
||||
|
||||
// Re-enable cache on this CPU
|
||||
spi_flash_restore_cache(cpuid, s_flash_op_cache_state[cpuid]);
|
||||
@@ -132,13 +146,21 @@ void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu()
|
||||
} else {
|
||||
// Signal to spi_flash_op_block_task that flash operation is complete
|
||||
s_flash_op_complete = true;
|
||||
// Resume tasks on the current CPU
|
||||
}
|
||||
// Re-enable non-iram interrupts
|
||||
esp_intr_noniram_enable();
|
||||
|
||||
// Resume tasks on the current CPU, if the scheduler has started.
|
||||
// NOTE: enabling non-IRAM interrupts has to happen before this,
|
||||
// because once the scheduler has started, due to preemption the
|
||||
// current task can end up being moved to the other CPU.
|
||||
// But esp_intr_noniram_enable has to be called on the same CPU which
|
||||
// called esp_intr_noniram_disable
|
||||
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
|
||||
xTaskResumeAll();
|
||||
}
|
||||
// Release API lock
|
||||
spi_flash_op_unlock();
|
||||
// Re-enable non-iram interrupts
|
||||
esp_intr_noniram_enable();
|
||||
}
|
||||
|
||||
void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu_no_os()
|
||||
|
||||
Reference in New Issue
Block a user