mirror of
https://github.com/espressif/esp-idf.git
synced 2026-08-18 06:35:35 +03:00
fix(freertos): Avoid core switch deadlock on start
With CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY=y the main task could switch core between registering the idle hook and the while loop. This would cause a deadlock were the current task was waiting for the idle hook to run on the same core it's busy waiting. Merge https://github.com/espressif/esp-idf/pull/16149
This commit is contained in:
committed by
Konstantin Kondrashov
parent
a6425c02a0
commit
94b526c9fe
@@ -63,6 +63,9 @@ _Static_assert(offsetof( StaticTask_t, pxDummy8 ) == PORT_OFFSET_PX_END_OF_STACK
|
||||
|
||||
BaseType_t uxSchedulerRunning = 0; // Duplicate of xSchedulerRunning, accessible to port files
|
||||
volatile UBaseType_t uxInterruptNesting = 0;
|
||||
#if configNUM_CORES > 1
|
||||
volatile unsigned port_uxCoreStartupDone[portNUM_PROCESSORS] = {0}; // Indicates whether the core has completed its startup sequence
|
||||
#endif
|
||||
portMUX_TYPE port_xTaskLock = portMUX_INITIALIZER_UNLOCKED;
|
||||
portMUX_TYPE port_xISRLock = portMUX_INITIALIZER_UNLOCKED;
|
||||
volatile BaseType_t xPortSwitchFlag = 0;
|
||||
@@ -291,7 +294,9 @@ BaseType_t xPortStartScheduler(void)
|
||||
uxInterruptNesting = 0;
|
||||
port_uxCriticalNestingIDF = 0;
|
||||
uxSchedulerRunning = 0;
|
||||
|
||||
#if configNUM_CORES > 1
|
||||
port_uxCoreStartupDone[xPortGetCoreID()] = 0;
|
||||
#endif
|
||||
/* Setup the hardware to generate the tick. */
|
||||
vPortSetupTimer();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -19,6 +19,9 @@
|
||||
.global pxCurrentTCBs
|
||||
.global vTaskSwitchContext
|
||||
.global xPortSwitchFlag
|
||||
#if ( configNUM_CORES > 1 )
|
||||
.global port_uxCoreStartupDone
|
||||
#endif
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
.global xIsrStack
|
||||
.global port_offset_pxStack
|
||||
@@ -137,6 +140,16 @@ no_switch:
|
||||
lw t0, pxCurrentTCBs
|
||||
lw sp, 0x0(t0)
|
||||
|
||||
#if ( configNUM_CORES > 1 )
|
||||
/* Indicate that this CPU has completed startup */
|
||||
la t1, port_uxCoreStartupDone /* t1 = &port_uxCoreStartupDone */
|
||||
csrr a1, mhartid /* a1 = coreID */
|
||||
slli a1, a1, 2 /* a1 = coreID * 4 */
|
||||
add t1, t1, a1 /* t1 = &port_uxCoreStartupDone[coreID] // a1 contains coreID * 4 */
|
||||
li a1, 1 /* a1 = 1 */
|
||||
sw a1, 0(t1) /* port_uxCoreStartupDone[coreID] = 1 */
|
||||
#endif /* ( configNUM_CORES > 1 ) */
|
||||
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
/* esp_hw_stack_guard_set_bounds(pxCurrentTCBs[0]->pxStack,
|
||||
* pxCurrentTCBs[0]->pxEndOfStack);
|
||||
|
||||
@@ -76,6 +76,7 @@ const DRAM_ATTR uint32_t offset_uxCoreAffinityMask = offsetof(StaticTask_t, uxDu
|
||||
|
||||
volatile unsigned port_xSchedulerRunning[portNUM_PROCESSORS] = {0}; // Indicates whether scheduler is running on a per-core basis
|
||||
unsigned int port_interruptNesting[portNUM_PROCESSORS] = {0}; // Interrupt nesting level. Increased/decreased in portasm.c, _frxt_int_enter/_frxt_int_exit
|
||||
volatile unsigned port_uxCoreStartupDone[portNUM_PROCESSORS] = {0}; // Indicates whether the core has completed its startup sequence
|
||||
#if ( configNUMBER_OF_CORES > 1 )
|
||||
//FreeRTOS SMP Locks
|
||||
portMUX_TYPE port_xTaskLock = portMUX_INITIALIZER_UNLOCKED;
|
||||
@@ -333,7 +334,10 @@ BaseType_t xPortStartScheduler( void )
|
||||
/* Setup the hardware to generate the tick. */
|
||||
vPortSetupTimer();
|
||||
|
||||
port_xSchedulerRunning[xPortGetCoreID()] = 1;
|
||||
/* Initialize all kernel state tracking variables */
|
||||
BaseType_t coreID = xPortGetCoreID();
|
||||
port_xSchedulerRunning[coreID] = 1;
|
||||
port_uxCoreStartupDone[coreID] = 0;
|
||||
|
||||
#if configNUM_CORES > 1
|
||||
// Workaround for non-thread safe multi-core OS startup (see IDF-4524)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileContributor: 2016-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2015-2019 Cadence Design Systems, Inc.
|
||||
@@ -284,7 +284,7 @@ _frxt_int_exit:
|
||||
* Manages the tick timer and calls xPortSysTickHandler() every tick.
|
||||
* See the detailed description of the XT_RTOS_ENTER macro in xtensa_rtos.h.
|
||||
*
|
||||
* Callable from C (obeys ABI conventions). Implemented in assmebly code for performance.
|
||||
* Callable from C (obeys ABI conventions). Implemented in assembly code for performance.
|
||||
*
|
||||
**********************************************************************************************************
|
||||
*/
|
||||
@@ -373,7 +373,7 @@ _frxt_timer_int:
|
||||
* _frxt_tick_timer_init
|
||||
* void _frxt_tick_timer_init(void)
|
||||
*
|
||||
* Initialize timer and timer interrrupt handler (_xt_tick_divisor_init() has already been been called).
|
||||
* Initialize timer and timer interrupt handler (_xt_tick_divisor_init() has already been been called).
|
||||
* Callable from C (obeys ABI conventions on entry).
|
||||
*
|
||||
**********************************************************************************************************
|
||||
@@ -463,6 +463,15 @@ _frxt_dispatch:
|
||||
l32i sp, a3, TOPOFSTACK_OFFS /* SP = next_TCB->pxTopOfStack; */
|
||||
s32i a3, a2, 0
|
||||
|
||||
#if ( configNUM_CORES > 1 )
|
||||
/* Indicate that this CPU has completed startup. */
|
||||
movi a2, port_uxCoreStartupDone /* a2 = &port_uxCoreStartupDone */
|
||||
getcoreid a3
|
||||
addx4 a2, a3, a2 /* a2 = &port_uxCoreStartupDone[coreid] */
|
||||
movi a3, 1 /* a3 = 1 */
|
||||
s32i a3, a2, 0 /* port_uxCoreStartupDone[coreid] = 1 */
|
||||
#endif /* ( configNUM_CORES > 1 ) */
|
||||
|
||||
/* Determine the type of stack frame. */
|
||||
l32i a2, sp, XT_STK_EXIT /* exit dispatcher or solicited flag */
|
||||
bnez a2, .L_frxt_dispatch_stk
|
||||
|
||||
@@ -99,6 +99,7 @@ volatile UBaseType_t port_uxInterruptNesting[portNUM_PROCESSORS] = {0}; // Inte
|
||||
volatile UBaseType_t port_uxCriticalNesting[portNUM_PROCESSORS] = {0};
|
||||
volatile UBaseType_t port_uxOldInterruptState[portNUM_PROCESSORS] = {0};
|
||||
volatile UBaseType_t xPortSwitchFlag[portNUM_PROCESSORS] = {0};
|
||||
volatile UBaseType_t port_uxCoreStartupDone[portNUM_PROCESSORS] = {0}; // Indicates whether the core has completed its startup sequence
|
||||
|
||||
#if ( SOC_CPU_COPROC_NUM > 0 )
|
||||
|
||||
@@ -156,6 +157,7 @@ BaseType_t xPortStartScheduler(void)
|
||||
port_uxInterruptNesting[coreID] = 0;
|
||||
port_uxCriticalNesting[coreID] = 0;
|
||||
port_xSchedulerRunning[coreID] = 0;
|
||||
port_uxCoreStartupDone[coreID] = 0;
|
||||
|
||||
/* Initialize ISR Stack(s) */
|
||||
for (int i = 0; i < portNUM_PROCESSORS; i++) {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
.global pxCurrentTCBs
|
||||
.global vTaskSwitchContext
|
||||
.global xPortSwitchFlag
|
||||
.global port_uxCoreStartupDone
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
.global xIsrStackBottom
|
||||
.global esp_hw_stack_guard_monitor_stop
|
||||
@@ -857,6 +858,13 @@ restore_stack_pointer:
|
||||
lw sp, 0(a0)
|
||||
#endif /* ( configNUM_CORES > 1 ) */
|
||||
|
||||
#if ( configNUM_CORES > 1 )
|
||||
/* Indicate that this CPU has completed startup */
|
||||
la t1, port_uxCoreStartupDone /* t1 = &port_uxCoreStartupDone */
|
||||
add t1, t1, s0 /* t1 = &port_uxCoreStartupDone[coreID] // s0 contains coreID * 4 */
|
||||
li a1, 1 /* a1 = 1 */
|
||||
sw a1, 0(t1) /* port_uxCoreStartupDone[coreID] = 1 */
|
||||
#endif /* ( configNUM_CORES > 1 ) */
|
||||
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
/* esp_hw_stack_guard_set_bounds(pxCurrentTCBs[0]->pxStack,
|
||||
|
||||
@@ -76,6 +76,7 @@ volatile unsigned port_xSchedulerRunning[portNUM_PROCESSORS] = {0}; // Indicates
|
||||
unsigned port_interruptNesting[portNUM_PROCESSORS] = {0}; // Interrupt nesting level. Increased/decreased in portasm.c, _frxt_int_enter/_frxt_int_exit
|
||||
BaseType_t port_uxCriticalNesting[portNUM_PROCESSORS] = {0};
|
||||
BaseType_t port_uxOldInterruptState[portNUM_PROCESSORS] = {0};
|
||||
volatile unsigned port_uxCoreStartupDone[portNUM_PROCESSORS] = {0}; // Indicates whether the core has completed its startup sequence
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
@@ -110,7 +111,10 @@ BaseType_t xPortStartScheduler( void )
|
||||
/* Setup the hardware to generate the tick. */
|
||||
vPortSetupTimer();
|
||||
|
||||
port_xSchedulerRunning[xPortGetCoreID()] = 1;
|
||||
/* Initialize all kernel state tracking variables */
|
||||
BaseType_t coreID = xPortGetCoreID();
|
||||
port_xSchedulerRunning[coreID] = 1;
|
||||
port_uxCoreStartupDone[coreID] = 0;
|
||||
|
||||
// Windows contain references to the startup stack which will be reclaimed by the main task
|
||||
// Spill the windows to create a clean environment to ensure we do not carry over any such references
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* SPDX-FileContributor: 2016-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileContributor: 2016-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2015-2019 Cadence Design Systems, Inc.
|
||||
@@ -457,6 +457,15 @@ _frxt_dispatch:
|
||||
l32i sp, a3, TOPOFSTACK_OFFS /* SP = next_TCB->pxTopOfStack; */
|
||||
s32i a3, a2, 0
|
||||
|
||||
#if ( configNUM_CORES > 1 )
|
||||
/* Indicate that this CPU has completed startup. */
|
||||
movi a2, port_uxCoreStartupDone /* a2 = &port_uxCoreStartupDone */
|
||||
getcoreid a3
|
||||
addx4 a2, a3, a2 /* a2 = &port_uxCoreStartupDone[coreid] */
|
||||
movi a3, 1 /* a3 = 1 */
|
||||
s32i a3, a2, 0 /* port_uxCoreStartupDone[coreid] = 1 */
|
||||
#endif /* ( configNUM_CORES > 1 ) */
|
||||
|
||||
/* Determine the type of stack frame. */
|
||||
l32i a2, sp, XT_STK_EXIT /* exit dispatcher or solicited flag */
|
||||
bnez a2, .L_frxt_dispatch_stk
|
||||
|
||||
@@ -108,7 +108,7 @@ void esp_startup_start_app_other_cores(void)
|
||||
}
|
||||
|
||||
// Wait for CPU0 to start FreeRTOS before progressing
|
||||
extern volatile unsigned port_xSchedulerRunning[portNUM_PROCESSORS];
|
||||
extern volatile unsigned port_xSchedulerRunning[CONFIG_FREERTOS_NUMBER_OF_CORES];
|
||||
while (port_xSchedulerRunning[0] == 0) {
|
||||
;
|
||||
}
|
||||
@@ -137,27 +137,27 @@ void esp_startup_start_app_other_cores(void)
|
||||
|
||||
ESP_LOG_ATTR_TAG(MAIN_TAG, "main_task");
|
||||
|
||||
#if !CONFIG_FREERTOS_UNICORE
|
||||
static volatile bool s_other_cpu_startup_done = false;
|
||||
static bool other_cpu_startup_idle_hook_cb(void)
|
||||
/* This function has to guarantee that all CPUs have finished the FreeRTOS initialization
|
||||
* and the startup stack is not in use.
|
||||
*/
|
||||
static void wait_for_all_cores_ready(void)
|
||||
{
|
||||
s_other_cpu_startup_done = true;
|
||||
return true;
|
||||
#if !CONFIG_FREERTOS_UNICORE
|
||||
extern volatile unsigned port_uxCoreStartupDone[CONFIG_FREERTOS_NUMBER_OF_CORES];
|
||||
bool all_cpus_has_been_started;
|
||||
do {
|
||||
all_cpus_has_been_started = true;
|
||||
for (int cpu = 0; cpu < CONFIG_FREERTOS_NUMBER_OF_CORES; cpu++) {
|
||||
all_cpus_has_been_started &= port_uxCoreStartupDone[cpu] != 0;
|
||||
}
|
||||
} while (!all_cpus_has_been_started);
|
||||
#endif // !CONFIG_FREERTOS_UNICORE
|
||||
}
|
||||
#endif
|
||||
|
||||
static void main_task(void* args)
|
||||
// Reinitialize the startup stack heaps, so it can be used for heap allocation.
|
||||
static void reclaim_startup_stack_memory_for_heap(void)
|
||||
{
|
||||
ESP_LOGI(MAIN_TAG, "Started on CPU%d", (int)xPortGetCoreID());
|
||||
#if !CONFIG_FREERTOS_UNICORE
|
||||
// Wait for FreeRTOS initialization to finish on other core, before replacing its startup stack
|
||||
esp_register_freertos_idle_hook_for_cpu(other_cpu_startup_idle_hook_cb, !xPortGetCoreID());
|
||||
while (!s_other_cpu_startup_done) {
|
||||
;
|
||||
}
|
||||
esp_deregister_freertos_idle_hook_for_cpu(other_cpu_startup_idle_hook_cb, !xPortGetCoreID());
|
||||
#endif
|
||||
|
||||
wait_for_all_cores_ready();
|
||||
// [refactor-todo] check if there is a way to move the following block to esp_system startup
|
||||
heap_caps_enable_nonos_stack_heaps();
|
||||
|
||||
@@ -171,6 +171,13 @@ static void main_task(void* args)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void main_task(void* args)
|
||||
{
|
||||
ESP_LOGI(MAIN_TAG, "Started on CPU%d", (int)xPortGetCoreID());
|
||||
|
||||
reclaim_startup_stack_memory_for_heap();
|
||||
|
||||
// Initialize TWDT if configured to do so
|
||||
#if CONFIG_ESP_TASK_WDT_INIT
|
||||
|
||||
Reference in New Issue
Block a user