Skip to content

Commit cf960f6

Browse files
change(freertos/smp): Update stream_buffer.c locking
Updated stream_buffer.c to use granular locking - Added xTaskSpinlock and xISRSpinlock - Replaced critical section macros with data group locking macros such as taskENTER/EXIT_CRITICAL() with sbENTER/EXIT_CRITICAL(). - Added prvLockStreamBufferForTasks() and prvUnlockStreamBufferForTasks() to suspend the stream buffer when executing non-deterministic code. Co-authored-by: Sudeep Mohanty <[email protected]>
1 parent 268fa4d commit cf960f6

File tree

2 files changed

+127
-19
lines changed

2 files changed

+127
-19
lines changed

include/FreeRTOS.h

+3
Original file line numberDiff line numberDiff line change
@@ -3442,6 +3442,9 @@ typedef struct xSTATIC_STREAM_BUFFER
34423442
void * pvDummy5[ 2 ];
34433443
#endif
34443444
UBaseType_t uxDummy6;
3445+
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
3446+
portSPINLOCK_TYPE xDummySpinlock[ 2 ];
3447+
#endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */
34453448
} StaticStreamBuffer_t;
34463449

34473450
/* Message buffers are built on stream buffers. */

stream_buffer.c

+124-19
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,47 @@
5858
#error INCLUDE_xTaskGetCurrentTaskHandle must be set to 1 to build stream_buffer.c
5959
#endif
6060

61+
62+
/*
63+
* Macros to mark the start and end of a critical code region.
64+
*/
65+
#if ( portUSING_GRANULAR_LOCKS == 1 )
66+
#define sbENTER_CRITICAL( pxStreamBuffer ) portENTER_CRITICAL_DATA_GROUP( ( portSPINLOCK_TYPE * ) &( pxStreamBuffer->xTaskSpinlock ), &( pxStreamBuffer->xISRSpinlock ) )
67+
#define sbENTER_CRITICAL_FROM_ISR( pxStreamBuffer ) portENTER_CRITICAL_DATA_GROUP_FROM_ISR( ( portSPINLOCK_TYPE * ) &( pxStreamBuffer->xISRSpinlock ) )
68+
#define sbEXIT_CRITICAL( pxEventBits ) portEXIT_CRITICAL_DATA_GROUP( ( portSPINLOCK_TYPE * ) &( pxStreamBuffer->xTaskSpinlock ), &( pxStreamBuffer->xISRSpinlock ) )
69+
#define sbEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus, pxEventBits ) portEXIT_CRITICAL_DATA_GROUP_FROM_ISR( uxSavedInterruptStatus, ( portSPINLOCK_TYPE * ) &( pxStreamBuffer->xISRSpinlock ) )
70+
#else /* #if ( portUSING_GRANULAR_LOCKS == 1 ) */
71+
#define sbENTER_CRITICAL( pxEventBits ) do { ( void ) pxStreamBuffer; taskENTER_CRITICAL(); } while( 0 )
72+
#define sbENTER_CRITICAL_FROM_ISR( pxEventBits ) do { ( void ) pxStreamBuffer; taskENTER_CRITICAL_FROM_ISR(); } while( 0 )
73+
#define sbEXIT_CRITICAL( pxEventBits ) do { ( void ) pxStreamBuffer; taskEXIT_CRITICAL(); } while( 0 )
74+
#define sbEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus, pxStreamBuffer ) do { ( void ) pxStreamBuffer; taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); } while( 0 )
75+
#endif /* #if ( portUSING_GRANULAR_LOCKS == 1 ) */
76+
77+
/*
78+
* Macro used to lock and unlock a stream buffer. When a task locks a stream
79+
* buffer, the task will have thread safe non-deterministic access to the stream
80+
* buffer.
81+
* - Concurrent access from other tasks will be blocked by the xTaskSpinlock
82+
* - Concurrent access from ISRs will be pended
83+
*
84+
* When the task unlocks the stream buffer, all pended access attempts are handled.
85+
*/
86+
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
87+
#define sbLOCK( pxStreamBuffer ) prvLockStreamBufferForTasks( pxStreamBuffer )
88+
#define sbUNLOCK( pxStreamBuffer ) prvUnlockStreamBufferForTasks( pxStreamBuffer )
89+
#else /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */
90+
#define sbLOCK( pxStreamBuffer ) vTaskSuspendAll()
91+
#define sbUNLOCK( pxStreamBuffer ) ( void ) xTaskResumeAll()
92+
#endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */
93+
6194
/* If the user has not provided application specific Rx notification macros,
6295
* or #defined the notification macros away, then provide default implementations
6396
* that uses task notifications. */
6497
#ifndef sbRECEIVE_COMPLETED
6598
#define sbRECEIVE_COMPLETED( pxStreamBuffer ) \
6699
do \
67100
{ \
68-
vTaskSuspendAll(); \
101+
sbLOCK( pxStreamBuffer ); \
69102
{ \
70103
if( ( pxStreamBuffer )->xTaskWaitingToSend != NULL ) \
71104
{ \
@@ -76,7 +109,7 @@
76109
( pxStreamBuffer )->xTaskWaitingToSend = NULL; \
77110
} \
78111
} \
79-
( void ) xTaskResumeAll(); \
112+
( void ) sbUNLOCK( pxStreamBuffer ); \
80113
} while( 0 )
81114
#endif /* sbRECEIVE_COMPLETED */
82115

