Skip to content

Commit

Permalink
fix(spi_flash): Fix stuck during flash operation
Browse files Browse the repository at this point in the history
When a task was not pinned to a certain CPU.
  • Loading branch information
KonstantinKondrashov committed Apr 17, 2024
1 parent a41e037 commit 955341d
Showing 1 changed file with 36 additions and 24 deletions.
60 changes: 36 additions & 24 deletions components/spi_flash/cache_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
#include <soc/soc.h>
#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"
Expand Down Expand Up @@ -144,8 +144,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);
Expand All @@ -160,33 +160,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);

// 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));
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

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 <cpuid> 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
Expand Down

0 comments on commit 955341d

Please sign in to comment.