Skip to content

Commit 40a991d

Browse files
feat(freertos-smp): Light Weight Preemption Disable Locks
1 parent ec3c41e commit 40a991d

File tree

4 files changed

+223
-22
lines changed

4 files changed

+223
-22
lines changed

event_groups.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -883,14 +883,29 @@
883883
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
884884
static BaseType_t prvUnlockEventGroupForTasks( EventGroup_t * pxEventBits )
885885
{
886+
BaseType_t xReturn = pdFALSE;
887+
886888
/* Release the previously held task spinlock */
887889
portRELEASE_SPINLOCK( portGET_CORE_ID(), &( pxEventBits->xTaskSpinlock ) );
888890

889891
/* Re-enable preemption */
890892
vTaskPreemptionEnable( NULL );
891893

892-
/* We assume that the task was preempted when preemption was enabled */
893-
return pdTRUE;
894+
/* Yield if preemption was re-enabled*/
895+
if( xTaskUnlockCanYield() == pdTRUE )
896+
{
897+
taskYIELD_WITHIN_API();
898+
899+
/* Return true as the task was preempted */
900+
xReturn = pdTRUE;
901+
}
902+
else
903+
{
904+
/* Return false as the task was not preempted */
905+
xReturn = pdFALSE;
906+
}
907+
908+
return xReturn;
894909
}
895910
#endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */
896911
/*-----------------------------------------------------------*/

include/FreeRTOS.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2958,6 +2958,10 @@
29582958
#error configUSE_PORT_OPTIMISED_TASK_SELECTION is not supported in SMP FreeRTOS
29592959
#endif
29602960

2961+
#ifndef configLIGHTWEIGHT_CRITICAL_SECTION
2962+
#define configLIGHTWEIGHT_CRITICAL_SECTION 0
2963+
#endif
2964+
29612965
#ifndef configINITIAL_TICK_COUNT
29622966
#define configINITIAL_TICK_COUNT 0
29632967
#endif

include/task.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3859,6 +3859,22 @@ void vTaskInternalSetTimeOutState( TimeOut_t * const pxTimeOut ) PRIVILEGED_FUNC
38593859
void vTaskExitCriticalFromISR( UBaseType_t uxSavedInterruptStatus );
38603860
#endif
38613861

3862+
/*
3863+
* This function is only intended for use when disabling or enabling preemption of a task.
3864+
* This function takes only the kernel ISR lock, not the task lock.
3865+
*/
3866+
#if ( configLIGHTWEIGHT_CRITICAL_SECTION == 1 )
3867+
void vKernelLightWeightEnterCritical( void );
3868+
#endif
3869+
3870+
/*
3871+
* This function is only intended for use when disabling or enabling preemption of a task.
3872+
* This function releases only the kernel ISR lock, not the task lock.
3873+
*/
3874+
#if ( configLIGHTWEIGHT_CRITICAL_SECTION == 1 )
3875+
void vKernelLightWeightExitCritical( void );
3876+
#endif
3877+
38623878
/*
38633879
* Checks whether a yield is required after portUNLOCK_DATA_GROUP() returns.
38643880
* To be called while data group is locked.

tasks.c

Lines changed: 186 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,14 @@ static BaseType_t prvCreateIdleTasks( void );
629629
static void prvCheckForRunStateChange( void );
630630
#endif /* #if ( configNUMBER_OF_CORES > 1 ) */
631631

632+
#if ( configLIGHTWEIGHT_CRITICAL_SECTION == 1 )
633+
/*
634+
* Checks to see if another task moved the current task out of the ready
635+
* list while it was waiting to enter a lightweight critical section and yields, if so.
636+
*/
637+
static void prvLightWeightCheckForRunStateChange( void );
638+
#endif /* #if ( configLIGHTWEIGHT_CRITICAL_SECTION == 1 ) */
639+
632640
#if ( configNUMBER_OF_CORES > 1 )
633641

634642
/*
@@ -960,6 +968,68 @@ static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB ) PRIVILEGED_FUNCTION;
960968

961969
/*-----------------------------------------------------------*/
962970