@@ -105,7 +138,7 @@
105138
do { \
106139
UBaseType_t uxSavedInterruptStatus; \
107140
\
108-
uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR(); \
141+
uxSavedInterruptStatus = sbENTER_CRITICAL_FROM_ISR( pxStreamBuffer ); \
109142
{ \
110143
if( ( pxStreamBuffer )->xTaskWaitingToSend != NULL ) \
111144
{ \
@@ -117,7 +150,7 @@
117150
( pxStreamBuffer )->xTaskWaitingToSend = NULL; \
118151
} \
119152
} \
120-
taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); \
153+
sbEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus, pxStreamBuffer ); \
121154
} while( 0 )
122155
#endif /* sbRECEIVE_COMPLETED_FROM_ISR */
123156

@@ -145,7 +178,7 @@
145178
*/
146179
#ifndef sbSEND_COMPLETED
147180
#define sbSEND_COMPLETED( pxStreamBuffer ) \
148-
vTaskSuspendAll(); \
181+
sbLOCK( pxStreamBuffer ); \
149182
{ \
150183
if( ( pxStreamBuffer )->xTaskWaitingToReceive != NULL ) \
151184
{ \
@@ -156,7 +189,7 @@
156189
( pxStreamBuffer )->xTaskWaitingToReceive = NULL; \
157190
} \
158191
} \
159-
( void ) xTaskResumeAll()
192+
( void ) sbUNLOCK( pxStreamBuffer )
160193
#endif /* sbSEND_COMPLETED */
161194

162195
/* If user has provided a per-instance send completed callback, then
@@ -184,7 +217,7 @@
184217
do { \
185218
UBaseType_t uxSavedInterruptStatus; \
186219
\
187-
uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR(); \
220+
uxSavedInterruptStatus = sbENTER_CRITICAL_FROM_ISR( pxStreamBuffer ); \
188221
{ \
189222
if( ( pxStreamBuffer )->xTaskWaitingToReceive != NULL ) \
190223
{ \
@@ -196,7 +229,7 @@
196229
( pxStreamBuffer )->xTaskWaitingToReceive = NULL; \
197230
} \
198231
} \
199-
taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); \
232+
sbEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus, pxStreamBuffer ); \
200233
} while( 0 )
201234
#endif /* sbSEND_COMPLETE_FROM_ISR */
202235

@@ -249,8 +282,30 @@ typedef struct StreamBufferDef_t
249282
StreamBufferCallbackFunction_t pxReceiveCompletedCallback; /* Optional callback called on receive complete. sbRECEIVE_COMPLETED is called if this is NULL. */
250283
#endif
251284
UBaseType_t uxNotificationIndex; /* The index we are using for notification, by default tskDEFAULT_INDEX_TO_NOTIFY. */
285+
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
286+
portSPINLOCK_TYPE xTaskSpinlock;
287+
portSPINLOCK_TYPE xISRSpinlock;
288+
#endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */
252289
} StreamBuffer_t;
253290

