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:
Jonas Jonsson
2026-05-15 11:25:51 +03:00
committed by Konstantin Kondrashov
parent 1ece98b42d
commit dc5ae69a23
9 changed files with 89 additions and 28 deletions
@@ -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 )
@@ -152,6 +153,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++) {
@@ -23,6 +23,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
@@ -717,6 +718,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,
@@ -72,6 +72,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
/*
*******************************************************************************
@@ -106,7 +107,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.
@@ -281,7 +281,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.
*
**********************************************************************************************************
*/
@@ -370,7 +370,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).
*
**********************************************************************************************************
@@ -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