feat(freertos/smp): Add support for TCB locks

This commit is contained in:
Sudeep Mohanty 2026-06-06 11:36:05 +02:00
parent 62f334a51e
commit 1ae917325c
6 changed files with 1059 additions and 326 deletions

View file

@ -81,6 +81,34 @@
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
/*
* Macros used to lock and unlock an event group. When a task locks an,
* event group, the task will have thread safe non-deterministic access to
* the event group.
* - Concurrent access from other tasks will be blocked by the xTaskSpinlock
* - Concurrent access from ISRs will be pended
*
* When the task unlocks the event group, all pended access attempts are handled.
*/
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
#define event_groupsLOCK( pxEventBits ) taskDATA_GROUP_LOCK( &( ( pxEventBits )->xTaskSpinlock ) )
#define event_groupsUNLOCK( pxEventBits ) taskDATA_GROUP_UNLOCK( &( ( pxEventBits )->xTaskSpinlock ) )
#define event_groupsUNLOCK_WITH_YIELD_STATUS( pxEventBits, pxAlreadyYielded ) \
do { \
*( pxAlreadyYielded ) = taskDATA_GROUP_UNLOCK( &( ( pxEventBits )->xTaskSpinlock ) ); \
} while( 0 )
#else /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */
#define event_groupsLOCK( pxEventBits ) vTaskSuspendAll()
#define event_groupsUNLOCK( pxEventBits ) xTaskResumeAll()
#define event_groupsUNLOCK_WITH_YIELD_STATUS( pxEventBits, pxAlreadyYielded ) \
do { \
*( pxAlreadyYielded ) = xTaskResumeAll(); \
} while( 0 )
#endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */
/*-----------------------------------------------------------*/
#if ( configSUPPORT_STATIC_ALLOCATION == 1 ) #if ( configSUPPORT_STATIC_ALLOCATION == 1 )
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer ) EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t * pxEventGroupBuffer )
@ -245,7 +273,7 @@
} }
} }
} }
xAlreadyYielded = xTaskResumeAll(); event_groupsUNLOCK_WITH_YIELD_STATUS( pxEventBits, &xAlreadyYielded );
if( xTicksToWait != ( TickType_t ) 0 ) if( xTicksToWait != ( TickType_t ) 0 )
{ {
@ -401,7 +429,7 @@
traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor ); traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor );
} }
} }
xAlreadyYielded = xTaskResumeAll(); event_groupsUNLOCK_WITH_YIELD_STATUS( pxEventBits, &xAlreadyYielded );
if( xTicksToWait != ( TickType_t ) 0 ) if( xTicksToWait != ( TickType_t ) 0 )
{ {
@ -568,6 +596,13 @@
{ {
traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet ); traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet );
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
/* We are about to access the kernel data group non-deterministically,
* thus we suspend the kernel data group.*/
vTaskSuspendAll();
#endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */
pxListItem = listGET_HEAD_ENTRY( pxList ); pxListItem = listGET_HEAD_ENTRY( pxList );
/* Set the bits. */ /* Set the bits. */
@ -638,8 +673,12 @@
/* Snapshot resulting bits. */ /* Snapshot resulting bits. */
uxReturnBits = pxEventBits->uxEventBits; uxReturnBits = pxEventBits->uxEventBits;
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
( void ) xTaskResumeAll();
#endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */
} }
( void ) xTaskResumeAll(); event_groupsUNLOCK( pxEventBits );
traceRETURN_xEventGroupSetBits( uxReturnBits ); traceRETURN_xEventGroupSetBits( uxReturnBits );
@ -662,6 +701,13 @@
{ {
traceEVENT_GROUP_DELETE( xEventGroup ); traceEVENT_GROUP_DELETE( xEventGroup );
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
/* We are about to access the kernel data group non-deterministically,
* thus we suspend the kernel data group.*/
vTaskSuspendAll();
#endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */
while( listCURRENT_LIST_LENGTH( pxTasksWaitingForBits ) > ( UBaseType_t ) 0 ) while( listCURRENT_LIST_LENGTH( pxTasksWaitingForBits ) > ( UBaseType_t ) 0 )
{ {
/* Unblock the task, returning 0 as the event list is being deleted /* Unblock the task, returning 0 as the event list is being deleted
@ -669,8 +715,12 @@
configASSERT( pxTasksWaitingForBits->xListEnd.pxNext != ( const ListItem_t * ) &( pxTasksWaitingForBits->xListEnd ) ); configASSERT( pxTasksWaitingForBits->xListEnd.pxNext != ( const ListItem_t * ) &( pxTasksWaitingForBits->xListEnd ) );
vTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET ); vTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET );
} }
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
( void ) xTaskResumeAll();
#endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */
} }
( void ) xTaskResumeAll(); event_groupsUNLOCK( pxEventBits );
#if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) ) #if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) )
{ {

View file

@ -2942,10 +2942,6 @@
#error configUSE_MUTEXES must be set to 1 to use recursive mutexes #error configUSE_MUTEXES must be set to 1 to use recursive mutexes
#endif #endif
#if ( ( configRUN_MULTIPLE_PRIORITIES == 0 ) && ( configUSE_TASK_PREEMPTION_DISABLE != 0 ) )
#error configRUN_MULTIPLE_PRIORITIES must be set to 1 to use task preemption disable
#endif
#if ( ( configUSE_PREEMPTION == 0 ) && ( configUSE_TASK_PREEMPTION_DISABLE != 0 ) ) #if ( ( configUSE_PREEMPTION == 0 ) && ( configUSE_TASK_PREEMPTION_DISABLE != 0 ) )
#error configUSE_PREEMPTION must be set to 1 to use task preemption disable #error configUSE_PREEMPTION must be set to 1 to use task preemption disable
#endif #endif
@ -3245,6 +3241,9 @@ typedef struct xSTATIC_TCB
#if ( configUSE_TASK_PREEMPTION_DISABLE == 1 ) #if ( configUSE_TASK_PREEMPTION_DISABLE == 1 )
BaseType_t xDummy26; BaseType_t xDummy26;
#endif #endif
#if ( portUSING_GRANULAR_LOCKS == 1 )
portSPINLOCK_TYPE xDummy27;
#endif
#if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
void * pxDummy8; void * pxDummy8;
#endif #endif

View file

@ -296,22 +296,12 @@ typedef enum
* \ingroup GranularLocks * \ingroup GranularLocks
*/ */
#if ( portUSING_GRANULAR_LOCKS == 1 ) #if ( portUSING_GRANULAR_LOCKS == 1 )
#define taskDATA_GROUP_ENTER_CRITICAL( pxTaskSpinlock, pxISRSpinlock ) \
do { \ /* Using a function implementation now since the data group entering critical
/* Disable preemption to avoid task state changes during the critical section. */ \ * section needs to check for run state change. */
vTaskPreemptionDisable( NULL ); \ void taskDataGroupEnterCritical( portSPINLOCK_TYPE * pxTaskSpinlock,
{ \ portSPINLOCK_TYPE * pxISRSpinlock );
const BaseType_t xCoreID = ( BaseType_t ) portGET_CORE_ID(); \ #define taskDATA_GROUP_ENTER_CRITICAL taskDataGroupEnterCritical
/* Task spinlock is always taken first */ \
portGET_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) ( pxTaskSpinlock ) ); \
/* Disable interrupts */ \
portDISABLE_INTERRUPTS(); \
/* Take the ISR spinlock next */ \
portGET_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) ( pxISRSpinlock ) ); \
/* Increment the critical nesting count */ \
portINCREMENT_CRITICAL_NESTING_COUNT( xCoreID ); \
} \
} while( 0 )
#endif /* #if ( portUSING_GRANULAR_LOCKS == 1 ) */ #endif /* #if ( portUSING_GRANULAR_LOCKS == 1 ) */
/** /**
@ -365,7 +355,7 @@ typedef enum
mtCOVERAGE_TEST_MARKER(); \ mtCOVERAGE_TEST_MARKER(); \
} \ } \
/* Re-enable preemption */ \ /* Re-enable preemption */ \
prvTaskPreemptionEnable( NULL ); \ ( void ) xTaskPreemptionEnableWithYieldStatus( NULL ); \
} while( 0 ) } while( 0 )
#endif /* #if ( portUSING_GRANULAR_LOCKS == 1 ) */ #endif /* #if ( portUSING_GRANULAR_LOCKS == 1 ) */
@ -421,12 +411,12 @@ typedef enum
* \ingroup GranularLocks * \ingroup GranularLocks
*/ */
#if ( portUSING_GRANULAR_LOCKS == 1 ) #if ( portUSING_GRANULAR_LOCKS == 1 )
#define taskDATA_GROUP_UNLOCK( pxTaskSpinlock ) \
( { \ /* Release the task spinlock and re-enable preemption.
portRELEASE_SPINLOCK( portGET_CORE_ID(), ( portSPINLOCK_TYPE * ) ( pxTaskSpinlock ) ); \ * Returns the yield status reported by xTaskPreemptionEnableWithYieldStatus(). */
/* Re-enable preemption after releasing the task spinlock. */ \ #define taskDATA_GROUP_UNLOCK( pxTaskSpinlock ) \
prvTaskPreemptionEnable( NULL ); \ ( portRELEASE_SPINLOCK( portGET_CORE_ID(), ( portSPINLOCK_TYPE * ) ( pxTaskSpinlock ) ), \
} ) xTaskPreemptionEnableWithYieldStatus( NULL ) )
#endif /* #if ( portUSING_GRANULAR_LOCKS == 1 ) */ #endif /* #if ( portUSING_GRANULAR_LOCKS == 1 ) */
/*----------------------------------------------------------- /*-----------------------------------------------------------
@ -1644,7 +1634,7 @@ BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION;
* switch, otherwise pdFALSE. This is used by the scheduler to determine if a * switch, otherwise pdFALSE. This is used by the scheduler to determine if a
* context switch may be required following the enable. * context switch may be required following the enable.
*/ */
BaseType_t prvTaskPreemptionEnable( const TaskHandle_t xTask ); BaseType_t xTaskPreemptionEnableWithYieldStatus( const TaskHandle_t xTask );
#endif #endif
/*----------------------------------------------------------- /*-----------------------------------------------------------
@ -3727,6 +3717,8 @@ void vTaskPlaceOnEventListRestricted( List_t * const pxEventList,
* Removes a task from both the specified event list and the list of blocked * Removes a task from both the specified event list and the list of blocked
* tasks, and places it on a ready queue. * tasks, and places it on a ready queue.
* *
* Do not call this function from an ISR context. Call xTaskRemoveFromEventListFromISR() instead.
*
* xTaskRemoveFromEventList()/vTaskRemoveFromUnorderedEventList() will be called * xTaskRemoveFromEventList()/vTaskRemoveFromUnorderedEventList() will be called
* if either an event occurs to unblock a task, or the block timeout period * if either an event occurs to unblock a task, or the block timeout period
* expires. * expires.
@ -3743,6 +3735,23 @@ void vTaskPlaceOnEventListRestricted( List_t * const pxEventList,
* making the call, otherwise pdFALSE. * making the call, otherwise pdFALSE.
*/ */
BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ) PRIVILEGED_FUNCTION; BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ) PRIVILEGED_FUNCTION;
/*
* THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY
* INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS
* AN INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER.
*
* THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED.
*
* Removes a task from both the specified event list and the list of blocked
* tasks, and places it on a ready queue. This function is the ISR-safe version
* of xTaskRemoveFromEventList().
*
* @return pdTRUE if the task being removed has a higher priority than the task
* making the call, otherwise pdFALSE.
*/
BaseType_t xTaskRemoveFromEventListFromISR( const List_t * const pxEventList ) PRIVILEGED_FUNCTION;
void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem,
const TickType_t xItemValue ) PRIVILEGED_FUNCTION; const TickType_t xItemValue ) PRIVILEGED_FUNCTION;