971+
#if ( configLIGHTWEIGHT_CRITICAL_SECTION == 1 )
972+
static void prvLightWeightCheckForRunStateChange( void )
973+
{
974+
975+
const TCB_t * pxThisTCB;
976+
BaseType_t xCoreID = ( BaseType_t ) portGET_CORE_ID();
977+
978+
/* This must only be called from within a task. */
979+
portASSERT_IF_IN_ISR();
980+
981+
/* This function is always called with interrupts disabled
982+
* so this is safe. */
983+
pxThisTCB = pxCurrentTCBs[ xCoreID ];
984+
985+
while( pxThisTCB->xTaskRunState == taskTASK_SCHEDULED_TO_YIELD )
986+
{
987+
UBaseType_t uxPrevCriticalNesting;
988+
989+
/* We are only here if we just entered a critical section
990+
* or if we just suspended the scheduler, and another task
991+
* has requested that we yield.
992+
*
993+
* This is slightly complicated since we need to save and restore
994+
* the suspension and critical nesting counts, as well as release
995+
* and reacquire the correct locks. And then, do it all over again
996+
* if our state changed again during the reacquisition. */
997+
uxPrevCriticalNesting = portGET_CRITICAL_NESTING_COUNT( xCoreID );
998+
999+
if( uxPrevCriticalNesting > 0U )
1000+
{
1001+
portSET_CRITICAL_NESTING_COUNT( xCoreID, 0U );
1002+
kernelRELEASE_ISR_LOCK( xCoreID );
1003+
}
1004+
else
1005+
{
1006+
/* The scheduler is suspended. uxSchedulerSuspended is updated
1007+
* only when the task is not requested to yield. */
1008+
mtCOVERAGE_TEST_MARKER();
1009+
}
1010+
1011+
portMEMORY_BARRIER();
1012+
1013+
portENABLE_INTERRUPTS();
1014+
1015+
/* Enabling interrupts should cause this core to immediately service
1016+
* the pending interrupt and yield. After servicing the pending interrupt,
1017+
* the task needs to re-evaluate its run state within this loop, as
1018+
* other cores may have requested this task to yield, potentially altering
1019+
* its run state. */
1020+
1021+
portDISABLE_INTERRUPTS();
1022+
1023+
xCoreID = ( BaseType_t ) portGET_CORE_ID();
1024+
kernelGET_ISR_LOCK( xCoreID );
1025+
1026+
portSET_CRITICAL_NESTING_COUNT( xCoreID, uxPrevCriticalNesting );
1027+
};
1028+
}
1029+
#endif /* #if ( configLIGHTWEIGHT_CRITICAL_SECTION == 1 ) */
1030+
1031+
/*-----------------------------------------------------------*/
1032+
9631033
#if ( configNUMBER_OF_CORES > 1 )
9641034
static void prvYieldForTask( const TCB_t * pxTCB )
9651035
{
@@ -2314,7 +2384,8 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
23142384
}
23152385
else
23162386
{
2317-
mtCOVERAGE_TEST_MARKER();
2387+
/* Reset the deferred state change flags */
2388+
pxTCB->uxDeferredStateChange &= ~tskDEFERRED_DELETION;
23182389
}
23192390
#endif /* configUSE_TASK_PREEMPTION_DISABLE */
23202391

@@ -3199,7 +3270,11 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
31993270

32003271
traceENTER_vTaskPreemptionDisable( xTask );
32013272

3202-
kernelENTER_CRITICAL();
3273+
#if ( configLIGHTWEIGHT_CRITICAL_SECTION == 1 )
3274+
vKernelLightWeightEnterCritical();
3275+
#else
3276+
kernelENTER_CRITICAL();
3277+
#endif
32033278
{
32043279
if( xSchedulerRunning != pdFALSE )
32053280
{
@@ -3213,7 +3288,11 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
32133288
mtCOVERAGE_TEST_MARKER();
32143289
}
32153290
}
3216-
kernelEXIT_CRITICAL();
3291+
#if ( configLIGHTWEIGHT_CRITICAL_SECTION == 1 )
3292+
vKernelLightWeightExitCritical();
3293+
#else
3294+
kernelEXIT_CRITICAL();
3295+
#endif
32173296

32183297
traceRETURN_vTaskPreemptionDisable();
32193298
}
@@ -3226,10 +3305,15 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
32263305
void vTaskPreemptionEnable( const TaskHandle_t xTask )
32273306
{
32283307
TCB_t * pxTCB;
3308+
UBaseType_t uxDeferredAction = 0U;
32293309

32303310
traceENTER_vTaskPreemptionEnable( xTask );
32313311

3232-
kernelENTER_CRITICAL();
3312+
#if ( configLIGHTWEIGHT_CRITICAL_SECTION == 1 )
3313+
vKernelLightWeightEnterCritical();
3314+
#else
3315+
kernelENTER_CRITICAL();
3316+
#endif
32333317
{
32343318
if( xSchedulerRunning != pdFALSE )
32353319
{
@@ -3245,20 +3329,8 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
32453329
* preemption was disabled. */
32463330
if( pxTCB->uxDeferredStateChange != 0U )
32473331
{
3248-
if( pxTCB->uxDeferredStateChange & tskDEFERRED_DELETION )
3249-
{
3250-
vTaskDelete( xTask );
3251-
}
3252-
else if( pxTCB->uxDeferredStateChange & tskDEFERRED_SUSPENSION )
3253-
{
3254-
vTaskSuspend( xTask );
3255-
}
3256-
else
3257-
{
3258-
mtCOVERAGE_TEST_MARKER();
3259-
}
3260-
3261-
pxTCB->uxDeferredStateChange = 0U;
3332+
/* Capture the deferred action to perform outside critical section */
3333+
uxDeferredAction = pxTCB->uxDeferredStateChange;
32623334
}
32633335
else
32643336
{
@@ -3282,7 +3354,28 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
32823354
mtCOVERAGE_TEST_MARKER();
32833355
}
32843356
}
3285-
kernelEXIT_CRITICAL();
3357+
#if ( configLIGHTWEIGHT_CRITICAL_SECTION == 1 )
3358+
vKernelLightWeightExitCritical();
3359+
#else
3360+
kernelEXIT_CRITICAL();
3361+
#endif
3362+
3363+
/* Handle deferred actions outside critical section */
3364+
if( uxDeferredAction != 0U )
3365+
{
3366+
if( uxDeferredAction & tskDEFERRED_DELETION )
3367+
{
3368+
vTaskDelete( xTask );
3369+
}
3370+
else if( uxDeferredAction & tskDEFERRED_SUSPENSION )
3371+
{
3372+
vTaskSuspend( xTask );
3373+
}
3374+
else
3375+
{
3376+
mtCOVERAGE_TEST_MARKER();
3377+
}
3378+
}
32863379

32873380
traceRETURN_vTaskPreemptionEnable();
32883381
}
@@ -3320,7 +3413,8 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
33203413
}
33213414
else
33223415
{
3323-
mtCOVERAGE_TEST_MARKER();
3416+
/* Reset the deferred state change flags */
3417+
pxTCB->uxDeferredStateChange &= ~tskDEFERRED_SUSPENSION;
33243418
}
33253419
#endif /* configUSE_TASK_PREEMPTION_DISABLE */
33263420

