From 4f03e0241e27015d2256526027cccc1c406cb69e Mon Sep 17 00:00:00 2001 From: KonstantinKondrashov Date: Thu, 28 Dec 2023 16:50:55 +0800 Subject: [PATCH] fix(spi_flash): Fix stuck during flash operation When a task was not pinned to a certain CPU. --- components/spi_flash/cache_utils.c | 58 ++++++++++++++++++------------ 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/components/spi_flash/cache_utils.c b/components/spi_flash/cache_utils.c index 862e57e5b5..b61a06c63a 100644 --- a/components/spi_flash/cache_utils.c +++ b/components/spi_flash/cache_utils.c @@ -48,7 +48,7 @@ #include #include "sdkconfig.h" #ifndef CONFIG_FREERTOS_UNICORE -#include "esp_ipc.h" +#include "esp_private/esp_ipc.h" #endif #include "esp_attr.h" #include "esp_memory_utils.h" @@ -157,8 +157,8 @@ void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu(void) spi_flash_op_lock(); - const int cpuid = xPortGetCoreID(); - const uint32_t other_cpuid = (cpuid == 0) ? 1 : 0; + int cpuid = xPortGetCoreID(); + 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); @@ -173,33 +173,45 @@ void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu(void) // esp_intr_noniram_disable. assert(other_cpuid == 1); } else { - // Temporarily raise current task priority to prevent a deadlock while - // waiting for IPC task to start on the other CPU - prvTaskSavedPriority_t SavedPriority; - prvTaskPriorityRaise(&SavedPriority, configMAX_PRIORITIES - 1); + bool ipc_call_was_send_to_other_cpu; + do { + #ifdef CONFIG_FREERTOS_SMP + //Note: Scheduler suspension behavior changed in FreeRTOS SMP + vTaskPreemptionDisable(NULL); + #else + // Disable scheduler on the current CPU + vTaskSuspendAll(); + #endif // CONFIG_FREERTOS_SMP - // Signal to the spi_flash_op_block_task on the other CPU that we need it to - // disable cache there and block other tasks from executing. - s_flash_op_can_start = false; - ESP_ERROR_CHECK(esp_ipc_call(other_cpuid, &spi_flash_op_block_func, (void *) other_cpuid)); + cpuid = xPortGetCoreID(); + other_cpuid = (cpuid == 0) ? 1 : 0; + #ifndef NDEBUG + s_flash_op_cpu = cpuid; + #endif + + s_flash_op_can_start = false; + + ipc_call_was_send_to_other_cpu = esp_ipc_call_nonblocking(other_cpuid, &spi_flash_op_block_func, (void *) other_cpuid) == ESP_OK; + + if (!ipc_call_was_send_to_other_cpu) { + // IPC call was not send to other cpu because another nonblocking API is running now. + // Enable the Scheduler again will not help the IPC to speed it up + // but there is a benefit to schedule to a higher priority task before the nonblocking running IPC call is done. + #ifdef CONFIG_FREERTOS_SMP + //Note: Scheduler suspension behavior changed in FreeRTOS SMP + vTaskPreemptionEnable(NULL); + #else + xTaskResumeAll(); + #endif // CONFIG_FREERTOS_SMP + } + } while (!ipc_call_was_send_to_other_cpu); while (!s_flash_op_can_start) { // Busy loop and wait for spi_flash_op_block_func to disable cache // on the other CPU } -#ifdef CONFIG_FREERTOS_SMP - //Note: Scheduler suspension behavior changed in FreeRTOS SMP - vTaskPreemptionDisable(NULL); -#else - // Disable scheduler on the current CPU - vTaskSuspendAll(); -#endif // CONFIG_FREERTOS_SMP - // Can now set the priority back to the normal one - prvTaskPriorityRestore(&SavedPriority); - // This is guaranteed to run on CPU because the other CPU is now - // occupied by highest priority task - assert(xPortGetCoreID() == cpuid); } + // Kill interrupts that aren't located in IRAM esp_intr_noniram_disable(); // This CPU executes this routine, with non-IRAM interrupts and the scheduler