87
queue.c
View file

@ -2498,7 +2498,15 @@ static void prvUnlockQueue( Queue_t * const pxQueue )
* removed from the queue while the queue was locked. When a queue is * removed from the queue while the queue was locked. When a queue is
* locked items can be added or removed, but the event lists cannot be * locked items can be added or removed, but the event lists cannot be
* updated. */ * updated. */
taskENTER_CRITICAL(); #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
const BaseType_t xCoreID = portGET_CORE_ID();
portDISABLE_INTERRUPTS();
portINCREMENT_CRITICAL_NESTING_COUNT( xCoreID );
portGET_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) &( pxQueue->xISRSpinlock ) );
#else
queueENTER_CRITICAL( pxQueue );
#endif
{ {
int8_t cTxLock = pxQueue->cTxLock; int8_t cTxLock = pxQueue->cTxLock;
@ -2576,10 +2584,26 @@ static void prvUnlockQueue( Queue_t * const pxQueue )
pxQueue->cTxLock = queueUNLOCKED; pxQueue->cTxLock = queueUNLOCKED;
} }
taskEXIT_CRITICAL(); #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
portRELEASE_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) &( pxQueue->xISRSpinlock ) );
portDECREMENT_CRITICAL_NESTING_COUNT( xCoreID );
if( portGET_CRITICAL_NESTING_COUNT( xCoreID ) == 0U )
{
portENABLE_INTERRUPTS();
}
#else
queueEXIT_CRITICAL( pxQueue );
#endif
/* Do the same for the Rx lock. */ /* Do the same for the Rx lock. */
taskENTER_CRITICAL(); #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
portDISABLE_INTERRUPTS();
portINCREMENT_CRITICAL_NESTING_COUNT( xCoreID );
portGET_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) &( pxQueue->xISRSpinlock ) );
#else
queueENTER_CRITICAL( pxQueue );
#endif
{ {
int8_t cRxLock = pxQueue->cRxLock; int8_t cRxLock = pxQueue->cRxLock;
@ -3325,8 +3349,63 @@ BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue )
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
#if ( configUSE_QUEUE_SETS == 1 ) #if ( configUSE_QUEUE_SETS == 1 )
static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue ) static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue )
{
BaseType_t xReturn;
/* Call the generic version with xIsISR = pdFALSE to indicate task context */
#if ( portUSING_GRANULAR_LOCKS == 0 )
{
xReturn = prvNotifyQueueSetContainerGeneric( pxQueue, pdFALSE );
}
#else
{
const BaseType_t xCoreID = portGET_CORE_ID();
/* This API must be called in a critical section which already has preemption
* and interrupt disabled. */
portGET_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) &( pxQueue->pxQueueSetContainer->xTaskSpinlock ) );
portGET_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) &( pxQueue->pxQueueSetContainer->xISRSpinlock ) );
{
xReturn = prvNotifyQueueSetContainerGeneric( pxQueue, pdFALSE );
}
portRELEASE_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) &( pxQueue->pxQueueSetContainer->xISRSpinlock ) );
portRELEASE_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) &( pxQueue->pxQueueSetContainer->xTaskSpinlock ) );
}
#endif /* if ( portUSING_GRANULAR_LOCKS == 0 ) */
return xReturn;
}
static BaseType_t prvNotifyQueueSetContainerFromISR( const Queue_t * const pxQueue )
{
BaseType_t xReturn;
/* Call the generic version with xIsISR = pdTRUE to indicate ISR context */
#if ( portUSING_GRANULAR_LOCKS == 0 )
{
xReturn = prvNotifyQueueSetContainerGeneric( pxQueue, pdTRUE );
}
#else
{
const BaseType_t xCoreID = portGET_CORE_ID();
/* This API must be called in a critical section which already has interrupt disabled. */
portGET_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) &( pxQueue->pxQueueSetContainer->xISRSpinlock ) );
{
xReturn = prvNotifyQueueSetContainerGeneric( pxQueue, pdTRUE );
}
portRELEASE_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) &( pxQueue->pxQueueSetContainer->xISRSpinlock ) );
}
#endif /* if ( portUSING_GRANULAR_LOCKS == 0 ) */
return xReturn;
}
static BaseType_t prvNotifyQueueSetContainerGeneric( const Queue_t * const pxQueue,
const BaseType_t xIsISR )
{ {
Queue_t * pxQueueSetContainer = pxQueue->pxQueueSetContainer; Queue_t * pxQueueSetContainer = pxQueue->pxQueueSetContainer;
BaseType_t xReturn = pdFALSE; BaseType_t xReturn = pdFALSE;

1166
tasks.c

File diff suppressed because it is too large Load diff

View file

@ -149,6 +149,16 @@
PRIVILEGED_DATA static QueueHandle_t xTimerQueue = NULL; PRIVILEGED_DATA static QueueHandle_t xTimerQueue = NULL;
PRIVILEGED_DATA static TaskHandle_t xTimerTaskHandle = NULL; PRIVILEGED_DATA static TaskHandle_t xTimerTaskHandle = NULL;
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
#ifdef portREMOVE_STATIC_QUALIFIER
PRIVILEGED_DATA portSPINLOCK_TYPE xTimerTaskSpinlock = portINIT_SPINLOCK_STATIC;
PRIVILEGED_DATA portSPINLOCK_TYPE xTimerISRSpinlock = portINIT_SPINLOCK_STATIC;
#else
PRIVILEGED_DATA static portSPINLOCK_TYPE xTimerTaskSpinlock = portINIT_SPINLOCK_STATIC;
PRIVILEGED_DATA static portSPINLOCK_TYPE xTimerISRSpinlock = portINIT_SPINLOCK_STATIC;
#endif
#endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
/* /*