From 39cf3638ae69e55f5bceae85ced3f65ab05a548a Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Tue, 22 Aug 2023 22:32:44 +0800 Subject: [PATCH] change(freertos): Deprecate usage of vPortCleanUpTCB() by applications Previously, if CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP was enabled, users would provide a definition for a vPortCleanUpTCB() hook function that is called right before a task's memory is freed in prvDeleteTCB(). However, vPortCleanUpTCB() will be reclaimed by ESP-IDF for internal use in v6.0. This commit introduces the following changes... Introduced a new CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK option: - Provides the same pre-deletion hook functionality. But users now define vTaskPreDeletionHook() instead. - CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP still exists, but is marked as deprecated. This is to maintain compatibility with existing applications that already define vPortCleanUpTCB(). - Removed redundant --wl --wrap workaround with vPortCleanUpTCB() - Added todo notes to remove support for user defined vPortCleanUpTCB() completely in v6.0. - Updated test cases to use new CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK option Freed up portCLEAN_UP_TCB() to call a new internal vPortTCBPreDeleteHook(): - vPortTCBPreDeleteHook() now replaces the previous "wrapped" implementation of vPortCleanUpTCB(). - vPortTCBPreDeleteHook() is an internal task pre-delete hook for IDF FreeRTOS ports to inject some pre-deletion operations. - Internal pre-delete hook now invokes user provided vTaskPreDeletionHook() if enabled. - Relocated vPortTCBPreDeleteHook() to correct section in port.c --- components/freertos/CMakeLists.txt | 3 - .../riscv/include/freertos/portmacro.h | 17 +- .../FreeRTOS-Kernel-SMP/portable/riscv/port.c | 110 ++++++----- .../xtensa/include/freertos/portmacro.h | 19 +- .../portable/xtensa/port.c | 183 +++++++++--------- .../riscv/include/freertos/portmacro.h | 18 +- .../FreeRTOS-Kernel/portable/riscv/port.c | 19 ++ .../xtensa/include/freertos/portmacro.h | 21 +- .../FreeRTOS-Kernel/portable/xtensa/port.c | 23 ++- components/freertos/Kconfig | 22 ++- components/freertos/linker_smp.lf | 4 +- .../kernel/tasks/test_freertos_hooks.c | 8 +- .../freertos/port/test_tlsp_del_cb.c | 22 +-- .../freertos/sdkconfig.ci.freertos_options | 2 +- .../kernel_tests/port/test_tlsp_del_cb.c | 20 -- 15 files changed, 275 insertions(+), 216 deletions(-) diff --git a/components/freertos/CMakeLists.txt b/components/freertos/CMakeLists.txt index a37c39a346..2c93a4016b 100644 --- a/components/freertos/CMakeLists.txt +++ b/components/freertos/CMakeLists.txt @@ -250,7 +250,4 @@ else() idf_component_optional_requires(PRIVATE esp_pm) endif() - if(CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP AND CONFIG_FREERTOS_SMP) - target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=vPortCleanUpTCB") - endif() endif() 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 acae7f1b18..cb948cf4e8 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 @@ -143,9 +143,19 @@ void vPortYieldFromISR(void); static inline BaseType_t __attribute__((always_inline)) xPortGetCoreID( void ); -// ----------------------- TCB Cleanup -------------------------- +// --------------------- TCB Cleanup ----------------------- -void vPortCleanUpTCB ( void *pxTCB ); +/** + * @brief TCB cleanup hook + * + * The portCLEAN_UP_TCB() macro is called in prvDeleteTCB() right before a + * deleted task's memory is freed. We map that macro to this internal function + * so that IDF FreeRTOS ports can inject some task pre-deletion operations. + * + * @note We can't use vPortCleanUpTCB() due to API compatibility issues. See + * CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP. Todo: IDF-8097 + */ +void vPortTCBPreDeleteHook( void *pxTCB ); /* ------------------------------------------- FreeRTOS Porting Interface ---------------------------------------------- * - Contains all the mappings of the macros required by FreeRTOS @@ -218,7 +228,8 @@ extern void vTaskExitCritical( void ); // --------------------- TCB Cleanup ----------------------- -#define portCLEAN_UP_TCB( pxTCB ) vPortCleanUpTCB( pxTCB ) +#define portCLEAN_UP_TCB( pxTCB ) vPortTCBPreDeleteHook( pxTCB ) + /* --------------------------------------------- Inline Implementations ------------------------------------------------ * - Implementation of inline functions of the forward declares diff --git a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c index 055864c078..daefa24e65 100644 --- a/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c +++ b/components/freertos/FreeRTOS-Kernel-SMP/portable/riscv/port.c @@ -208,6 +208,63 @@ void vPortYieldFromISR( void ) uxSchedulerRunning = 1; xPortSwitchFlag = 1; } +// ----------------------- System -------------------------- + +// ------------------- Run Time Stats ---------------------- + +// --------------------- TCB Cleanup ----------------------- + +#if ( CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS ) +static void vPortTLSPointersDelCb( void *pxTCB ) +{ + /* Typecast pxTCB to StaticTask_t type to access TCB struct members. + * pvDummy15 corresponds to pvThreadLocalStoragePointers member of the TCB. + */ + StaticTask_t *tcb = ( StaticTask_t * )pxTCB; + + /* The TLSP deletion callbacks are stored at an offset of (configNUM_THREAD_LOCAL_STORAGE_POINTERS/2) */ + TlsDeleteCallbackFunction_t *pvThreadLocalStoragePointersDelCallback = ( TlsDeleteCallbackFunction_t * )( &( tcb->pvDummy15[ ( configNUM_THREAD_LOCAL_STORAGE_POINTERS / 2 ) ] ) ); + + /* We need to iterate over half the depth of the pvThreadLocalStoragePointers area + * to access all TLS pointers and their respective TLS deletion callbacks. + */ + for ( int x = 0; x < ( configNUM_THREAD_LOCAL_STORAGE_POINTERS / 2 ); x++ ) { + if ( pvThreadLocalStoragePointersDelCallback[ x ] != NULL ) { //If del cb is set + /* In case the TLSP deletion callback has been overwritten by a TLS pointer, gracefully abort. */ + if ( !esp_ptr_executable( pvThreadLocalStoragePointersDelCallback[ x ] ) ) { + ESP_LOGE("FreeRTOS", "Fatal error: TLSP deletion callback at index %d overwritten with non-excutable pointer %p", x, pvThreadLocalStoragePointersDelCallback[ x ]); + abort(); + } + + pvThreadLocalStoragePointersDelCallback[ x ]( x, tcb->pvDummy15[ x ] ); //Call del cb + } + } +} +#endif /* CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS */ + +void vPortTCBPreDeleteHook( void *pxTCB ) +{ + #if ( CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK ) + /* Call the user defined task pre-deletion hook */ + extern void vTaskPreDeletionHook( void * pxTCB ); + vTaskPreDeletionHook( pxTCB ); + #endif /* CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK */ + + #if ( CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP ) + /* + * If the user is using the legacy task pre-deletion hook, call it. + * Todo: Will be removed in IDF-8097 + */ + #warning "CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP is deprecated. Use CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK instead." + extern void vPortCleanUpTCB( void * pxTCB ); + vPortCleanUpTCB( pxTCB ); + #endif /* CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP */ + + #if ( CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS ) + /* Call TLS pointers deletion callbacks */ + vPortTLSPointersDelCb( pxTCB ); + #endif /* CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS */ +} /* ------------------------------------------------ FreeRTOS Portable -------------------------------------------------- * - Provides implementation for functions required by FreeRTOS @@ -423,36 +480,6 @@ StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxC //TODO: IDF-2393 } -// ------- Thread Local Storage Pointers Deletion Callbacks ------- - -#if ( CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS ) -void vPortTLSPointersDelCb( void *pxTCB ) -{ - /* Typecast pxTCB to StaticTask_t type to access TCB struct members. - * pvDummy15 corresponds to pvThreadLocalStoragePointers member of the TCB. - */ - StaticTask_t *tcb = ( StaticTask_t * )pxTCB; - - /* The TLSP deletion callbacks are stored at an offset of (configNUM_THREAD_LOCAL_STORAGE_POINTERS/2) */ - TlsDeleteCallbackFunction_t *pvThreadLocalStoragePointersDelCallback = ( TlsDeleteCallbackFunction_t * )( &( tcb->pvDummy15[ ( configNUM_THREAD_LOCAL_STORAGE_POINTERS / 2 ) ] ) ); - - /* We need to iterate over half the depth of the pvThreadLocalStoragePointers area - * to access all TLS pointers and their respective TLS deletion callbacks. - */ - for ( int x = 0; x < ( configNUM_THREAD_LOCAL_STORAGE_POINTERS / 2 ); x++ ) { - if ( pvThreadLocalStoragePointersDelCallback[ x ] != NULL ) { //If del cb is set - /* In case the TLSP deletion callback has been overwritten by a TLS pointer, gracefully abort. */ - if ( !esp_ptr_executable( pvThreadLocalStoragePointersDelCallback[ x ] ) ) { - ESP_LOGE("FreeRTOS", "Fatal error: TLSP deletion callback at index %d overwritten with non-excutable pointer %p", x, pvThreadLocalStoragePointersDelCallback[ x ]); - abort(); - } - - pvThreadLocalStoragePointersDelCallback[ x ]( x, tcb->pvDummy15[ x ] ); //Call del cb - } - } -} -#endif // CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS - // ------------------- Hook Functions ---------------------- void __attribute__((weak)) vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) @@ -496,26 +523,3 @@ void vApplicationMinimalIdleHook( void ) esp_vApplicationIdleHook(); //Run IDF style hooks } #endif // CONFIG_FREERTOS_USE_MINIMAL_IDLE_HOOK - -/* - * Hook function called during prvDeleteTCB() to cleanup any - * user defined static memory areas in the TCB. - */ -#if CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP -void __real_vPortCleanUpTCB( void *pxTCB ); - -void __wrap_vPortCleanUpTCB( void *pxTCB ) -#else -void vPortCleanUpTCB ( void *pxTCB ) -#endif /* CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP */ -{ -#if ( CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP ) - /* Call user defined vPortCleanUpTCB */ - __real_vPortCleanUpTCB( pxTCB ); -#endif /* CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP */ - -#if ( CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS ) - /* Call TLS pointers deletion callbacks */ - vPortTLSPointersDelCb( pxTCB ); -#endif /* CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS */ -} 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 4ff15d932d..26c6841036 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 @@ -127,9 +127,20 @@ static inline void __attribute__((always_inline)) vPortYieldFromISR( void ); static inline BaseType_t __attribute__((always_inline)) xPortGetCoreID( void ); -// ----------------------- TCB Cleanup -------------------------- +// --------------------- TCB Cleanup ----------------------- + +/** + * @brief TCB cleanup hook + * + * The portCLEAN_UP_TCB() macro is called in prvDeleteTCB() right before a + * deleted task's memory is freed. We map that macro to this internal function + * so that IDF FreeRTOS ports can inject some task pre-deletion operations. + * + * @note We can't use vPortCleanUpTCB() due to API compatibility issues. See + * CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP. Todo: IDF-8097 + */ +void vPortTCBPreDeleteHook( void *pxTCB ); -void vPortCleanUpTCB ( void *pxTCB ); /* ----------------------------------------- FreeRTOS SMP Porting Interface -------------------------------------------- * - Contains all the mappings of the macros required by FreeRTOS SMP @@ -221,9 +232,9 @@ extern void vTaskExitCritical( void ); #define portALT_GET_RUN_TIME_COUNTER_VALUE(x) ({x = (uint32_t)esp_timer_get_time();}) #endif -// ------------------- TCB Cleanup ---------------------- +// --------------------- TCB Cleanup ----------------------- -#define portCLEAN_UP_TCB( pxTCB ) vPortCleanUpTCB( pxTCB ) +#define portCLEAN_UP_TCB( pxTCB ) vPortTCBPreDeleteHook( pxTCB ) /* --------------------------------------------- Inline Implementations ------------------------------------------------ * - Implementation of inline functions of the forward declares diff --git a/components/freertos/FreeRTOS-Kernel-SMP/portable/xtensa/port.c b/components/freertos/FreeRTOS-Kernel-SMP/portable/xtensa/port.c index 2f39720aca..ad5fa3dbd9 100644 --- a/components/freertos/FreeRTOS-Kernel-SMP/portable/xtensa/port.c +++ b/components/freertos/FreeRTOS-Kernel-SMP/portable/xtensa/port.c @@ -42,6 +42,14 @@ _Static_assert(portBYTE_ALIGNMENT == 16, "portBYTE_ALIGNMENT must be set to 16"); +/** + * @brief Align stack pointer in a downward growing stack + * + * This macro is used to round a stack pointer downwards to the nearest n-byte boundary, where n is a power of 2. + * This macro is generally used when allocating aligned areas on a downward growing stack. + */ +#define STACKPTR_ALIGN_DOWN(n, ptr) ((ptr) & (~((n)-1))) + /* ---------------------------------------------------- Variables ------------------------------------------------------ * - Various variables used to maintain the FreeRTOS port's state. Used from both port.c and various .S files * - Constant offsets are used by assembly to jump to particular TCB members or a stack area (such as the CPSA). We use @@ -209,6 +217,90 @@ void vPortReleaseLock( portMUX_TYPE *lock ) // ----------------------- System -------------------------- +// ------------------- Run Time Stats ---------------------- + +// --------------------- TCB Cleanup ----------------------- + +#if ( CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS ) +static void vPortTLSPointersDelCb( void *pxTCB ) +{ + /* Typecast pxTCB to StaticTask_t type to access TCB struct members. + * pvDummy15 corresponds to pvThreadLocalStoragePointers member of the TCB. + */ + StaticTask_t *tcb = ( StaticTask_t * )pxTCB; + + /* The TLSP deletion callbacks are stored at an offset of (configNUM_THREAD_LOCAL_STORAGE_POINTERS/2) */ + TlsDeleteCallbackFunction_t *pvThreadLocalStoragePointersDelCallback = ( TlsDeleteCallbackFunction_t * )( &( tcb->pvDummy15[ ( configNUM_THREAD_LOCAL_STORAGE_POINTERS / 2 ) ] ) ); + + /* We need to iterate over half the depth of the pvThreadLocalStoragePointers area + * to access all TLS pointers and their respective TLS deletion callbacks. + */ + for ( int x = 0; x < ( configNUM_THREAD_LOCAL_STORAGE_POINTERS / 2 ); x++ ) { + if ( pvThreadLocalStoragePointersDelCallback[ x ] != NULL ) { //If del cb is set + /* In case the TLSP deletion callback has been overwritten by a TLS pointer, gracefully abort. */ + if ( !esp_ptr_executable( pvThreadLocalStoragePointersDelCallback[ x ] ) ) { + // We call EARLY log here as currently portCLEAN_UP_TCB() is called in a critical section + ESP_EARLY_LOGE("FreeRTOS", "Fatal error: TLSP deletion callback at index %d overwritten with non-excutable pointer %p", x, pvThreadLocalStoragePointersDelCallback[ x ]); + abort(); + } + + pvThreadLocalStoragePointersDelCallback[ x ]( x, tcb->pvDummy15[ x ] ); //Call del cb + } + } +} +#endif /* CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS */ + +#if ( XCHAL_CP_NUM > 0 && configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1 ) +static void vPortCleanUpCoprocArea( void *pxTCB ) +{ + UBaseType_t uxCoprocArea; + BaseType_t xTargetCoreID; + + /* Get pointer to the task's coprocessor save area from TCB->pxEndOfStack. See uxInitialiseStackCPSA() */ + uxCoprocArea = ( UBaseType_t ) ( ( ( StaticTask_t * ) pxTCB )->pxDummy8 ); /* Get TCB_t.pxEndOfStack */ + uxCoprocArea = STACKPTR_ALIGN_DOWN(16, uxCoprocArea - XT_CP_SIZE); + + /* Extract core ID from the affinity mask */ + xTargetCoreID = ( ( StaticTask_t * ) pxTCB )->uxDummy25 ; + xTargetCoreID = ( BaseType_t ) __builtin_ffs( ( int ) xTargetCoreID ); + assert( xTargetCoreID >= 1 ); // __builtin_ffs always returns first set index + 1 + xTargetCoreID -= 1; + + /* If task has live floating point registers somewhere, release them */ + void _xt_coproc_release(volatile void *coproc_sa_base, BaseType_t xTargetCoreID); + _xt_coproc_release( (void *)uxCoprocArea, xTargetCoreID ); +} +#endif /* ( XCHAL_CP_NUM > 0 && configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1 ) */ + +void vPortTCBPreDeleteHook( void *pxTCB ) +{ + #if ( CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK ) + /* Call the user defined task pre-deletion hook */ + extern void vTaskPreDeletionHook( void * pxTCB ); + vTaskPreDeletionHook( pxTCB ); + #endif /* CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK */ + + #if ( CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP ) + /* + * If the user is using the legacy task pre-deletion hook, call it. + * Todo: Will be removed in IDF-8097 + */ + #warning "CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP is deprecated. Use CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK instead." + extern void vPortCleanUpTCB( void * pxTCB ); + vPortCleanUpTCB( pxTCB ); + #endif /* CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP */ + + #if ( CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS ) + /* Call TLS pointers deletion callbacks */ + vPortTLSPointersDelCb( pxTCB ); + #endif /* CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS */ + + #if ( XCHAL_CP_NUM > 0 && configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1 ) + /* Cleanup coproc save area */ + vPortCleanUpCoprocArea( pxTCB ); + #endif /* ( XCHAL_CP_NUM > 0 && configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1 ) */ +} + /* ------------------------------------------------ FreeRTOS Portable -------------------------------------------------- * - Provides implementation for functions required by FreeRTOS * - Declared in portable.h @@ -269,14 +361,6 @@ static void vPortTaskWrapper(TaskFunction_t pxCode, void *pvParameters) } #endif -/** - * @brief Align stack pointer in a downward growing stack - * - * This macro is used to round a stack pointer downwards to the nearest n-byte boundary, where n is a power of 2. - * This macro is generally used when allocating aligned areas on a downward growing stack. - */ -#define STACKPTR_ALIGN_DOWN(n, ptr) ((ptr) & (~((n)-1))) - #if XCHAL_CP_NUM > 0 /** * @brief Allocate and initialize coprocessor save area on the stack @@ -559,61 +643,6 @@ StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack, // Return the task's current stack pointer address which should point to the starting interrupt stack frame return (StackType_t *)uxStackPointer; } -// -------------------- Co-Processor ----------------------- -#if ( XCHAL_CP_NUM > 0 && configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1 ) - -void _xt_coproc_release(volatile void *coproc_sa_base, BaseType_t xTargetCoreID); - -void vPortCleanUpCoprocArea( void *pxTCB ) -{ - UBaseType_t uxCoprocArea; - BaseType_t xTargetCoreID; - - /* Get pointer to the task's coprocessor save area from TCB->pxEndOfStack. See uxInitialiseStackCPSA() */ - uxCoprocArea = ( UBaseType_t ) ( ( ( StaticTask_t * ) pxTCB )->pxDummy8 ); /* Get TCB_t.pxEndOfStack */ - uxCoprocArea = STACKPTR_ALIGN_DOWN(16, uxCoprocArea - XT_CP_SIZE); - - /* Extract core ID from the affinity mask */ - xTargetCoreID = ( ( StaticTask_t * ) pxTCB )->uxDummy25 ; - xTargetCoreID = ( BaseType_t ) __builtin_ffs( ( int ) xTargetCoreID ); - assert( xTargetCoreID >= 1 ); // __builtin_ffs always returns first set index + 1 - xTargetCoreID -= 1; - - /* If task has live floating point registers somewhere, release them */ - _xt_coproc_release( (void *)uxCoprocArea, xTargetCoreID ); -} -#endif // ( XCHAL_CP_NUM > 0 && configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1 ) - -// ------- Thread Local Storage Pointers Deletion Callbacks ------- - -#if ( CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS ) -void vPortTLSPointersDelCb( void *pxTCB ) -{ - /* Typecast pxTCB to StaticTask_t type to access TCB struct members. - * pvDummy15 corresponds to pvThreadLocalStoragePointers member of the TCB. - */ - StaticTask_t *tcb = ( StaticTask_t * )pxTCB; - - /* The TLSP deletion callbacks are stored at an offset of (configNUM_THREAD_LOCAL_STORAGE_POINTERS/2) */ - TlsDeleteCallbackFunction_t *pvThreadLocalStoragePointersDelCallback = ( TlsDeleteCallbackFunction_t * )( &( tcb->pvDummy15[ ( configNUM_THREAD_LOCAL_STORAGE_POINTERS / 2 ) ] ) ); - - /* We need to iterate over half the depth of the pvThreadLocalStoragePointers area - * to access all TLS pointers and their respective TLS deletion callbacks. - */ - for ( int x = 0; x < ( configNUM_THREAD_LOCAL_STORAGE_POINTERS / 2 ); x++ ) { - if ( pvThreadLocalStoragePointersDelCallback[ x ] != NULL ) { //If del cb is set - /* In case the TLSP deletion callback has been overwritten by a TLS pointer, gracefully abort. */ - if ( !esp_ptr_executable( pvThreadLocalStoragePointersDelCallback[ x ] ) ) { - // We call EARLY log here as currently portCLEAN_UP_TCB() is called in a critical section - ESP_EARLY_LOGE("FreeRTOS", "Fatal error: TLSP deletion callback at index %d overwritten with non-excutable pointer %p", x, pvThreadLocalStoragePointersDelCallback[ x ]); - abort(); - } - - pvThreadLocalStoragePointersDelCallback[ x ]( x, tcb->pvDummy15[ x ] ); //Call del cb - } - } -} -#endif // CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS // ------------------- Hook Functions ---------------------- @@ -655,31 +684,3 @@ void vApplicationMinimalIdleHook( void ) esp_vApplicationIdleHook(); //Run IDF style hooks } #endif // CONFIG_FREERTOS_USE_MINIMAL_IDLE_HOOK - -/* - * Hook function called during prvDeleteTCB() to cleanup any - * user defined static memory areas in the TCB. - */ -#if CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP -void __real_vPortCleanUpTCB( void *pxTCB ); - -void __wrap_vPortCleanUpTCB( void *pxTCB ) -#else -void vPortCleanUpTCB ( void *pxTCB ) -#endif /* CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP */ -{ -#if ( CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP ) - /* Call user defined vPortCleanUpTCB */ - __real_vPortCleanUpTCB( pxTCB ); -#endif /* CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP */ - -#if ( CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS ) - /* Call TLS pointers deletion callbacks */ - vPortTLSPointersDelCb( pxTCB ); -#endif /* CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS */ - -#if ( XCHAL_CP_NUM > 0 && configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1 ) - /* Cleanup coproc save area */ - vPortCleanUpCoprocArea( pxTCB ); -#endif // ( XCHAL_CP_NUM > 0 && configUSE_CORE_AFFINITY == 1 && configNUM_CORES > 1 ) -} 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 c60afe000f..0e1aef741e 100644 --- a/components/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos/portmacro.h +++ b/components/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos/portmacro.h @@ -427,7 +427,19 @@ FORCE_INLINE_ATTR BaseType_t xPortGetCoreID(void) return (BaseType_t) esp_cpu_get_core_id(); } +// --------------------- TCB Cleanup ----------------------- +/** + * @brief TCB cleanup hook + * + * The portCLEAN_UP_TCB() macro is called in prvDeleteTCB() right before a + * deleted task's memory is freed. We map that macro to this internal function + * so that IDF FreeRTOS ports can inject some task pre-deletion operations. + * + * @note We can't use vPortCleanUpTCB() due to API compatibility issues. See + * CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP. Todo: IDF-8097 + */ +void vPortTCBPreDeleteHook( void *pxTCB ); /* ------------------------------------------- FreeRTOS Porting Interface ---------------------------------------------- * - Contains all the mappings of the macros required by FreeRTOS @@ -561,11 +573,7 @@ FORCE_INLINE_ATTR BaseType_t xPortGetCoreID(void) // --------------------- TCB Cleanup ----------------------- -#if CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP -/* If enabled, users must provide an implementation of vPortCleanUpTCB() */ -extern void vPortCleanUpTCB ( void *pxTCB ); -#define portCLEAN_UP_TCB( pxTCB ) vPortCleanUpTCB( pxTCB ) -#endif /* CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP */ +#define portCLEAN_UP_TCB( pxTCB ) vPortTCBPreDeleteHook( pxTCB ) // -------------- Optimized Task Selection ----------------- diff --git a/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c b/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c index 0c7a30c07e..fda5f44e0d 100644 --- a/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c +++ b/components/freertos/FreeRTOS-Kernel/portable/riscv/port.c @@ -589,7 +589,26 @@ void vPortSetStackWatchpoint(void *pxStackStart) esp_cpu_set_watchpoint(STACK_WATCH_POINT_NUMBER, (char *)addr, STACK_WATCH_AREA_SIZE, ESP_CPU_WATCHPOINT_STORE); } +// --------------------- TCB Cleanup ----------------------- +void vPortTCBPreDeleteHook( void *pxTCB ) +{ + #if ( CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK ) + /* Call the user defined task pre-deletion hook */ + extern void vTaskPreDeletionHook( void * pxTCB ); + vTaskPreDeletionHook( pxTCB ); + #endif /* CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK */ + + #if ( CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP ) + /* + * If the user is using the legacy task pre-deletion hook, call it. + * Todo: Will be removed in IDF-8097 + */ + #warning "CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP is deprecated. Use CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK instead." + extern void vPortCleanUpTCB( void * pxTCB ); + vPortCleanUpTCB( pxTCB ); + #endif /* CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP */ +} /* ---------------------------------------------- Misc Implementations ------------------------------------------------- * 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 660a508467..5a19389fcf 100644 --- a/components/freertos/FreeRTOS-Kernel/portable/xtensa/include/freertos/portmacro.h +++ b/components/freertos/FreeRTOS-Kernel/portable/xtensa/include/freertos/portmacro.h @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT * - * SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileContributor: 2016-2023 Espressif Systems (Shanghai) CO LTD */ /* * FreeRTOS Kernel V10.4.3 @@ -406,6 +406,19 @@ void vPortSetStackWatchpoint( void *pxStackStart ); */ FORCE_INLINE_ATTR BaseType_t xPortGetCoreID(void); +// --------------------- TCB Cleanup ----------------------- + +/** + * @brief TCB cleanup hook + * + * The portCLEAN_UP_TCB() macro is called in prvDeleteTCB() right before a + * deleted task's memory is freed. We map that macro to this internal function + * so that IDF FreeRTOS ports can inject some task pre-deletion operations. + * + * @note We can't use vPortCleanUpTCB() due to API compatibility issues. See + * CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP. Todo: IDF-8097 + */ +void vPortTCBPreDeleteHook( void *pxTCB ); /* ------------------------------------------- FreeRTOS Porting Interface ---------------------------------------------- @@ -525,11 +538,7 @@ extern void _frxt_setup_switch( void ); //Defined in portasm.S // --------------------- TCB Cleanup ----------------------- -#if CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP -/* If enabled, users must provide an implementation of vPortCleanUpTCB() */ -extern void vPortCleanUpTCB ( void *pxTCB ); -#define portCLEAN_UP_TCB( pxTCB ) vPortCleanUpTCB( pxTCB ) -#endif /* CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP */ +#define portCLEAN_UP_TCB( pxTCB ) vPortTCBPreDeleteHook( pxTCB ) // -------------- Optimized Task Selection ----------------- diff --git a/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c b/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c index 7304ceaebd..ab4120e267 100644 --- a/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c +++ b/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT * - * SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileContributor: 2016-2023 Espressif Systems (Shanghai) CO LTD */ /* * FreeRTOS Kernel V10.4.3 @@ -605,6 +605,27 @@ void vPortSetStackWatchpoint( void *pxStackStart ) esp_cpu_set_watchpoint(STACK_WATCH_POINT_NUMBER, (char *)addr, 32, ESP_CPU_WATCHPOINT_STORE); } +// --------------------- TCB Cleanup ----------------------- + +void vPortTCBPreDeleteHook( void *pxTCB ) +{ + #if ( CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK ) + /* Call the user defined task pre-deletion hook */ + extern void vTaskPreDeletionHook( void * pxTCB ); + vTaskPreDeletionHook( pxTCB ); + #endif /* CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK */ + + #if ( CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP ) + /* + * If the user is using the legacy task pre-deletion hook, call it. + * Todo: Will be removed in IDF-8097 + */ + #warning "CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP is deprecated. Use CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK instead." + extern void vPortCleanUpTCB( void * pxTCB ); + vPortCleanUpTCB( pxTCB ); + #endif /* CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP */ +} + // -------------------- Co-Processor ----------------------- #if XCHAL_CP_NUM > 0 diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index 5fcc598b43..4bd2c87935 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -324,10 +324,28 @@ menu "FreeRTOS" callbacks. If the user does not wish to use TLSP deletion callbacks then this option could be turned off to save space in the TCB memory. - config FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP - bool "Enable static task clean up hook" + config FREERTOS_TASK_PRE_DELETION_HOOK + # This option is a replacement for FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP (which is now deprecated). If the + # deprecated option is defined, we hide this option to avoid multiple pre-deletion hooks from running. + bool "Enable task pre-deletion hook" + depends on !FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP default n help + Enable this option to make FreeRTOS call a user provided hook function right before it deletes a task + (i.e., frees/releases a dynamically/statically allocated task's memory). This is useful if users want + to know when a task is actually deleted (in case the task's deletion is delegated to the IDLE task). + + If this config option is enabled, users must define a ``void vTaskPreDeletionHook( void * pxTCB )`` + hook function in their application. + + config FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP + # This option is deprecated (replaced by FREERTOS_TASK_PRE_DELETION_HOOK) but still exists to maintain + # compatibility. Todo: Remove by v6.0 (see IDF-8097). + bool "Enable static task clean up hook (DEPRECATED)" + default n + help + THIS OPTION IS DEPRECATED. Use FREERTOS_TASK_PRE_DELETION_HOOK instead. + Enable this option to make FreeRTOS call the static task clean up hook when a task is deleted. Note: Users will need to provide a ``void vPortCleanUpTCB ( void *pxTCB )`` callback diff --git a/components/freertos/linker_smp.lf b/components/freertos/linker_smp.lf index 3ee3613314..131e8530cb 100644 --- a/components/freertos/linker_smp.lf +++ b/components/freertos/linker_smp.lf @@ -219,7 +219,7 @@ entries: port:vPortCleanUpCoprocArea (default) if FREERTOS_TLSP_DELETION_CALLBACKS = y: port:vPortTLSPointersDelCb (default) - port:vPortCleanUpTCB (default) + port:vPortTCBPreDeleteHook (default) # -------------------------------------------------------------------------------------------------------------- # portable/riscv/port.c # - Most functions are called from an ISR context, except for scheduler/task init/deinit functions @@ -234,4 +234,4 @@ entries: port:pxPortInitialiseStack (default) if FREERTOS_TLSP_DELETION_CALLBACKS = y: port:vPortTLSPointersDelCb (default) - port:vPortCleanUpTCB (default) + port:vPortTCBPreDeleteHook (default) diff --git a/components/freertos/test_apps/freertos/kernel/tasks/test_freertos_hooks.c b/components/freertos/test_apps/freertos/kernel/tasks/test_freertos_hooks.c index e9b8a973b5..893647c2d1 100644 --- a/components/freertos/test_apps/freertos/kernel/tasks/test_freertos_hooks.c +++ b/components/freertos/test_apps/freertos/kernel/tasks/test_freertos_hooks.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -65,7 +65,7 @@ TEST_CASE("FreeRTOS tick hook", "[freertos]") #endif // configUSE_TICK_HOOK #endif // CONFIG_FREERTOS_SMP -#if CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP +#if CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK static volatile void *deleted_tcb; @@ -74,7 +74,7 @@ static void taskDeletesItself(void *ignored) vTaskDelete(NULL); } -void vPortCleanUpTCB(void *pxTCB) +void vTaskPreDeletionHook(void *pxTCB) { deleted_tcb = pxTCB; } @@ -91,4 +91,4 @@ TEST_CASE("static task cleanup hook is called based on config", "[freertos]") } } -#endif // CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP +#endif // CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK diff --git a/components/freertos/test_apps/freertos/port/test_tlsp_del_cb.c b/components/freertos/test_apps/freertos/port/test_tlsp_del_cb.c index 7a858e844a..4c3082fbd9 100644 --- a/components/freertos/test_apps/freertos/port/test_tlsp_del_cb.c +++ b/components/freertos/test_apps/freertos/port/test_tlsp_del_cb.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -105,15 +105,6 @@ TEST_CASE("Test TLSP deletion callbacks", "[freertos]") //The variables pointed to by Thread Local Storage Pointer static uint32_t task_storage[portNUM_PROCESSORS][NO_OF_TLSP] = {0}; -/* If static task cleanup is defined, can't set index 0 even if the calling task is not a pthread, - as the cleanup is called for every task. -*/ -#if defined(CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP) -static const int skip_index = 0; /*PTHREAD_TLS_INDEX*/ -#else -static const int skip_index = -1; -#endif - static void del_cb(int index, void *ptr) { *((uint32_t *)ptr) = (TLSP_DEL_BASE << index); //Indicate deletion by setting task storage element to a unique value @@ -123,19 +114,11 @@ static void task_cb(void *arg) { int core = xPortGetCoreID(); for(int i = 0; i < NO_OF_TLSP; i++){ - if (i == skip_index) { - continue; - } - task_storage[core][i] = (TLSP_SET_BASE << i); //Give each element of task_storage a unique number vTaskSetThreadLocalStoragePointerAndDelCallback(NULL, i, (void *)&task_storage[core][i], del_cb); //Set each TLSP to point to a task storage element } for(int i = 0; i < NO_OF_TLSP; i++){ - if (i == skip_index) { - continue; - } - uint32_t * tlsp = (uint32_t *)pvTaskGetThreadLocalStoragePointer(NULL, i); TEST_ASSERT_EQUAL(*tlsp, (TLSP_SET_BASE << i)); //Check if TLSP points to the correct task storage element by checking unique value } @@ -153,9 +136,6 @@ TEST_CASE("Test FreeRTOS thread local storage pointers and del cb", "[freertos]" for(int core = 0; core < portNUM_PROCESSORS; core++){ for(int i = 0; i < NO_OF_TLSP; i++){ - if (i == skip_index) { - continue; - } TEST_ASSERT_EQUAL((TLSP_DEL_BASE << i), task_storage[core][i]); //Check del_cb ran by checking task storage for unique value } } diff --git a/components/freertos/test_apps/freertos/sdkconfig.ci.freertos_options b/components/freertos/test_apps/freertos/sdkconfig.ci.freertos_options index 6b278c939a..541b767a2a 100644 --- a/components/freertos/test_apps/freertos/sdkconfig.ci.freertos_options +++ b/components/freertos/test_apps/freertos/sdkconfig.ci.freertos_options @@ -7,7 +7,7 @@ CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL=y CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y CONFIG_FREERTOS_INTERRUPT_BACKTRACE=n CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y -CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP=y +CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK=y CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=10 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y diff --git a/tools/test_apps/linux_compatible/linux_freertos/components/kernel_tests/port/test_tlsp_del_cb.c b/tools/test_apps/linux_compatible/linux_freertos/components/kernel_tests/port/test_tlsp_del_cb.c index c8fae1a854..289b86df44 100644 --- a/tools/test_apps/linux_compatible/linux_freertos/components/kernel_tests/port/test_tlsp_del_cb.c +++ b/tools/test_apps/linux_compatible/linux_freertos/components/kernel_tests/port/test_tlsp_del_cb.c @@ -103,15 +103,6 @@ TEST_CASE("Test TLSP deletion callbacks", "[freertos]") //The variables pointed to by Thread Local Storage Pointer static uint32_t task_storage[portNUM_PROCESSORS][NO_OF_TLSP] = {0}; -/* If static task cleanup is defined, can't set index 0 even if the calling task is not a pthread, - as the cleanup is called for every task. -*/ -#if defined(CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP) -static const int skip_index = 0; /*PTHREAD_TLS_INDEX*/ -#else -static const int skip_index = -1; -#endif - static void del_cb(int index, void *ptr) { *((uint32_t *)ptr) = (TLSP_DEL_BASE << index); //Indicate deletion by setting task storage element to a unique value @@ -121,19 +112,11 @@ static void task_cb(void *arg) { int core = xPortGetCoreID(); for(int i = 0; i < NO_OF_TLSP; i++){ - if (i == skip_index) { - continue; - } - task_storage[core][i] = (TLSP_SET_BASE << i); //Give each element of task_storage a unique number vTaskSetThreadLocalStoragePointerAndDelCallback(NULL, i, (void *)&task_storage[core][i], del_cb); //Set each TLSP to point to a task storage element } for(int i = 0; i < NO_OF_TLSP; i++){ - if (i == skip_index) { - continue; - } - uint32_t * tlsp = (uint32_t *)pvTaskGetThreadLocalStoragePointer(NULL, i); TEST_ASSERT_EQUAL(*tlsp, (TLSP_SET_BASE << i)); //Check if TLSP points to the correct task storage element by checking unique value } @@ -151,9 +134,6 @@ TEST_CASE("Test FreeRTOS thread local storage pointers and del cb", "[freertos]" for(int core = 0; core < portNUM_PROCESSORS; core++){ for(int i = 0; i < NO_OF_TLSP; i++){ - if (i == skip_index) { - continue; - } TEST_ASSERT_EQUAL((TLSP_DEL_BASE << i), task_storage[core][i]); //Check del_cb ran by checking task storage for unique value } }