@@ -7741,6 +7835,78 @@ static void prvResetNextTaskUnblockTime( void )
77417835
#endif /* #if ( configNUMBER_OF_CORES > 1 ) */
77427836
/*-----------------------------------------------------------*/
77437837

7838+
#if ( configLIGHTWEIGHT_CRITICAL_SECTION == 1 )
7839+
7840+
void vKernelLightWeightEnterCritical( void )
7841+
{
7842+
if( xSchedulerRunning != pdFALSE )
7843+
{
7844+
portDISABLE_INTERRUPTS();
7845+
{
7846+
const BaseType_t xCoreID = ( BaseType_t ) portGET_CORE_ID();
7847+
7848+
/* Get only the ISR lock, not the task lock */
7849+
kernelGET_ISR_LOCK( xCoreID );
7850+
7851+
portINCREMENT_CRITICAL_NESTING_COUNT( xCoreID );
7852+
7853+
if( portGET_CRITICAL_NESTING_COUNT( xCoreID ) == 1U
7854+
#if ( configUSE_TASK_PREEMPTION_DISABLE == 1 )
7855+
/* Check for the run state change of the task only if a deferred state change is not pending */
7856+
&& pxCurrentTCB->uxDeferredStateChange == 0U
7857+
#endif /* ( configUSE_TASK_PREEMPTION_DISABLE == 1 ) */
7858+
)
7859+
{
7860+
prvLightWeightCheckForRunStateChange();
7861+
}
7862+
}
7863+
}
7864+
}
7865+
7866+
#endif /* #if ( configLIGHTWEIGHT_CRITICAL_SECTION == 1 ) */
7867+
/*-----------------------------------------------------------*/
7868+
7869+
#if ( configLIGHTWEIGHT_CRITICAL_SECTION == 1 )
7870+
7871+
void vKernelLightWeightExitCritical( void )
7872+
{
7873+
if( xSchedulerRunning != pdFALSE )
7874+
{
7875+
const BaseType_t xCoreID = ( BaseType_t ) portGET_CORE_ID();
7876+
7877+
if( portGET_CRITICAL_NESTING_COUNT( xCoreID ) > 0U )
7878+
{
7879+
/* Release the ISR lock */
7880+
kernelRELEASE_ISR_LOCK( xCoreID );
7881+
7882+
portDECREMENT_CRITICAL_NESTING_COUNT( xCoreID );
7883+
7884+
BaseType_t xYieldCurrentTask;
7885+
7886+
xYieldCurrentTask = xTaskUnlockCanYield();
7887+
7888+
/* If the critical nesting count is 0, enable interrupts */
7889+
if( portGET_CRITICAL_NESTING_COUNT( xCoreID ) == 0U )
7890+
{
7891+
portENABLE_INTERRUPTS();
7892+
7893+
if( xYieldCurrentTask != pdFALSE
7894+
#if ( configUSE_TASK_PREEMPTION_DISABLE == 1 )
7895+
/* Yield only if no deferred state change is pending */
7896+
&& pxCurrentTCB->uxDeferredStateChange == 0U
7897+
#endif /* ( configUSE_TASK_PREEMPTION_DISABLE == 1 ) */
7898+
)
7899+
{
7900+
portYIELD();
7901+
}
7902+
}
7903+
}
7904+
}
7905+
}
7906+
7907+
#endif /* #if ( configLIGHTWEIGHT_CRITICAL_SECTION == 1 ) */
7908+
/*-----------------------------------------------------------*/
7909+
77447910
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
77457911

77467912
BaseType_t xTaskUnlockCanYield( void )

0 commit comments

Comments
 (0)