diff --git a/components/freertos/esp_additions/include/freertos/idf_additions.h b/components/freertos/esp_additions/include/freertos/idf_additions.h index 038deb950d..6523575c8d 100644 --- a/components/freertos/esp_additions/include/freertos/idf_additions.h +++ b/components/freertos/esp_additions/include/freertos/idf_additions.h @@ -7,6 +7,7 @@ #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "idf_additions_inc.h" #if CONFIG_FREERTOS_SMP || __DOXYGEN__ @@ -141,3 +142,50 @@ BaseType_t xTaskGetAffinity( TaskHandle_t xTask ); #endif // CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS #endif // CONFIG_FREERTOS_SMP || __DOXYGEN__ + +#if ( INCLUDE_vTaskPrioritySet == 1 ) + +/** + * INCLUDE_vTaskPrioritySet must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Saves the current priority and current base priority of a task, then raises the tasks + * current and base priority to uxNewPriority if uxNewPriority is of a higher priority. + * Once a task's priority has been raised with this function, the priority can be restored + * by calling prvTaskPriorityRestore() + * - Note that this function differs from vTaskPrioritySet() as the task's current priority + * will be modified even if the task has already inherited a priority. + * - This function is intended for special circumstance where a task must be forced immediately + * to a higher priority. + * + * For configUSE_MUTEXES == 0: A context switch will occur before the function returns if the priority + * being set is higher than the currently executing task. + * + * @note This functions is private is only be called internally within various IDF components. + * Users should never call this function from their application. + * + * @note vTaskPrioritySet() should not be called while a task's priority is already raised via this function + * + * @param pxSavedPriority returns base and current priorities + * + * @param uxNewPriority The priority to which the task will be set. + */ +void prvTaskPriorityRaise( prvTaskSavedPriority_t * pxSavedPriority, UBaseType_t uxNewPriority ); + +/** + * INCLUDE_vTaskPrioritySet must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Restore a task's priority that was previously raised by prvTaskPriorityRaise(). + * + * For configUSE_MUTEXES == 0: A context switch will occur before the function returns if the priority + * being set is higher than the currently executing task. + * + * @note This functions is private is only be called internally within various IDF components. + * Users should never call this function from their application. + * + * @param pxSavedPriority previously saved base and current priorities that need to be restored + */ +void prvTaskPriorityRestore( prvTaskSavedPriority_t * pxSavedPriority ); + +#endif // ( INCLUDE_vTaskPrioritySet == 1) diff --git a/components/freertos/esp_additions/include/freertos/idf_additions_inc.h b/components/freertos/esp_additions/include/freertos/idf_additions_inc.h new file mode 100644 index 0000000000..25b0b6d9a4 --- /dev/null +++ b/components/freertos/esp_additions/include/freertos/idf_additions_inc.h @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#ifndef FREERTOS_ADDITITIONS_INC_H_ +#define FREERTOS_ADDITITIONS_INC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" + +#if ( INCLUDE_vTaskPrioritySet == 1 ) + +typedef struct { + UBaseType_t uxPriority; +#if ( configUSE_MUTEXES == 1 ) + UBaseType_t uxBasePriority; +#endif +} prvTaskSavedPriority_t; + +#endif // ( INCLUDE_vTaskPrioritySet == 1) + +#ifdef __cplusplus +} +#endif + +#endif //FREERTOS_ADDITITIONS_INC_H_ diff --git a/components/freertos/esp_additions/private_include/freertos_tasks_c_additions.h b/components/freertos/esp_additions/private_include/freertos_tasks_c_additions.h index f5ecdbb5a9..bdcc082276 100644 --- a/components/freertos/esp_additions/private_include/freertos_tasks_c_additions.h +++ b/components/freertos/esp_additions/private_include/freertos_tasks_c_additions.h @@ -7,6 +7,7 @@ #pragma once #include "sdkconfig.h" +#include "idf_additions_inc.h" /** * This file will be included in `tasks.c` file, thus, it must NOT be included @@ -393,3 +394,188 @@ void vTaskSetThreadLocalStoragePointerAndDelCallback( TaskHandle_t xTaskToSet, B #endif // CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS #endif // CONFIG_FREERTOS_SMP + +#if ( INCLUDE_vTaskPrioritySet == 1 ) + +void prvTaskPriorityRaise( prvTaskSavedPriority_t * pxSavedPriority, UBaseType_t uxNewPriority ) +{ + TCB_t * pxTCB; + UBaseType_t uxPriorityUsedOnEntry; + + configASSERT( ( uxNewPriority < configMAX_PRIORITIES ) ); + + /* Ensure the new priority is valid. */ + if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) + { + uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; + } + + taskENTER_CRITICAL(); + { + pxTCB = prvGetTCBFromHandle( NULL ); + + #if ( configUSE_MUTEXES == 1 ) + { + pxSavedPriority->uxPriority = pxTCB->uxPriority; + pxSavedPriority->uxBasePriority = pxTCB->uxBasePriority; + + /* If uxNewPriority < uxBasePriority, then there is nothing else to + * do, as uxBasePriority is always <= uxPriority. */ + if( uxNewPriority > pxTCB->uxBasePriority ) + { + pxTCB->uxBasePriority = uxNewPriority; + + /* Remember the task's current priority before attempting to + * change it. If the task's current priority is changed, it must + * be done so before moving the task between task lists) in order + * for the taskRESET_READY_PRIORITY() macro to function correctly. */ + uxPriorityUsedOnEntry = pxTCB->uxPriority; + + if( uxNewPriority > pxTCB->uxPriority ) + { + pxTCB->uxPriority = uxNewPriority; + + /* Only reset the event list item value if the value is not + * being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) + { + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + } + + /* If the task is in the blocked or suspended list we need do + * nothing more than change its priority variable. However, if + * the task is in a ready list it needs to be removed and placed + * in the list appropriate to its new priority. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) + { + /* The task is currently in its ready list - remove before + * adding it to its new ready list. As we are in a critical + * section we can do this even if the scheduler is suspended. */ + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + /* It is known that the task is in its ready list so + * there is no need to check again and the port level + * reset macro can be called directly. */ + portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority ); + } + prvAddTaskToReadyList( pxTCB ); + } + } + } + } + #else /* if ( configUSE_MUTEXES == 1 ) */ + { + pxSavedPriority->uxPriority = pxTCB->uxPriority; + if ( uxNewPriority > pxTCB->uxPriority) + { + vTaskPrioritySet( NULL, uxNewPriority ); + } + } + #endif + } + taskEXIT_CRITICAL(); +} + +void prvTaskPriorityRestore( prvTaskSavedPriority_t * pxSavedPriority ) +{ + TCB_t * pxTCB; + UBaseType_t uxNewPriority; + UBaseType_t uxPriorityUsedOnEntry; + UBaseType_t uxBasePriorityUsedOnEntry; + BaseType_t xYieldRequired = pdFALSE; + + taskENTER_CRITICAL(); + { + pxTCB = prvGetTCBFromHandle( NULL ); + + #if ( configUSE_MUTEXES == 1 ) + { + /* If the saved uxBasePriority == the task's uxBasePriority, it means + * that prvTaskPriorityRaise() never raised the task's uxBasePriority. + * In that case, there is nothing else to do. */ + if( pxSavedPriority->uxBasePriority != pxTCB->uxBasePriority ) + { + uxBasePriorityUsedOnEntry = pxTCB->uxBasePriority; + pxTCB->uxBasePriority = pxSavedPriority->uxBasePriority; + + /* Remember the task's current priority before attempting to + * change it. If the task's current priority is changed, it must + * be done so before moving the task between task lists in order + * for the taskRESET_READY_PRIORITY() macro to function correctly. */ + uxPriorityUsedOnEntry = pxTCB->uxPriority; + + /* Check if the task inherited a priority after prvTaskPriorityRaise(). + * If this is the case, there is nothing else to do. The priority + * will be restored when the task disinherits its priority. */ + if( pxTCB->uxPriority == uxBasePriorityUsedOnEntry ) + { + if( pxTCB->uxMutexesHeld == 0 ) + { + /* The task may have inherited a priority before prvTaskPriorityRaise() + * then disinherited a priority after prvTaskPriorityRaise(). + * Thus we need set the uxPriority to the saved base priority + * so that the task's priority gets restored to the priority + * before any inheritance or raising. */ + pxTCB->uxPriority = pxSavedPriority->uxBasePriority; + } + else + { + /* The task may have inherited a priority before prvTaskPriorityRaise() + * was called. Thus, we need to restore uxPriority to the + * "saved uxPriority" so that the task still retains that + * inherited priority. */ + pxTCB->uxPriority = pxSavedPriority->uxPriority; + } + uxNewPriority = pxTCB->uxPriority; + + if( uxNewPriority < uxPriorityUsedOnEntry ) + { + /* Setting the priority of the running task down means + * there may now be another task of higher priority that + * is ready to execute. */ + xYieldRequired = pdTRUE; + } + + /* Only reset the event list item value if the value is not + * being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) + { + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + } + + /* If the task is in the blocked or suspended list we need do + * nothing more than change its priority variable. However, if + * the task is in a ready list it needs to be removed and placed + * in the list appropriate to its new priority. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) + { + /* The task is currently in its ready list - remove before + * adding it to its new ready list. As we are in a critical + * section we can do this even if the scheduler is suspended. */ + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + /* It is known that the task is in its ready list so + * there is no need to check again and the port level + * reset macro can be called directly. */ + portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority ); + } + prvAddTaskToReadyList( pxTCB ); + } + + if( xYieldRequired != pdFALSE ) + { + taskYIELD_IF_USING_PREEMPTION(); + } + } + } + } + #else /* if ( configUSE_MUTEXES == 1 ) */ + { + vTaskPrioritySet( NULL, pxSavedPriority->uxPriority ); + } + #endif + } + taskEXIT_CRITICAL(); +} + +#endif // ( INCLUDE_vTaskPrioritySet == 1 ) diff --git a/components/freertos/linker.lf b/components/freertos/linker.lf index 58fda61f49..173a62af78 100644 --- a/components/freertos/linker.lf +++ b/components/freertos/linker.lf @@ -34,6 +34,8 @@ entries: tasks: vTaskRemoveFromUnorderedEventList (default) tasks: uxTaskPriorityGet (default) tasks: vTaskPrioritySet (default) + tasks: prvTaskPriorityRaise (default) + tasks: prvTaskPriorityRestore (default) tasks: vTaskSetThreadLocalStoragePointerAndDelCallback (default) tasks: pvTaskGetThreadLocalStoragePointer (default) tasks: xTaskGetCurrentTaskHandleForCPU (default) diff --git a/components/freertos/linker_smp.lf b/components/freertos/linker_smp.lf index ff9ba77f55..01f7c5b211 100644 --- a/components/freertos/linker_smp.lf +++ b/components/freertos/linker_smp.lf @@ -76,6 +76,8 @@ entries: tasks: eTaskGetState (default) tasks: uxTaskPriorityGet (default) tasks: vTaskPrioritySet (default) + tasks: prvTaskPriorityRaise (default) + tasks: prvTaskPriorityRestore (default) tasks: vTaskSuspend (default) tasks: vTaskResume (default) tasks: prvCreateIdleTasks (default) diff --git a/components/spi_flash/cache_utils.c b/components/spi_flash/cache_utils.c index 89bb53e8fc..d2021dc27d 100644 --- a/components/spi_flash/cache_utils.c +++ b/components/spi_flash/cache_utils.c @@ -11,6 +11,7 @@ #include #include +#include #include #if CONFIG_IDF_TARGET_ESP32 #include "soc/dport_reg.h" @@ -170,8 +171,9 @@ void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu(void) } else { // Temporarily raise current task priority to prevent a deadlock while // waiting for IPC task to start on the other CPU - int old_prio = uxTaskPriorityGet(NULL); - vTaskPrioritySet(NULL, configMAX_PRIORITIES - 1); + prvTaskSavedPriority_t SavedPriority; + prvTaskPriorityRaise(&SavedPriority, configMAX_PRIORITIES - 1); + // Signal to the spi_flash_op_block_task on the other CPU that we need it to // disable cache there and block other tasks from executing. s_flash_op_can_start = false; @@ -189,7 +191,7 @@ void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu(void) vTaskSuspendAll(); #endif // CONFIG_FREERTOS_SMP // Can now set the priority back to the normal one - vTaskPrioritySet(NULL, old_prio); + prvTaskPriorityRestore(&SavedPriority); // This is guaranteed to run on CPU because the other CPU is now // occupied by highest priority task assert(xPortGetCoreID() == cpuid);