From 90f6eeb32785b3701d54fcc3b9f618af76adc00f Mon Sep 17 00:00:00 2001 From: wuzhenghui Date: Wed, 3 Jun 2026 17:51:34 +0800 Subject: [PATCH 1/4] feat(freertos): introduce thread-safe context management functions --- .../riscv/include/freertos/portmacro.h | 18 +++++++++++ .../FreeRTOS-Kernel-SMP/portable/riscv/port.c | 26 +++++++++++++++- .../xtensa/include/freertos/portmacro.h | 20 ++++++++++++- .../portable/xtensa/port.c | 24 +++++++++++++++ .../freertos/FreeRTOS-Kernel-SMP/tasks.c | 12 +++++++- .../freertos/FreeRTOS-Kernel/idf_changes.md | 4 +++ .../riscv/include/freertos/portmacro.h | 16 ++++++++++ .../FreeRTOS-Kernel/portable/riscv/port.c | 30 +++++++++++++++++++ .../xtensa/include/freertos/portmacro.h | 18 ++++++++++- .../FreeRTOS-Kernel/portable/xtensa/port.c | 23 ++++++++++++++ .../freertos/include/freertos_performance.h | 2 +- docs/en/api-reference/system/freertos_idf.rst | 13 ++++++++ .../api-reference/system/freertos_idf.rst | 13 ++++++++ 13 files changed, 214 insertions(+), 5 deletions(-) diff --git a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/include/freertos/portmacro.h b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/include/freertos/portmacro.h index d63062ca520..d0d85db6330 100644 --- a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/include/freertos/portmacro.h +++ b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/include/freertos/portmacro.h @@ -327,6 +327,24 @@ and vPortExitCritical() from precompiled libraries (.a) thereby failing linking. void vPortEnterCritical(void); void vPortExitCritical(void); +/** + * @brief Claim thread-safe region start + * If claimed, vPortEnterCritical/vPortExitCritical on the current core are no-ops. + * Only can be used in single-core running context with interrupts disabled. + * @note !!! Caller must guarantee thread safety between Claim and Disclaim !!! + */ +void xPortThreadSafeClaim(void); + +/** + * @brief Claim thread-safe region end + * Restores normal port critical behavior + * Only can be used in single-core running context with interrupts disabled. + * @note !!! Caller must guarantee thread safety between Claim and Disclaim !!! + */ +void xPortThreadSafeDisclaim(void); + +extern volatile bool port_xThreadSafeClaimed; + //IDF task critical sections #define portTRY_ENTER_CRITICAL(lock, timeout) ({(void) lock; (void) timeout; vPortEnterCritical(); pdPASS;}) #define portENTER_CRITICAL_IDF(lock) ({(void) lock; vPortEnterCritical();}) diff --git a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c index e77a989c3d9..5c37ecd52d3 100644 --- a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c +++ b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c @@ -1,11 +1,13 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "sdkconfig.h" +#include #include +#include "esp_compiler.h" #include "soc/soc_caps.h" #include "soc/periph_defs.h" #include "soc/system_reg.h" @@ -75,6 +77,7 @@ StackType_t *xIsrStackTop = &xIsrStack[0] + (configISR_STACK_SIZE & (~((portPOIN // Variables used for IDF style critical sections. These are orthogonal to FreeRTOS critical sections static UBaseType_t port_uxCriticalNestingIDF = 0; static UBaseType_t port_uxCriticalOldInterruptStateIDF = 0; +volatile bool port_xThreadSafeClaimed = false; /* ------------------------------------------------ IDF Compatibility -------------------------------------------------- * - These need to be defined for IDF to compile @@ -82,8 +85,25 @@ static UBaseType_t port_uxCriticalOldInterruptStateIDF = 0; // ------------------ Critical Sections -------------------- +void xPortThreadSafeClaim(void) +{ + configASSERT(!xPortCanYield()); + configASSERT(!port_xThreadSafeClaimed); + port_xThreadSafeClaimed = true; +} + +void xPortThreadSafeDisclaim(void) +{ + configASSERT(!xPortCanYield()); + configASSERT(port_xThreadSafeClaimed); + port_xThreadSafeClaimed = false; +} + void vPortEnterCritical(void) { + if (unlikely(port_xThreadSafeClaimed)) { + return; + } // Save current interrupt threshold and disable interrupts UBaseType_t old_thresh = ulPortSetInterruptMask(); // Update the IDF critical nesting count @@ -96,6 +116,9 @@ void vPortEnterCritical(void) void vPortExitCritical(void) { + if (unlikely(port_xThreadSafeClaimed)) { + return; + } /* Critical section nesting coung must never be negative */ configASSERT( port_uxCriticalNestingIDF > 0 ); @@ -293,6 +316,7 @@ BaseType_t xPortStartScheduler(void) { uxInterruptNesting = 0; port_uxCriticalNestingIDF = 0; + port_xThreadSafeClaimed = false; uxSchedulerRunning = 0; #if configNUM_CORES > 1 port_uxCoreStartupDone[xPortGetCoreID()] = 0; diff --git a/components/freertos/FreeRTOS-Kernel-SMP/portable/xtensa/include/freertos/portmacro.h b/components/freertos/FreeRTOS-Kernel-SMP/portable/xtensa/include/freertos/portmacro.h index 2c59f7b5737..321f99232cc 100644 --- a/components/freertos/FreeRTOS-Kernel-SMP/portable/xtensa/include/freertos/portmacro.h +++ b/components/freertos/FreeRTOS-Kernel-SMP/portable/xtensa/include/freertos/portmacro.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -321,6 +321,24 @@ static inline void __attribute__((always_inline)) vPortCPUReleaseMutex(portMUX_T // ------------------ Critical Sections -------------------- +/** + * @brief Claim thread-safe region start + * If claimed, vPortEnterCritical/vPortExitCritical on the current core are no-ops. + * Only can be used in single-core running context with interrupts disabled. + * @note !!! Caller must guarantee thread safety between Claim and Disclaim !!! + */ +void xPortThreadSafeClaim(void); + +/** + * @brief Claim thread-safe region end + * Restores normal port critical behavior + * Only can be used in single-core running context with interrupts disabled. + * @note !!! Caller must guarantee thread safety between Claim and Disclaim !!! + */ +void xPortThreadSafeDisclaim(void); + +extern volatile bool port_xThreadSafeClaimed; + BaseType_t xPortEnterCriticalTimeout(portMUX_TYPE *lock, BaseType_t timeout); static inline void __attribute__((always_inline)) vPortEnterCriticalIDF(portMUX_TYPE *lock) diff --git a/components/freertos/FreeRTOS-Kernel-SMP/portable/xtensa/port.c b/components/freertos/FreeRTOS-Kernel-SMP/portable/xtensa/port.c index d96180c422d..6d3282ae06f 100644 --- a/components/freertos/FreeRTOS-Kernel-SMP/portable/xtensa/port.c +++ b/components/freertos/FreeRTOS-Kernel-SMP/portable/xtensa/port.c @@ -5,8 +5,10 @@ */ #include "sdkconfig.h" +#include #include #include +#include "esp_compiler.h" #include "FreeRTOS.h" #include "task.h" //For vApplicationStackOverflowHook #include "port_systick.h" @@ -102,6 +104,7 @@ Variables used by IDF critical sections only (SMP tracks critical nesting inside */ BaseType_t port_uxCriticalNestingIDF[portNUM_PROCESSORS] = {0}; BaseType_t port_uxCriticalOldInterruptStateIDF[portNUM_PROCESSORS] = {0}; +volatile bool port_xThreadSafeClaimed = false; /* ******************************************************************************* @@ -113,8 +116,25 @@ volatile StackType_t DRAM_ATTR __attribute__((aligned(16))) port_IntStack[portNU /* One flag for each individual CPU. */ volatile uint32_t port_switch_flag[portNUM_PROCESSORS]; +void xPortThreadSafeClaim(void) +{ + configASSERT(!xPortCanYield()); + configASSERT(!port_xThreadSafeClaimed); + port_xThreadSafeClaimed = true; +} + +void xPortThreadSafeDisclaim(void) +{ + configASSERT(!xPortCanYield()); + configASSERT(port_xThreadSafeClaimed); + port_xThreadSafeClaimed = false; +} + BaseType_t xPortEnterCriticalTimeout(portMUX_TYPE *lock, BaseType_t timeout) { + if (unlikely(port_xThreadSafeClaimed)) { + return pdPASS; + } /* Interrupts may already be disabled (if this function is called in nested * manner). However, there's no atomic operation that will allow us to check, * thus we have to disable interrupts again anyways. @@ -143,6 +163,9 @@ BaseType_t xPortEnterCriticalTimeout(portMUX_TYPE *lock, BaseType_t timeout) void vPortExitCriticalIDF(portMUX_TYPE *lock) { + if (unlikely(port_xThreadSafeClaimed)) { + return; + } /* This function may be called in a nested manner. Therefore, we only need * to re-enable interrupts if this is the last call to exit the critical. We * can use the nesting count to determine whether this is the last exit call. @@ -338,6 +361,7 @@ BaseType_t xPortStartScheduler( void ) BaseType_t coreID = xPortGetCoreID(); port_xSchedulerRunning[coreID] = 1; port_uxCoreStartupDone[coreID] = 0; + port_xThreadSafeClaimed = false; #if configNUM_CORES > 1 // Workaround for non-thread safe multi-core OS startup (see IDF-4524) diff --git a/components/freertos/FreeRTOS-Kernel-SMP/tasks.c b/components/freertos/FreeRTOS-Kernel-SMP/tasks.c index 29e1cd988ae..038c5cb4ee7 100644 --- a/components/freertos/FreeRTOS-Kernel-SMP/tasks.c +++ b/components/freertos/FreeRTOS-Kernel-SMP/tasks.c @@ -6,7 +6,7 @@ * * SPDX-License-Identifier: MIT * - * SPDX-FileContributor: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileContributor: 2023-2026 Espressif Systems (Shanghai) CO LTD * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -6968,6 +6968,11 @@ static void prvResetNextTaskUnblockTime( void ) void vTaskEnterCritical( void ) { + if( port_xThreadSafeClaimed ) + { + return; + } + traceENTER_vTaskEnterCritical(); portDISABLE_INTERRUPTS(); @@ -7095,6 +7100,11 @@ static void prvResetNextTaskUnblockTime( void ) void vTaskExitCritical( void ) { + if( port_xThreadSafeClaimed ) + { + return; + } + traceENTER_vTaskExitCritical(); if( xSchedulerRunning != pdFALSE ) diff --git a/components/freertos/FreeRTOS-Kernel/idf_changes.md b/components/freertos/FreeRTOS-Kernel/idf_changes.md index 0a75fa8cf8a..9a974217cb1 100644 --- a/components/freertos/FreeRTOS-Kernel/idf_changes.md +++ b/components/freertos/FreeRTOS-Kernel/idf_changes.md @@ -142,6 +142,10 @@ The following functions were modified to accommodate SMP behavior: - Queues no longer use queue locks (see `queueUSE_LOCKS`) - Queues now just use critical sections and skips queue locking - Queue functions can now execute within a single critical section block +- New `xPortThreadSafeClaim()` / `xPortThreadSafeDisclaim()` + - While claimed, port-layer vPortEnterCritical/vPortExitCritical on the current core are no-ops + - Claim only with interrupts disabled on the current core; one active claim system-wide; must pair with Disclaim + - Documented in `portmacro.h` and `docs/en/api-reference/system/freertos_idf.rst` ## Single Core Differences diff --git a/components/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos/portmacro.h b/components/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos/portmacro.h index 462fd852e6d..94f5231bc8a 100644 --- a/components/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos/portmacro.h +++ b/components/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos/portmacro.h @@ -230,6 +230,22 @@ void vPortEnterCritical(void); */ void vPortExitCritical(void); +/** + * @brief Claim thread-safe region start + * If claimed, vPortEnterCritical/vPortExitCritical on the current core are no-ops. + * Only can be used in single-core running context with interrupts disabled. + * @note !!! Caller must guarantee thread safety between Claim and Disclaim !!! + */ +void xPortThreadSafeClaim(void); + +/** + * @brief Claim thread-safe region end + * Restores normal port critical behavior + * Only can be used in single-core running context with interrupts disabled. + * @note !!! Caller must guarantee thread safety between Claim and Disclaim !!! + */ +void xPortThreadSafeDisclaim(void); + #if (configNUM_CORES > 1) /** * @brief Enter an SMP critical section with a timeout diff --git a/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c b/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c index 61c02998fb7..cee52c33edc 100644 --- a/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c +++ b/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c @@ -35,6 +35,7 @@ *----------------------------------------------------------------------*/ #include "sdkconfig.h" +#include #include #include "soc/soc_caps.h" #include "soc/periph_defs.h" @@ -48,6 +49,7 @@ #include "esp_private/crosscore_int.h" #include "hal/crosscore_int_ll.h" #include "esp_attr.h" +#include "esp_compiler.h" #include "esp_system.h" #include "esp_intr_alloc.h" #include "esp_log.h" @@ -97,6 +99,7 @@ _Static_assert(offsetof( StaticTask_t, pxDummy8 ) == PORT_OFFSET_PX_END_OF_STACK volatile UBaseType_t port_xSchedulerRunning[portNUM_PROCESSORS] = {0}; // Indicates whether scheduler is running on a per-core basis volatile UBaseType_t port_uxInterruptNesting[portNUM_PROCESSORS] = {0}; // Interrupt nesting level. Increased/decreased in portasm.c volatile UBaseType_t port_uxCriticalNesting[portNUM_PROCESSORS] = {0}; +volatile bool port_xThreadSafeClaimed = false; 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 @@ -156,6 +159,7 @@ BaseType_t xPortStartScheduler(void) BaseType_t coreID = xPortGetCoreID(); port_uxInterruptNesting[coreID] = 0; port_uxCriticalNesting[coreID] = 0; + port_xThreadSafeClaimed = false; port_xSchedulerRunning[coreID] = 0; port_uxCoreStartupDone[coreID] = 0; @@ -525,6 +529,9 @@ void vPortClearInterruptMaskFromISR(UBaseType_t prev_int_level) #if (configNUM_CORES > 1) BaseType_t __attribute__((optimize("-O3"))) xPortEnterCriticalTimeout(portMUX_TYPE *mux, BaseType_t timeout) { + if (unlikely(port_xThreadSafeClaimed)) { + return pdPASS; + } /* Interrupts may already be disabled (if this function is called in nested * manner). However, there's no atomic operation that will allow us to check, * thus we have to disable interrupts again anyways. @@ -552,6 +559,9 @@ BaseType_t __attribute__((optimize("-O3"))) xPortEnterCriticalTimeout(portMUX_TY void __attribute__((optimize("-O3"))) vPortExitCriticalMultiCore(portMUX_TYPE *mux) { + if (unlikely(port_xThreadSafeClaimed)) { + return; + } /* This function may be called in a nested manner. Therefore, we only need * to re-enable interrupts if this is the last call to exit the critical. We * can use the nesting count to determine whether this is the last exit call. @@ -599,12 +609,29 @@ void vPortExitCriticalCompliance(portMUX_TYPE *mux) } #endif /* (configNUM_CORES > 1) */ +void xPortThreadSafeClaim(void) +{ + configASSERT(!xPortCanYield()); + configASSERT(!port_xThreadSafeClaimed); + port_xThreadSafeClaimed = true; +} + +void xPortThreadSafeDisclaim(void) +{ + configASSERT(!xPortCanYield()); + configASSERT(port_xThreadSafeClaimed); + port_xThreadSafeClaimed = false; +} + void vPortEnterCritical(void) { #if (configNUM_CORES > 1) esp_rom_printf("vPortEnterCritical(void) is not supported on multi-core targets. Please use vPortEnterCriticalMultiCore(portMUX_TYPE *mux) instead.\n"); abort(); #endif /* (configNUM_CORES > 1) */ + if (unlikely(port_xThreadSafeClaimed)) { + return; + } BaseType_t state = portSET_INTERRUPT_MASK_FROM_ISR(); port_uxCriticalNesting[0]++; @@ -619,6 +646,9 @@ void vPortExitCritical(void) esp_rom_printf("vPortExitCritical(void) is not supported on multi-core targets. Please use vPortExitCriticalMultiCore(portMUX_TYPE *mux) instead.\n"); abort(); #endif /* (configNUM_CORES > 1) */ + if (unlikely(port_xThreadSafeClaimed)) { + return; + } /* Critical section nesting count must never be negative */ configASSERT( port_uxCriticalNesting[0] > 0 ); diff --git a/components/freertos/FreeRTOS-Kernel/portable/xtensa/include/freertos/portmacro.h b/components/freertos/FreeRTOS-Kernel/portable/xtensa/include/freertos/portmacro.h index 34f387ff25c..9847b325e5a 100644 --- a/components/freertos/FreeRTOS-Kernel/portable/xtensa/include/freertos/portmacro.h +++ b/components/freertos/FreeRTOS-Kernel/portable/xtensa/include/freertos/portmacro.h @@ -8,7 +8,7 @@ * * SPDX-License-Identifier: MIT * - * SPDX-FileContributor: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileContributor: 2023-2026 Espressif Systems (Shanghai) CO LTD * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -229,6 +229,22 @@ static inline void __attribute__((always_inline)) vPortEnterCritical(portMUX_TYP */ void vPortExitCritical(portMUX_TYPE *mux); +/** + * @brief Claim thread-safe region start + * If claimed, vPortEnterCritical/vPortExitCritical on the current core are no-ops. + * Only can be used in single-core running context with interrupts disabled. + * @note !!! Caller must guarantee thread safety between Claim and Disclaim !!! + */ +void xPortThreadSafeClaim(void); + +/** + * @brief Claim thread-safe region end + * Restores normal port critical behavior + * Only can be used in single-core running context with interrupts disabled. + * @note !!! Caller must guarantee thread safety between Claim and Disclaim !!! + */ +void xPortThreadSafeDisclaim(void); + /** * @brief FreeRTOS Compliant version of xPortEnterCriticalTimeout() * diff --git a/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c b/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c index 558475de231..82ddf41829a 100644 --- a/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c +++ b/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c @@ -33,6 +33,7 @@ */ #include "sdkconfig.h" +#include #include #include #include @@ -40,6 +41,7 @@ #include #include "soc/soc_caps.h" #include "esp_attr.h" +#include "esp_compiler.h" #include "esp_private/crosscore_int.h" #include "esp_private/esp_int_wdt.h" #include "esp_system.h" @@ -75,6 +77,7 @@ const DRAM_ATTR uint32_t offset_xCoreID = offsetof(StaticTask_t, xDummyCoreID); volatile unsigned port_xSchedulerRunning[portNUM_PROCESSORS] = {0}; // Indicates whether scheduler is running on a per-core basis 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}; +volatile bool port_xThreadSafeClaimed = false; BaseType_t port_uxOldInterruptState[portNUM_PROCESSORS] = {0}; volatile unsigned port_uxCoreStartupDone[portNUM_PROCESSORS] = {0}; // Indicates whether the core has completed its startup sequence @@ -479,8 +482,25 @@ BaseType_t xPortInterruptedFromISRContext(void) // ------------------ Critical Sections -------------------- +void xPortThreadSafeClaim(void) +{ + configASSERT(!xPortCanYield()); + configASSERT(!port_xThreadSafeClaimed); + port_xThreadSafeClaimed = true; +} + +void xPortThreadSafeDisclaim(void) +{ + configASSERT(!xPortCanYield()); + configASSERT(port_xThreadSafeClaimed); + port_xThreadSafeClaimed = false; +} + BaseType_t __attribute__((optimize("-O3"))) xPortEnterCriticalTimeout(portMUX_TYPE *mux, BaseType_t timeout) { + if (unlikely(port_xThreadSafeClaimed)) { + return pdPASS; + } /* Interrupts may already be disabled (if this function is called in nested * manner). However, there's no atomic operation that will allow us to check, * thus we have to disable interrupts again anyways. @@ -508,6 +528,9 @@ BaseType_t __attribute__((optimize("-O3"))) xPortEnterCriticalTimeout(portMUX_TY void __attribute__((optimize("-O3"))) vPortExitCritical(portMUX_TYPE *mux) { + if (unlikely(port_xThreadSafeClaimed)) { + return; + } /* This function may be called in a nested manner. Therefore, we only need * to re-enable interrupts if this is the last call to exit the critical. We * can use the nesting count to determine whether this is the last exit call. diff --git a/components/freertos/test_apps/freertos/include/freertos_performance.h b/components/freertos/test_apps/freertos/include/freertos_performance.h index e133460f382..c281f68a1e2 100644 --- a/components/freertos/test_apps/freertos/include/freertos_performance.h +++ b/components/freertos/test_apps/freertos/include/freertos_performance.h @@ -5,7 +5,7 @@ #pragma once #ifndef IDF_PERFORMANCE_MAX_FREERTOS_SPINLOCK_CYCLES_PER_OP -#define IDF_PERFORMANCE_MAX_FREERTOS_SPINLOCK_CYCLES_PER_OP 215 +#define IDF_PERFORMANCE_MAX_FREERTOS_SPINLOCK_CYCLES_PER_OP 230 #endif #ifndef IDF_PERFORMANCE_MAX_FREERTOS_SPINLOCK_CYCLES_PER_OP_PSRAM #define IDF_PERFORMANCE_MAX_FREERTOS_SPINLOCK_CYCLES_PER_OP_PSRAM 300 diff --git a/docs/en/api-reference/system/freertos_idf.rst b/docs/en/api-reference/system/freertos_idf.rst index f28f8b4f268..10107efa6d9 100644 --- a/docs/en/api-reference/system/freertos_idf.rst +++ b/docs/en/api-reference/system/freertos_idf.rst @@ -382,6 +382,19 @@ In IDF FreeRTOS, the process of a particular core entering and exiting a critica #. The core releases the spinlock by clearing the spinlock's owner value. #. The core re-enables interrupts or interrupt nesting. +Thread-Safe Port Critical Bypass +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``xPortThreadSafeClaim()`` and ``xPortThreadSafeDisclaim()`` (``portmacro.h``) skip port-layer critical enter/exit on the current core. **Thread safety between Claim and Disclaim must be guaranteed by the caller.** + +.. warning:: + + - Caller guarantees thread safety for the Claim–Disclaim window (all cores). + - Claim only with interrupts disabled on the current core; one active claim system-wide; pair with Disclaim on every path. + - ``pdPASS`` from ``xPortEnterCriticalTimeout()`` does not mean ``mux`` was taken. + +Not a substitute for ``taskENTER_CRITICAL(&spinlock)``. + Restrictions and Considerations ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/zh_CN/api-reference/system/freertos_idf.rst b/docs/zh_CN/api-reference/system/freertos_idf.rst index 4b0a3e539f0..4ce7c8bfd76 100644 --- a/docs/zh_CN/api-reference/system/freertos_idf.rst +++ b/docs/zh_CN/api-reference/system/freertos_idf.rst @@ -382,6 +382,19 @@ IDF FreeRTOS 中,特定核进入和退出临界区的过程如下: #. 核通过清除自旋锁的所有者值释放自旋锁。 #. 核重新启用中断或中断嵌套。 +线程安全 Port 临界区旁路 +^^^^^^^^^^^^^^^^^^^^^^^^ + +``xPortThreadSafeClaim()`` 与 ``xPortThreadSafeDisclaim()`` (``portmacro.h``) 使当前核上 port 层临界区进入/退出变为 no-op。**Claim 与 Disclaim 之间的线程安全须由调用方保证。** + +.. warning:: + + - 调用方保证 Claim–Disclaim 窗口内的线程安全(含多核)。 + - 仅在当前核已关中断时 Claim;全局至多一个 Claim;所有路径须与 Disclaim 成对。 + - ``xPortEnterCriticalTimeout()`` 返回 ``pdPASS`` 不表示已获取 ``mux``。 + +不能替代 ``taskENTER_CRITICAL(&spinlock)``。 + 限制与注意事项 ^^^^^^^^^^^^^^ From d4e3109c1b9c31f43c6f44d02c64e58878a46ed8 Mon Sep 17 00:00:00 2001 From: wuzhenghui Date: Wed, 3 Jun 2026 19:35:18 +0800 Subject: [PATCH 2/4] feat(esp_hw_support): claim thread-safe in sleep process to avoid deadlock and fastup process --- components/esp_hw_support/sleep_modes.c | 56 ++++++++----------------- 1 file changed, 17 insertions(+), 39 deletions(-) diff --git a/components/esp_hw_support/sleep_modes.c b/components/esp_hw_support/sleep_modes.c index 114d787e3b1..583d24b047a 100644 --- a/components/esp_hw_support/sleep_modes.c +++ b/components/esp_hw_support/sleep_modes.c @@ -1248,20 +1248,17 @@ static esp_err_t FORCE_IRAM_ATTR deep_sleep_start(bool allow_sleep_rejection) esp_sync_timekeeping_timers(); - // Must acquire all spinlocks which may be acquired during sleep process before stalling other core, - // otherwise deadlock may occur. - esp_os_enter_critical(&spinlock_rtc_deep_sleep); -#if !CONFIG_FREERTOS_UNICORE - extern portMUX_TYPE rtc_spinlock; - esp_os_enter_critical_safe(&rtc_spinlock); // Maybe acquired from temp_sensor_get_raw_value by phy_close_rf callback - esp_clk_private_lock(); // Maybe acquired from esp_clk_slowclk_cal_set -#endif - /* Disable interrupts and stall another core in case another task writes * to RTC memory while we calculate RTC memory CRC. */ + esp_os_enter_critical(&spinlock_rtc_deep_sleep); esp_ipc_isr_stall_other_cpu(); esp_ipc_isr_stall_pause(); + /* Another core is stalled and interrupts are disabled, so we can safely claim the thread-safe + critical section to avoid deadlocks and fastup the sleep process. + */ + xPortThreadSafeClaim(); + #if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX // The other core will be stalled by high-priority interrupt and spins on variables in internal RAM, // which naturally avoids cache livelock, so the 20ms livelock workaround timeout is not needed. @@ -1354,12 +1351,11 @@ static esp_err_t FORCE_IRAM_ATTR deep_sleep_start(bool allow_sleep_rejection) // Configure WDT to use livelock workaround timeout after releasing other CPU esp_int_wdt_livelock_workaround(true); #endif + + /* Restore port critical before unstalling other CPU */ + xPortThreadSafeDisclaim(); esp_ipc_isr_stall_resume(); esp_ipc_isr_release_other_cpu(); -#if !CONFIG_FREERTOS_UNICORE - esp_clk_private_unlock(); - esp_os_exit_critical_safe(&rtc_spinlock); -#endif esp_os_exit_critical(&spinlock_rtc_deep_sleep); return err; } @@ -1459,30 +1455,8 @@ esp_err_t esp_light_sleep_start(void) timerret = esp_task_wdt_stop(); #endif // CONFIG_ESP_TASK_WDT_USE_ESP_TIMER - esp_os_enter_critical(&s_config.lock); - /* - Note: We are about to stall the other CPU via the esp_ipc_isr_stall_other_cpu(). However, there is a chance of - deadlock if after stalling the other CPU, we attempt to take spinlocks already held by the other CPU that is. - - Thus any functions that we call after stalling the other CPU will need to have the locks taken first to avoid - deadlock. - - Todo: IDF-5257 - */ - - /* We will be calling esp_timer_private_set inside DPORT access critical - * section. Make sure the code on the other CPU is not holding esp_timer - * lock, otherwise there will be deadlock. - */ esp_timer_private_lock(); - - /* We will be calling esp_rtc_get_time_us() below. Make sure the code on the other CPU is not holding the - * esp_rtc_get_time_us() lock, otherwise there will be deadlock. esp_rtc_get_time_us() is called via: - * - * - esp_clk_slowclk_cal_set() -> esp_rtc_get_time_us() - */ - esp_clk_private_lock(); - + esp_os_enter_critical(&s_config.lock); s_config.rtc_ticks_at_sleep_start = rtc_time_get(); uint32_t ccount_at_sleep_start = esp_cpu_get_cycle_count(); esp_sleep_execute_event_callbacks(SLEEP_EVENT_HW_TIME_START, (void *)0); @@ -1508,6 +1482,10 @@ esp_err_t esp_light_sleep_start(void) #endif esp_ipc_isr_stall_pause(); #endif + /* Another core is stalled and interrupts are disabled, so we can safely claim the thread-safe + critical section to avoid deadlocks and fastup the sleep process. + */ + xPortThreadSafeClaim(); #if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION && CONFIG_PM_SLP_IRAM_OPT /* Cache Suspend 0: if CONFIG_PM_SLP_IRAM_OPT is enabled, suspend cache here so that the access to flash @@ -1709,9 +1687,6 @@ esp_err_t esp_light_sleep_start(void) #endif } - esp_clk_private_unlock(); - esp_timer_private_unlock(); - #if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED && !CONFIG_SPIRAM_XIP_FROM_PSRAM && CONFIG_PM_SLP_IRAM_OPT // If CONFIG_SPIRAM_XIP_FROM_PSRAM is not enabled and CONFIG_PM_SLP_IRAM_OPT is enable, // the sleep-wake process prior to this point does not access the PSRAM, so we can postpone waiting @@ -1727,6 +1702,9 @@ esp_err_t esp_light_sleep_start(void) } #endif + /* Restore port critical before unstalling other CPU */ + xPortThreadSafeDisclaim(); + esp_timer_private_unlock(); #if !CONFIG_FREERTOS_UNICORE esp_ipc_isr_stall_resume(); #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW From 0c7caf79b5948c56b9fd6852b3e47bd1ca443d89 Mon Sep 17 00:00:00 2001 From: wuzhenghui Date: Wed, 17 Jun 2026 17:51:23 +0800 Subject: [PATCH 3/4] feat(esp_hw_support): implement software CPU retention synchronization for SMP sleep modes --- .../include/esp_private/sleep_cpu.h | 23 +++- .../lowpower/port/esp32h4/sleep_cpu.c | 18 +--- .../lowpower/port/esp32p4/sleep_cpu.c | 18 +--- .../lowpower/port/esp32s31/sleep_cpu.c | 18 +--- components/esp_hw_support/sleep_modes.c | 101 +++++++++++------- 5 files changed, 92 insertions(+), 86 deletions(-) diff --git a/components/esp_hw_support/include/esp_private/sleep_cpu.h b/components/esp_hw_support/include/esp_private/sleep_cpu.h index 76bb557910a..905d24ea7d3 100644 --- a/components/esp_hw_support/include/esp_private/sleep_cpu.h +++ b/components/esp_hw_support/include/esp_private/sleep_cpu.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -69,14 +69,27 @@ esp_err_t esp_sleep_cpu_retention(uint32_t (*goto_sleep)(uint32_t, uint32_t, uin #if !CONFIG_FREERTOS_UNICORE && CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU /** - * Do sleep prepare for other smp cores + * Start the software CPU retention synchronization. + * + * This waits until the other CPU retention state to IDLE before + * the caller starts the software CPU retention. */ -void sleep_smp_cpu_sleep_prepare(void); +void sleep_cpu_retention_start(void); /** - * Do wakeup prepare for other smp cores + * Run the software CPU retention flow on the stalled other CPU. + * + * @param arg Unused */ -void sleep_smp_cpu_wakeup_prepare(void); +void sleep_cpu_retention_execute(void* arg); + +/** + * Finish the software CPU retention synchronization. + * + * This is called by the initiating CPU after wakeup or sleep rejection to + * synchronize with the other CPU retention state and return its own state to idle. + */ +void sleep_cpu_retention_finish(void); /** * Notify the other core that this sleep does not require retention. diff --git a/components/esp_hw_support/lowpower/port/esp32h4/sleep_cpu.c b/components/esp_hw_support/lowpower/port/esp32h4/sleep_cpu.c index 546101cfae1..ba09ce46e19 100644 --- a/components/esp_hw_support/lowpower/port/esp32h4/sleep_cpu.c +++ b/components/esp_hw_support/lowpower/port/esp32h4/sleep_cpu.c @@ -477,17 +477,12 @@ esp_err_t sleep_cpu_configure(bool light_sleep_enable) #if !CONFIG_FREERTOS_UNICORE #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU -static IRAM_ATTR void smp_core_do_retention(void) +IRAM_ATTR void sleep_cpu_retention_execute(void* arg) { + (void) arg; esp_cpu_branch_prediction_disable(); uint8_t core_id = esp_cpu_get_core_id(); - if (core_id == 0) { - WRITE_PERI_REG(INTPRI_CPU_INTR_FROM_CPU_2_REG, 0); - } else { - WRITE_PERI_REG(INTPRI_CPU_INTR_FROM_CPU_3_REG, 0); - } - // Wait another core start to do retention ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-infinite-loop") bool smp_skip_retention = false; @@ -560,19 +555,16 @@ IRAM_ATTR void esp_sleep_cpu_skip_retention(void) { } #endif -void sleep_smp_cpu_sleep_prepare(void) +void sleep_cpu_retention_start(void) { #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU while (atomic_load(&s_smp_retention_state[!esp_cpu_get_core_id()]) != SMP_IDLE) { ; } - esp_ipc_isr_call((esp_ipc_isr_func_t)smp_core_do_retention, NULL); -#else - esp_ipc_isr_stall_other_cpu(); #endif } -void sleep_smp_cpu_wakeup_prepare(void) +void sleep_cpu_retention_finish(void) { #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU uint8_t core_id = esp_cpu_get_core_id(); @@ -584,8 +576,6 @@ void sleep_smp_cpu_wakeup_prepare(void) ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-infinite-loop") } atomic_store(&s_smp_retention_state[core_id], SMP_IDLE); -#else - esp_ipc_isr_release_other_cpu(); #endif } #endif //!CONFIG_FREERTOS_UNICORE diff --git a/components/esp_hw_support/lowpower/port/esp32p4/sleep_cpu.c b/components/esp_hw_support/lowpower/port/esp32p4/sleep_cpu.c index 0104d6e55bd..eefa0849aa6 100644 --- a/components/esp_hw_support/lowpower/port/esp32p4/sleep_cpu.c +++ b/components/esp_hw_support/lowpower/port/esp32p4/sleep_cpu.c @@ -395,17 +395,12 @@ esp_err_t sleep_cpu_configure(bool light_sleep_enable) #if !CONFIG_FREERTOS_UNICORE #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU -static SPM_IRAM_ATTR void smp_core_do_retention(void) +SPM_IRAM_ATTR void sleep_cpu_retention_execute(void* arg) { + (void) arg; esp_cpu_branch_prediction_disable(); uint8_t core_id = esp_cpu_get_core_id(); - if (core_id == 0) { - WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_2_REG, 0); - } else { - WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_3_REG, 0); - } - // Wait another core start to do retention ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-infinite-loop") bool smp_skip_retention = false; @@ -475,19 +470,16 @@ SPM_IRAM_ATTR void esp_sleep_cpu_skip_retention(void) { } #endif -void sleep_smp_cpu_sleep_prepare(void) +void sleep_cpu_retention_start(void) { #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU while (atomic_load(&s_smp_retention_state[!esp_cpu_get_core_id()]) != SMP_IDLE) { ; } - esp_ipc_isr_call((esp_ipc_isr_func_t)smp_core_do_retention, NULL); -#else - esp_ipc_isr_stall_other_cpu(); #endif } -void sleep_smp_cpu_wakeup_prepare(void) +void sleep_cpu_retention_finish(void) { #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU uint8_t core_id = esp_cpu_get_core_id(); @@ -499,8 +491,6 @@ void sleep_smp_cpu_wakeup_prepare(void) ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-infinite-loop") } atomic_store(&s_smp_retention_state[core_id], SMP_IDLE); -#else - esp_ipc_isr_release_other_cpu(); #endif } #endif //!CONFIG_FREERTOS_UNICORE diff --git a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu.c b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu.c index 138f6066a1f..be12e505cf2 100644 --- a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu.c +++ b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu.c @@ -418,17 +418,12 @@ esp_err_t sleep_cpu_configure(bool light_sleep_enable) #if !CONFIG_FREERTOS_UNICORE #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU -static IRAM_ATTR void smp_core_do_retention(void) +IRAM_ATTR void sleep_cpu_retention_execute(void* arg) { + (void) arg; esp_cpu_branch_prediction_disable(); uint8_t core_id = esp_cpu_get_core_id(); - if (core_id == 0) { - WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_2_REG, 0); - } else { - WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_3_REG, 0); - } - // Wait another core start to do retention ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-infinite-loop") bool smp_skip_retention = false; @@ -496,19 +491,16 @@ IRAM_ATTR void esp_sleep_cpu_skip_retention(void) { } #endif -void sleep_smp_cpu_sleep_prepare(void) +void sleep_cpu_retention_start(void) { #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU while (atomic_load(&s_smp_retention_state[!esp_cpu_get_core_id()]) != SMP_IDLE) { ; } - esp_ipc_isr_call((esp_ipc_isr_func_t)smp_core_do_retention, NULL); -#else - esp_ipc_isr_stall_other_cpu(); #endif } -void sleep_smp_cpu_wakeup_prepare(void) +void sleep_cpu_retention_finish(void) { #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU uint8_t core_id = esp_cpu_get_core_id(); @@ -520,8 +512,6 @@ void sleep_smp_cpu_wakeup_prepare(void) ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-infinite-loop") } atomic_store(&s_smp_retention_state[core_id], SMP_IDLE); -#else - esp_ipc_isr_release_other_cpu(); #endif } #endif //!CONFIG_FREERTOS_UNICORE diff --git a/components/esp_hw_support/sleep_modes.c b/components/esp_hw_support/sleep_modes.c index 583d24b047a..9fb5ff4809a 100644 --- a/components/esp_hw_support/sleep_modes.c +++ b/components/esp_hw_support/sleep_modes.c @@ -351,10 +351,6 @@ static sleep_config_t s_config = { expected when determining wakeup cause. */ static bool s_light_sleep_wakeup = false; -/* Updating RTC_MEMORY_CRC_REG register via set_rtc_memory_crc() - is not thread-safe, so we need to disable interrupts before going to deep sleep. */ -static portMUX_TYPE __attribute__((unused)) spinlock_rtc_deep_sleep = portMUX_INITIALIZER_UNLOCKED; - ESP_LOG_ATTR_TAG(TAG, "sleep"); /* APP core of esp32 can't access to RTC FAST MEMORY, do not define it with RTC_IRAM_ATTR, @@ -525,28 +521,28 @@ esp_err_t esp_deep_sleep_try(uint64_t time_in_us) static esp_err_t s_sleep_hook_register(esp_deep_sleep_cb_t new_cb, esp_deep_sleep_cb_t s_cb_array[MAX_DSLP_HOOKS]) { - esp_os_enter_critical(&spinlock_rtc_deep_sleep); + esp_os_enter_critical(&s_config.lock); for (int n = 0; n < MAX_DSLP_HOOKS; n++) { if (s_cb_array[n]==NULL || s_cb_array[n]==new_cb) { s_cb_array[n]=new_cb; - esp_os_exit_critical(&spinlock_rtc_deep_sleep); + esp_os_exit_critical(&s_config.lock); return ESP_OK; } } - esp_os_exit_critical(&spinlock_rtc_deep_sleep); + esp_os_exit_critical(&s_config.lock); ESP_LOGE(TAG, "Registered deepsleep callbacks exceeds MAX_DSLP_HOOKS"); return ESP_ERR_NO_MEM; } static void s_sleep_hook_deregister(esp_deep_sleep_cb_t old_cb, esp_deep_sleep_cb_t s_cb_array[MAX_DSLP_HOOKS]) { - esp_os_enter_critical(&spinlock_rtc_deep_sleep); + esp_os_enter_critical(&s_config.lock); for (int n = 0; n < MAX_DSLP_HOOKS; n++) { if(s_cb_array[n] == old_cb) { s_cb_array[n] = NULL; } } - esp_os_exit_critical(&spinlock_rtc_deep_sleep); + esp_os_exit_critical(&s_config.lock); } esp_err_t esp_deep_sleep_register_hook(esp_deep_sleep_cb_t new_dslp_cb) @@ -1251,7 +1247,7 @@ static esp_err_t FORCE_IRAM_ATTR deep_sleep_start(bool allow_sleep_rejection) /* Disable interrupts and stall another core in case another task writes * to RTC memory while we calculate RTC memory CRC. */ - esp_os_enter_critical(&spinlock_rtc_deep_sleep); + esp_os_enter_critical(&s_config.lock); esp_ipc_isr_stall_other_cpu(); esp_ipc_isr_stall_pause(); /* Another core is stalled and interrupts are disabled, so we can safely claim the thread-safe @@ -1356,7 +1352,7 @@ static esp_err_t FORCE_IRAM_ATTR deep_sleep_start(bool allow_sleep_rejection) xPortThreadSafeDisclaim(); esp_ipc_isr_stall_resume(); esp_ipc_isr_release_other_cpu(); - esp_os_exit_critical(&spinlock_rtc_deep_sleep); + esp_os_exit_critical(&s_config.lock); return err; } @@ -1428,6 +1424,51 @@ static SLEEP_FN_ATTR esp_err_t esp_light_sleep_inner(uint32_t sleep_flags, uint3 return reject; } +#if !CONFIG_FREERTOS_UNICORE +static SLEEP_FN_ATTR esp_err_t sleep_smp_cpu_sleep_prepare(void) +{ +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW + sleep_cpu_retention_start(); +#endif +#if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX + // The other core will be stalled by high-priority interrupt and spins on variables in internal RAM, + // which naturally avoids cache livelock, so the 20ms livelock workaround timeout is not needed. + esp_int_wdt_livelock_workaround(false); +#endif + esp_err_t ipc_isr_err = esp_ipc_isr_stall_other_cpu_safe(); + if (ipc_isr_err == ESP_OK) { +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW + // Run CPU retention in the context of the other safely stalled CPU. + esp_ipc_isr_call(sleep_cpu_retention_execute, NULL); +#endif + esp_ipc_isr_stall_pause(); + } else { +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW + sleep_cpu_retention_finish(); +#endif +#if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX + // Configure WDT to use livelock workaround timeout after releasing other CPU + esp_int_wdt_livelock_workaround(true); +#endif + return ESP_ERR_SLEEP_REJECT; + } + return ESP_OK; +} + +static SLEEP_FN_ATTR void sleep_smp_cpu_wakeup_prepare(void) +{ + esp_ipc_isr_stall_resume(); +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW + sleep_cpu_retention_finish(); +#endif + esp_ipc_isr_release_other_cpu(); +#if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX + // Configure WDT to use livelock workaround timeout after releasing other CPU + esp_int_wdt_livelock_workaround(true); +#endif +} +#endif // !CONFIG_FREERTOS_UNICORE + /** * vddsdio is used for power supply of spi flash * @@ -1448,14 +1489,7 @@ esp_err_t esp_light_sleep_start(void) { s_config.ccount_ticks_record = esp_cpu_get_cycle_count(); esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_GOTO_SLEEP, (void *)0); -#if CONFIG_ESP_TASK_WDT_USE_ESP_TIMER - esp_err_t timerret = ESP_OK; - /* If a task watchdog timer is running, we have to stop it. */ - timerret = esp_task_wdt_stop(); -#endif // CONFIG_ESP_TASK_WDT_USE_ESP_TIMER - - esp_timer_private_lock(); esp_os_enter_critical(&s_config.lock); s_config.rtc_ticks_at_sleep_start = rtc_time_get(); uint32_t ccount_at_sleep_start = esp_cpu_get_cycle_count(); @@ -1470,23 +1504,22 @@ esp_err_t esp_light_sleep_start(void) #endif #if !CONFIG_FREERTOS_UNICORE -#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW - sleep_smp_cpu_sleep_prepare(); -#else -#if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX - // The other core will be stalled by high-priority interrupt and spins on variables in internal RAM, - // which naturally avoids cache livelock, so the 20ms livelock workaround timeout is not needed. - esp_int_wdt_livelock_workaround(false); -#endif - esp_ipc_isr_stall_other_cpu(); -#endif - esp_ipc_isr_stall_pause(); + if (sleep_smp_cpu_sleep_prepare() != ESP_OK) { + esp_os_exit_critical(&s_config.lock); + return ESP_ERR_SLEEP_REJECT; + } #endif + /* Another core is stalled and interrupts are disabled, so we can safely claim the thread-safe critical section to avoid deadlocks and fastup the sleep process. */ xPortThreadSafeClaim(); +#if CONFIG_ESP_TASK_WDT_USE_ESP_TIMER + /* If a task watchdog timer is running, we have to stop it. */ + esp_err_t timerret = esp_task_wdt_stop(); +#endif // CONFIG_ESP_TASK_WDT_USE_ESP_TIMER + #if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION && CONFIG_PM_SLP_IRAM_OPT /* Cache Suspend 0: if CONFIG_PM_SLP_IRAM_OPT is enabled, suspend cache here so that the access to flash * during the sleep process can be explicitly exposed. @@ -1704,18 +1737,8 @@ esp_err_t esp_light_sleep_start(void) /* Restore port critical before unstalling other CPU */ xPortThreadSafeDisclaim(); - esp_timer_private_unlock(); #if !CONFIG_FREERTOS_UNICORE - esp_ipc_isr_stall_resume(); -#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW sleep_smp_cpu_wakeup_prepare(); -#else - esp_ipc_isr_release_other_cpu(); -#if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX - // Configure WDT to use livelock workaround timeout after releasing other CPU - esp_int_wdt_livelock_workaround(true); -#endif -#endif #endif #if SOC_RTC_WDT_SUPPORTED From bbfde331f15bb745428e9077f5a22d82ea2aa3fe Mon Sep 17 00:00:00 2001 From: wuzhenghui Date: Wed, 17 Jun 2026 18:04:13 +0800 Subject: [PATCH 4/4] fix(esp_hw_support): fix s31 sleep CPU retention state atomic declaration --- components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu.c | 2 +- .../esp_hw_support/lowpower/port/esp32s31/sleep_cpu_dynamic.c | 2 +- .../esp_hw_support/lowpower/port/esp32s31/sleep_cpu_retention.h | 2 +- .../esp_hw_support/lowpower/port/esp32s31/sleep_cpu_static.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu.c b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu.c index be12e505cf2..1be5e037e9e 100644 --- a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu.c +++ b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu.c @@ -41,7 +41,7 @@ #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE -static DRAM_ATTR smp_retention_state_t s_smp_retention_state[portNUM_PROCESSORS]; +static DRAM_ATTR _Atomic(smp_retention_state_t) s_smp_retention_state[portNUM_PROCESSORS]; #endif static bool s_fpu_saved[portNUM_PROCESSORS]; diff --git a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_dynamic.c b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_dynamic.c index 87b4cfae52c..f72d769bc7b 100644 --- a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_dynamic.c +++ b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_dynamic.c @@ -52,7 +52,7 @@ static inline void * cpu_domain_clic_sleep_frame_alloc_and_init(uint8_t core_id) } #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE -esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr, smp_retention_state_t *s_smp_retention_state) +esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr, _Atomic(smp_retention_state_t) *s_smp_retention_state) { for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { if (sleep_cpu_retention_ptr->retent.critical_frame[core_id] == NULL) { diff --git a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_retention.h b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_retention.h index 55f340c2f9e..043fc30b42d 100644 --- a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_retention.h +++ b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_retention.h @@ -47,7 +47,7 @@ typedef struct { } sleep_cpu_retention_t; #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE - esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr, smp_retention_state_t *s_smp_retention_state); + esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr, _Atomic(smp_retention_state_t) *s_smp_retention_state); #else esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr); #endif diff --git a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_static.c b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_static.c index 317362fe6f9..d57db2661af 100644 --- a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_static.c +++ b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_static.c @@ -74,7 +74,7 @@ static inline void * cpu_domain_clic_sleep_frame_alloc_and_init(uint8_t core_id) } #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE -esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr, smp_retention_state_t *s_smp_retention_state) +esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr, _Atomic(smp_retention_state_t) *s_smp_retention_state) { static DRAM_ATTR uint8_t rv_core_critical_regs[RV_SLEEP_CTX_FRMSZ * portNUM_PROCESSORS] __attribute__((aligned(4))); static DRAM_ATTR uint8_t rv_core_non_critical_regs[sizeof(RvCoreNonCriticalSleepFrame)* portNUM_PROCESSORS] __attribute__((aligned(4)));