291+
/*
292+
* Locks a stream buffer for tasks. Prevents other tasks from accessing the stream buffer
293+
* but allows ISRs to pend access to the stream buffer. Caller cannot be preempted
294+
* by other tasks after locking the stream buffer, thus allowing the caller to
295+
* execute non-deterministic operations.
296+
*/
297+
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
298+
static void prvLockStreamBufferForTasks( StreamBuffer_t * const pxStreamBuffer ) PRIVILEGED_FUNCTION;
299+
#endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */
300+
301+
/*
302+
* Unlocks a stream buffer for tasks. Handles all pended access from ISRs, then reenables preemption
303+
* for the caller.
304+
*/
305+
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
306+
static void prvUnlockStreamBufferForTasks( StreamBuffer_t * const pxStreamBuffer ) PRIVILEGED_FUNCTION;
307+
#endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */
308+
254309
/*
255310
* The number of bytes available to be read from the buffer.
256311
*/
@@ -327,6 +382,42 @@ static void prvInitialiseNewStreamBuffer( StreamBuffer_t * const pxStreamBuffer,
327382
StreamBufferCallbackFunction_t pxReceiveCompletedCallback ) PRIVILEGED_FUNCTION;
328383

329384
/*-----------------------------------------------------------*/
385+
386+
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
387+
static void prvLockStreamBufferForTasks( StreamBuffer_t * const pxStreamBuffer )
388+
{
389+
/* Disable preemption so that the current task cannot be preempted by another task */
390+
vTaskPreemptionDisable( NULL );
391+
392+
/* Lock the stream buffer data group so that we can suspend the stream buffer atomically */
393+
sbENTER_CRITICAL( pxStreamBuffer );
394+
395+
/* Keep holding xTaskSpinlock after unlocking the data group to prevent tasks
396+
* on other cores from accessing the stream buffer while it is suspended. */
397+
portGET_SPINLOCK( portGET_CORE_ID(), &( pxStreamBuffer->xTaskSpinlock ) );
398+
399+
sbEXIT_CRITICAL( pxStreamBuffer );
400+
}
401+
#endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */
402+
/*-----------------------------------------------------------*/
403+
404+
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
405+
static void prvUnlockStreamBufferForTasks( StreamBuffer_t * const pxStreamBuffer )
406+
{
407+
/* Lock the stream buffer data group so that we can handle any pended accesses atomically */
408+
sbENTER_CRITICAL( pxStreamBuffer );
409+
410+
/* Release the previously held task spinlock */
411+
portRELEASE_SPINLOCK( portGET_CORE_ID(), &( pxStreamBuffer->xTaskSpinlock ) );
412+
413+
sbEXIT_CRITICAL( pxStreamBuffer );
414+
415+
/* Re-enable preemption */
416+
vTaskPreemptionEnable( NULL );
417+
}
418+
#endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */
419+
/*-----------------------------------------------------------*/
420+
330421
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
331422
StreamBufferHandle_t xStreamBufferGenericCreate( size_t xBufferSizeBytes,
332423
size_t xTriggerLevelBytes,
@@ -405,6 +496,13 @@ static void prvInitialiseNewStreamBuffer( StreamBuffer_t * const pxStreamBuffer,
405496
pxSendCompletedCallback,
406497
pxReceiveCompletedCallback );
407498

499+
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
500+
{
501+
portINIT_SPINLOCK( &( ( ( StreamBuffer_t * ) pvAllocatedMemory )->xTaskSpinlock ) );
502+
portINIT_SPINLOCK( &( ( ( StreamBuffer_t * ) pvAllocatedMemory )->xISRSpinlock ) );
503+
}
504+
#endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */
505+
408506
traceSTREAM_BUFFER_CREATE( ( ( StreamBuffer_t * ) pvAllocatedMemory ), xStreamBufferType );
409507
}
410508
else
@@ -499,6 +597,13 @@ static void prvInitialiseNewStreamBuffer( StreamBuffer_t * const pxStreamBuffer,
499597
* again. */
500598
pxStreamBuffer->ucFlags |= sbFLAGS_IS_STATICALLY_ALLOCATED;
501599

600+
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
601+
{
602+
portINIT_SPINLOCK( &( pxStreamBuffer->xTaskSpinlock ) );
603+
portINIT_SPINLOCK( &( pxStreamBuffer->xISRSpinlock ) );
604+
}
605+
#endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */
606+
502607
traceSTREAM_BUFFER_CREATE( pxStreamBuffer, xStreamBufferType );
503608

504609
/* MISRA Ref 11.3.1 [Misaligned access] */
@@ -614,7 +719,7 @@ BaseType_t xStreamBufferReset( StreamBufferHandle_t xStreamBuffer )
614719
#endif
615720

616721
/* Can only reset a message buffer if there are no tasks blocked on it. */
617-
taskENTER_CRITICAL();
722+
sbENTER_CRITICAL( pxStreamBuffer );
618723
{
619724
if( ( pxStreamBuffer->xTaskWaitingToReceive == NULL ) && ( pxStreamBuffer->xTaskWaitingToSend == NULL ) )
620725
{
@@ -644,7 +749,7 @@ BaseType_t xStreamBufferReset( StreamBufferHandle_t xStreamBuffer )
644749
xReturn = pdPASS;
645750
}
646751
}
647-
taskEXIT_CRITICAL();
752+
sbEXIT_CRITICAL( pxStreamBuffer );
648753

649754
traceRETURN_xStreamBufferReset( xReturn );
650755

@@ -872,7 +977,7 @@ size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer,
872977
{
873978
/* Wait until the required number of bytes are free in the message
874979
* buffer. */
875-
taskENTER_CRITICAL();
980+
sbENTER_CRITICAL( pxStreamBuffer );
876981
{
877982
xSpace = xStreamBufferSpacesAvailable( pxStreamBuffer );
878983

@@ -887,11 +992,11 @@ size_t xStreamBufferSend( StreamBufferHandle_t xStreamBuffer,
887992
}
888993
else
889994
{
890-
taskEXIT_CRITICAL();
995+
sbEXIT_CRITICAL( pxStreamBuffer );
891996
break;
892997
}
893998
}
894-
taskEXIT_CRITICAL();
999+
sbEXIT_CRITICAL( pxStreamBuffer );
8951000

8961001
traceBLOCKING_ON_STREAM_BUFFER_SEND( xStreamBuffer );
8971002
( void ) xTaskNotifyWaitIndexed( pxStreamBuffer->uxNotificationIndex, ( uint32_t ) 0, ( uint32_t ) 0, NULL, xTicksToWait );
@@ -1087,7 +1192,7 @@ size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer,
10871192
{
10881193
/* Checking if there is data and clearing the notification state must be
10891194
* performed atomically. */
1090-
taskENTER_CRITICAL();
1195+
sbENTER_CRITICAL( pxStreamBuffer );
10911196
{
10921197
xBytesAvailable = prvBytesInBuffer( pxStreamBuffer );
10931198

@@ -1112,7 +1217,7 @@ size_t xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer,
11121217
mtCOVERAGE_TEST_MARKER();
11131218
}
11141219
}
1115-
taskEXIT_CRITICAL();
1220+
sbEXIT_CRITICAL( pxStreamBuffer );
11161221

11171222
if( xBytesAvailable <= xBytesToStoreMessageLength )
11181223
{
@@ -1409,7 +1514,7 @@ BaseType_t xStreamBufferSendCompletedFromISR( StreamBufferHandle_t xStreamBuffer
14091514
/* MISRA Ref 4.7.1 [Return value shall be checked] */
14101515
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#dir-47 */
14111516
/* coverity[misra_c_2012_directive_4_7_violation] */
1412-
uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();
1517+
uxSavedInterruptStatus = sbENTER_CRITICAL_FROM_ISR( pxStreamBuffer );
14131518
{
14141519
if( ( pxStreamBuffer )->xTaskWaitingToReceive != NULL )
14151520
{
@@ -1426,7 +1531,7 @@ BaseType_t xStreamBufferSendCompletedFromISR( StreamBufferHandle_t xStreamBuffer
14261531
xReturn = pdFALSE;
14271532
}
14281533
}
1429-
taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );
1534+
sbEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus, pxStreamBuffer );
14301535

14311536
traceRETURN_xStreamBufferSendCompletedFromISR( xReturn );
14321537

@@ -1448,7 +1553,7 @@ BaseType_t xStreamBufferReceiveCompletedFromISR( StreamBufferHandle_t xStreamBuf
14481553
/* MISRA Ref 4.7.1 [Return value shall be checked] */
14491554
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#dir-47 */
14501555
/* coverity[misra_c_2012_directive_4_7_violation] */
1451-
uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();
1556+
uxSavedInterruptStatus = sbENTER_CRITICAL_FROM_ISR( pxStreamBuffer );
14521557
{
14531558
if( ( pxStreamBuffer )->xTaskWaitingToSend != NULL )
14541559
{
@@ -1465,7 +1570,7 @@ BaseType_t xStreamBufferReceiveCompletedFromISR( StreamBufferHandle_t xStreamBuf
14651570
xReturn = pdFALSE;
14661571
}
14671572
}
1468-
taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );
1573+
sbEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus, pxStreamBuffer );
14691574

14701575
traceRETURN_xStreamBufferReceiveCompletedFromISR( xReturn );
14711576

0 commit comments

Comments
 (0)