Fix SMP task self void run state change (#984)

* Request a task to yield after been suspended or deleted to prevent this task puts itself back to another list
* Fix volatile variable access order to ensure ensure compliance with MISRA C 2012 Rule 13.5

---------

Signed-off-by: Gaurav Aggarwal <aggarg@amazon.com>
Co-authored-by: Gaurav Aggarwal <aggarg@amazon.com>
This commit is contained in:
chinglee-iot 2024-02-06 20:41:34 +08:00 committed by GitHub
parent 23afc48fc3
commit 57a5ed7f67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

139
tasks.c
View file

@ -2191,6 +2191,7 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
{ {
TCB_t * pxTCB; TCB_t * pxTCB;
BaseType_t xDeleteTCBInIdleTask = pdFALSE; BaseType_t xDeleteTCBInIdleTask = pdFALSE;
BaseType_t xTaskIsRunningOrYielding;
traceENTER_vTaskDelete( xTaskToDelete ); traceENTER_vTaskDelete( xTaskToDelete );
@ -2226,10 +2227,15 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
* not return. */ * not return. */
uxTaskNumber++; uxTaskNumber++;
/* Use temp variable as distinct sequence points for reading volatile
* variables prior to a logical operator to ensure compliance with
* MISRA C 2012 Rule 13.5. */
xTaskIsRunningOrYielding = taskTASK_IS_RUNNING_OR_SCHEDULED_TO_YIELD( pxTCB );
/* If the task is running (or yielding), we must add it to the /* If the task is running (or yielding), we must add it to the
* termination list so that an idle task can delete it when it is * termination list so that an idle task can delete it when it is
* no longer running. */ * no longer running. */
if( ( xSchedulerRunning != pdFALSE ) && ( taskTASK_IS_RUNNING_OR_SCHEDULED_TO_YIELD( pxTCB ) != pdFALSE ) ) if( ( xSchedulerRunning != pdFALSE ) && ( xTaskIsRunningOrYielding != pdFALSE ) )
{ {
/* A running task or a task which is scheduled to yield is being /* A running task or a task which is scheduled to yield is being
* deleted. This cannot complete when the task is still running * deleted. This cannot complete when the task is still running
@ -2261,6 +2267,30 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
#else #else
portPRE_TASK_DELETE_HOOK( pxTCB, &( xYieldPendings[ pxTCB->xTaskRunState ] ) ); portPRE_TASK_DELETE_HOOK( pxTCB, &( xYieldPendings[ pxTCB->xTaskRunState ] ) );
#endif #endif
/* In the case of SMP, it is possible that the task being deleted
* is running on another core. We must evict the task before
* exiting the critical section to ensure that the task cannot
* take an action which puts it back on ready/state/event list,
* thereby nullifying the delete operation. Once evicted, the
* task won't be scheduled ever as it will no longer be on the
* ready list. */
#if ( configNUMBER_OF_CORES > 1 )
{
if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE )
{
if( pxTCB->xTaskRunState == ( BaseType_t ) portGET_CORE_ID() )
{
configASSERT( uxSchedulerSuspended == 0 );
taskYIELD_WITHIN_API();
}
else
{
prvYieldCore( pxTCB->xTaskRunState );
}
}
}
#endif /* #if ( configNUMBER_OF_CORES > 1 ) */
} }
else else
{ {
@ -2284,9 +2314,9 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
/* Force a reschedule if it is the currently running task that has just /* Force a reschedule if it is the currently running task that has just
* been deleted. */ * been deleted. */
if( xSchedulerRunning != pdFALSE )
{
#if ( configNUMBER_OF_CORES == 1 ) #if ( configNUMBER_OF_CORES == 1 )
{
if( xSchedulerRunning != pdFALSE )
{ {
if( pxTCB == pxCurrentTCB ) if( pxTCB == pxCurrentTCB )
{ {
@ -2298,30 +2328,8 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
mtCOVERAGE_TEST_MARKER(); mtCOVERAGE_TEST_MARKER();
} }
} }
#else /* #if ( configNUMBER_OF_CORES == 1 ) */
{
/* It is important to use critical section here because
* checking run state of a task must be done inside a
* critical section. */
taskENTER_CRITICAL();
{
if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE )
{
if( pxTCB->xTaskRunState == ( BaseType_t ) portGET_CORE_ID() )
{
configASSERT( uxSchedulerSuspended == 0 );
taskYIELD_WITHIN_API();
}
else
{
prvYieldCore( pxTCB->xTaskRunState );
}
}
}
taskEXIT_CRITICAL();
} }
#endif /* #if ( configNUMBER_OF_CORES == 1 ) */ #endif /* #if ( configNUMBER_OF_CORES == 1 ) */
}
traceRETURN_vTaskDelete(); traceRETURN_vTaskDelete();
} }
@ -3155,9 +3163,51 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
} }
} }
#endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */
/* In the case of SMP, it is possible that the task being suspended
* is running on another core. We must evict the task before
* exiting the critical section to ensure that the task cannot
* take an action which puts it back on ready/state/event list,
* thereby nullifying the suspend operation. Once evicted, the
* task won't be scheduled before it is resumed as it will no longer
* be on the ready list. */
#if ( configNUMBER_OF_CORES > 1 )
{
if( xSchedulerRunning != pdFALSE )
{
/* Reset the next expected unblock time in case it referred to the
* task that is now in the Suspended state. */
prvResetNextTaskUnblockTime();
if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE )
{
if( pxTCB->xTaskRunState == ( BaseType_t ) portGET_CORE_ID() )
{
/* The current task has just been suspended. */
configASSERT( uxSchedulerSuspended == 0 );
vTaskYieldWithinAPI();
}
else
{
prvYieldCore( pxTCB->xTaskRunState );
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* #if ( configNUMBER_OF_CORES > 1 ) */
} }
taskEXIT_CRITICAL(); taskEXIT_CRITICAL();
#if ( configNUMBER_OF_CORES == 1 )
{
if( xSchedulerRunning != pdFALSE ) if( xSchedulerRunning != pdFALSE )
{ {
/* Reset the next expected unblock time in case it referred to the /* Reset the next expected unblock time in case it referred to the
@ -3173,8 +3223,6 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
mtCOVERAGE_TEST_MARKER(); mtCOVERAGE_TEST_MARKER();
} }
#if ( configNUMBER_OF_CORES == 1 )
{
if( pxTCB == pxCurrentTCB ) if( pxTCB == pxCurrentTCB )
{ {
if( xSchedulerRunning != pdFALSE ) if( xSchedulerRunning != pdFALSE )
@ -3207,43 +3255,6 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
mtCOVERAGE_TEST_MARKER(); mtCOVERAGE_TEST_MARKER();
} }
} }
#else /* #if ( configNUMBER_OF_CORES == 1 ) */
{
/* Enter critical section here to check run state of a task. */
taskENTER_CRITICAL();
{
if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE )
{
if( xSchedulerRunning != pdFALSE )
{
if( pxTCB->xTaskRunState == ( BaseType_t ) portGET_CORE_ID() )
{
/* The current task has just been suspended. */
configASSERT( uxSchedulerSuspended == 0 );
vTaskYieldWithinAPI();
}
else
{
prvYieldCore( pxTCB->xTaskRunState );
}
}
else
{
/* This code path is not possible because only Idle tasks are
* assigned a core before the scheduler is started ( i.e.
* taskTASK_IS_RUNNING is only true for idle tasks before
* the scheduler is started ) and idle tasks cannot be
* suspended. */
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
taskEXIT_CRITICAL();
}
#endif /* #if ( configNUMBER_OF_CORES == 1 ) */ #endif /* #if ( configNUMBER_OF_CORES == 1 ) */
traceRETURN_vTaskSuspend(); traceRETURN_vTaskSuspend();