FreeRTOS-Kernel icon indicating copy to clipboard operation
FreeRTOS-Kernel copied to clipboard

[BUG] SMP mode - vTaskCoreAffinitySet does not yield

Open WojciechJasko-BB opened this issue 1 year ago • 3 comments

Describe the bug vTaskCoreAffinitySet does not yield when the modified task is in a ready state but not executing.

Scenario: FreeRTOS is running on 4 cores. Core 0 is executing:

for (size_t i = 0u; i < 4U; ++i)
    {
        // TaskHandle_t handle = xTaskCreateStaticAffinitySet(
        //     core_task, "core", MAIN_TASK_SIZE, NULL, MAIN_TASK_PRI, gCoreTaskStack[i], &gCoreTaskObj[i], (1 << i));
        // configASSERT(handle != NULL);
        TaskHandle_t handle = xTaskCreateStatic(
            core_task, "core", MAIN_TASK_SIZE, NULL, MAIN_TASK_PRI, gCoreTaskStack[i], &gCoreTaskObj[i]);
        configASSERT(handle != NULL);

        vTaskCoreAffinitySet(handle, (1 << i));
    }

No other tasks are running.

Outcome: Sometimes one task is not executed (5% times), but it is on ReadyList.

From the code instrumentation, I see that:

  • when the core is executed then if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE ) was true
  • when the core is not executing then both if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE ) and if( ( uxPrevNotAllowedCores & uxCoreAffinityMask ) != 0U ) were false and nobody yield assigned core.

It makes sense because:

  • task was not running (it was in ready state)
  • we did not added a new core

In our case, there was no other job for this core.

if( ( uxPrevNotAllowedCores & uxCoreAffinityMask ) != 0U ) is wrong, because it assumes that the task was blocked (the ready state was forgotten).

WojciechJasko-BB avatar Jun 09 '24 22:06 WojciechJasko-BB

@WojciechJasko-BB The tasks created by xTaskCreateStatic will be scheduled to run when the following conditions are met:

  • The task's core affinity settings permit it to run on the core ( default no core affinity )
  • The task has a higher priority than the currently running task on the core.
  • configUSE_PREEMPTION is enabled.

Restricting the core affinity of a task in the ready state does not alter its state. This is because the task should be able to run already with it's original core affinity mask. Therefore, yield for the task is only required when new core is added to it's core affinity mask.

I use the following code for explanation:

for (size_t i = 0u; i < 4U; ++i)
{
    /* By default, task is created with no core affinity. Task 0 is able to run on core 0, 1, 2, 3.
     * So does task 1, 2 and 3. The only reason that the task is not running in this situation is that
     * it doesn't have higher priority than any current running task.
     * Let's assume that task 0, 1, 2 are running and task 3 is in ready state due to some higher priority
     * task is running on core 3. */
    TaskHandle_t handle = xTaskCreateStatic( core_task, 
                                             "core",
                                             MAIN_TASK_SIZE,
                                             NULL,
                                             MAIN_TASK_PRI,
                                             gCoreTaskStack[i],
                                             &gCoreTaskObj[i]);

    /* Setting core affinity of task 3 to core 3 only doesn't change it's state cause the same reason that
     * core 3 is running a task with higher priority than task 3. Therefore, this task can't be preempted by task 3. */
    vTaskCoreAffinitySet(handle, (1 << i));
}

We will need your help to provide the following information to investigate this problem.

  • Is timer task enabled ( configUSE_TIMERS )? What is the priority of the timer task?
  • What is the current running task on the core when the created task is in ready state?

chinglee-iot avatar Jun 14 '24 12:06 chinglee-iot

@chinglee-iot Is timer task enabled ( configUSE_TIMERS )? What is the priority of the timer task? Yes, timers are enabled but not used. Timer task has piority configMAX_PRIORITIES - 1. It is the same priority as newly created task.

What is the current running task on the core when the created task is in ready state? There is no other task that can be run on that core.

There are 6 task started by user code (excluding timer task). 4x tasks that are assigned per core for counters incrementation. 1 task for printing counters values assigned to core 0. 1 task for serving uart assigned to core 0.

These 4 tasks are created after start of scheduler. Other 2 before.

In 5% cases, one counter is not incrementing. When I check it with debugger, it is on ready list. I also instrumented portYIELD_CORE and it is not called for specific core.

WojciechJasko-BB avatar Jun 17 '24 04:06 WojciechJasko-BB

Thank you for your reply. From your information, it looks like that the task is in ready state and never get scheduled to run in 5% cases and the scheduler doesn't request the core to yield for this task.

I will need more information from you to investigate this problem. Assuming that your task in ready state is supposed to run on core x. Can you help to provide pxCurrentTCBs[ x ] information with your debugger? If you are using GDB, you can just print the *pxCurrentTCBs[ x ] to dump the fields in TCB_t.

Example dump:

(gdb) print *pxCurrentTCBs[0] $1 = {pxTopOfStack = 0x5555555e6390, uxCoreAffinityMask = 18446744073709551615, xStateListItem = {xItemValue = 0, pxNext = 0x5555555e64e0, pxPrevious = 0x555555596140 <pxReadyTasksLists+96>, pvOwner = 0x5555555e6280, pvContainer = 0x555555596130 <pxReadyTasksLists+80>}, xEventListItem = {xItemValue = 5, pxNext = 0x0, pxPrevious = 0x0, pvOwner = 0x5555555e6280, pvContainer = 0x0}, uxPriority = 2, pxStack = 0x5555555e6390, xTaskRunState = 0, uxTaskAttributes = 0, pcTaskName = "SMP Task\000\000\000", xPreemptionDisable = 0, pxEndOfStack = 0x5555555e64a4, uxCriticalNesting = 0, uxTCBNumber = 1, uxTaskNumber = 0, uxBasePriority = 2, uxMutexesHeld = 0, pxTaskTag = 0x0, ulNotifiedValue = {0, 0, 0, 0, 0}, ucNotifyState = "\000\000\000\000", ucDelayAborted = 0 '\000'}

chinglee-iot avatar Jun 18 '24 01:06 chinglee-iot

@WojciechJasko-BB can you help us with further information as requested by @chinglee-iot

amazonKamath avatar Aug 07 '24 11:08 amazonKamath

Hello, sorry for delay. In this run Cores 0, 2, 3 are running, but core 1 stucked in IDLE.

[1]	struct tskTaskControlBlock *	0x0000000080FCE090 {pxTopOfStack=0x0000000080FCD460 {0},uxCoreAffinityMask=18446744073709551615,xStateListItem=...	0x0000000081010208	
	*([1])	struct tskTaskControlBlock	{pxTopOfStack=0x0000000080FCD460 {0},uxCoreAffinityMask=18446744073709551615,xStateListItem=...	0x0000000080FCE090	
		pxTopOfStack	unsigned long *	0x0000000080FCD460 {0}	0x0000000080FCE090	
		uxCoreAffinityMask	unsigned long	18446744073709551615	0x0000000080FCE098	
		xStateListItem	struct ListItem_t	{xItemValue=0,pxNext=0x0000000080FCDFA0 {xItemValue=0,pxNext=0x0000000080FCDDA0 ...,pxPrevious=...,pxPrevious=...	0x0000000080FCE0A0	
			xItemValue	unsigned int	0	0x0000000080FCE0A0	
			pxNext	struct xLIST_ITEM *	0x0000000080FCDFA0 {xItemValue=0,pxNext=0x0000000080FCDDA0 {xItemValue=0,pxNext=...,pxPrevious=...	0x0000000080FCE0A8	
				*(pxNext)	struct xLIST_ITEM	{xItemValue=0,pxNext=0x0000000080FCDDA0 {xItemValue=0,pxNext=0x0000000080FCDEA0 ...,pxPrevious=...,pxPrevious=...	0x0000000080FCDFA0	
					xItemValue	unsigned int	0	0x0000000080FCDFA0	
					pxNext	struct xLIST_ITEM *	0x0000000080FCDDA0 {xItemValue=0,pxNext=0x0000000080FCDEA0 {xItemValue=0,pxNext=...,pxPrevious=...	0x0000000080FCDFA8	
					pxPrevious	struct xLIST_ITEM *	0x0000000080FCE0A0 {xItemValue=0,pxNext=0x0000000080FCDFA0 {xItemValue=0,pxNext=...,pxPrevious=...	0x0000000080FCDFB0	
					pvOwner	void *	0x0000000080FCDF90	0x0000000080FCDFB8	
					pvContainer	struct xLIST *	0x0000000080044B78 {uxNumberOfItems=4,pxIndex=0x0000000080044B88 {xItemValue=4294967295,pxNext=...,xListEnd=...	0x0000000080FCDFC0	
			pxPrevious	struct xLIST_ITEM *	0x0000000080044B88 {xItemValue=4294967295,pxNext=0x0000000080FCE0A0 {xItemValue=...,pxPrevious=...	0x0000000080FCE0B0	
				*(pxPrevious)	struct xLIST_ITEM	{xItemValue=4294967295,pxNext=0x0000000080FCE0A0 {xItemValue=0,pxNext=0x0000000080FCDFA0 ...,pxPrevious=...,pxPrevious=...	0x0000000080044B88	
					xItemValue	unsigned int	4294967295	0x0000000080044B88	
					pxNext	struct xLIST_ITEM *	0x0000000080FCE0A0 {xItemValue=0,pxNext=0x0000000080FCDFA0 {xItemValue=0,pxNext=...,pxPrevious=...	0x0000000080044B90	
					pxPrevious	struct xLIST_ITEM *	0x0000000080FCDEA0 {xItemValue=0,pxNext=0x0000000080044B88 {xItemValue=4294967295,pxNext=...,pxPrevious=...	0x0000000080044B98	
					pvOwner	void *	0x0000000000000000	0x0000000080044BA0	
					pvContainer	struct xLIST *	0x0000000080044BB0 {uxNumberOfItems=4294967295,pxIndex=0x0000000080044BB0 {xItemValue=...,xListEnd=...	0x0000000080044BA8	
			pvOwner	void *	0x0000000080FCE090	0x0000000080FCE0B8	
			pvContainer	struct xLIST *	0x0000000080044B78 {uxNumberOfItems=4,pxIndex=0x0000000080044B88 {xItemValue=4294967295,pxNext=...,xListEnd=...	0x0000000080FCE0C0	
				*(pvContainer)	struct xLIST	{uxNumberOfItems=4,pxIndex=0x0000000080044B88 {xItemValue=4294967295,pxNext=0x0000000080FCE0A0 ...,pxPrevious=...,xListEnd=...	0x0000000080044B78	
					uxNumberOfItems	unsigned long	4	0x0000000080044B78	
					pxIndex	struct xLIST_ITEM *	0x0000000080044B88 {xItemValue=4294967295,pxNext=0x0000000080FCE0A0 {xItemValue=...,pxPrevious=...	0x0000000080044B80	
					xListEnd	struct MiniListItem_t	{xItemValue=4294967295,pxNext=0x0000000080FCE0A0 {xItemValue=0,pxNext=0x0000000080FCDFA0 ...,pxPrevious=...,pxPrevious=...	0x0000000080044B88	
		xEventListItem	struct ListItem_t	{xItemValue=32,pxNext=0x0000000000000000 {xItemValue=0,pxNext=0x0000000000000000 ...,pxPrevious=...,pxPrevious=...	0x0000000080FCE0C8	
			xItemValue	unsigned int	32	0x0000000080FCE0C8	
			pxNext	struct xLIST_ITEM *	0x0000000000000000 {xItemValue=0,pxNext=0x0000000000000000 {xItemValue=0,pxNext=...,pxPrevious=...	0x0000000080FCE0D0	
			pxPrevious	struct xLIST_ITEM *	0x0000000000000000 {xItemValue=0,pxNext=0x0000000000000000 {xItemValue=0,pxNext=...,pxPrevious=...	0x0000000080FCE0D8	
			pvOwner	void *	0x0000000080FCE090	0x0000000080FCE0E0	
			pvContainer	struct xLIST *	0x0000000000000000 {uxNumberOfItems=0,pxIndex=0x0000000000000000 {xItemValue=0,pxNext=...,xListEnd=...	0x0000000080FCE0E8	
		uxPriority	unsigned long	0	0x0000000080FCE0F0	
		pxStack	unsigned long *	0x0000000080FCB590 {11936128518282651045}	0x0000000080FCE0F8	
		xTaskRunState	long	1	0x0000000080FCE100	
		uxTaskAttributes	unsigned long	1	0x0000000080FCE108	
		pcTaskName	unsigned char[32]	[73 'I',68 'D',76 'L',69 'E',51 '3'...]	0x0000000080FCE110	
		uxCriticalNesting	unsigned long	0	0x0000000080FCE130	
		uxTCBNumber	unsigned long	5	0x0000000080FCE138	
		uxTaskNumber	unsigned long	0	0x0000000080FCE140	
		uxBasePriority	unsigned long	0	0x0000000080FCE148	
		uxMutexesHeld	unsigned long	0	0x0000000080FCE150	
		pxTaskTag	long (void *)	0x0000000000000000	0x0000000080FCE158	
		pvThreadLocalStoragePointers	void *[4]	[0x0000000000000000,0x0000000000000000,0x0000000000000000,0x0000000000000000]	0x0000000080FCE160	
		ulRunTimeCounter	unsigned int	1680	0x0000000080FCE180	
		ulNotifiedValue	unsigned int[1]	[0]	0x0000000080FCE184	
		ucNotifyState	unsigned char[1]	[0 '\x00']	0x0000000080FCE188	
		ucStaticallyAllocated	unsigned char	2 '\x02'	0x0000000080FCE189	
		iTaskErrno	int	0	0x0000000080FCE18C	

WojciechJasko-BB avatar Aug 09 '24 09:08 WojciechJasko-BB

@WojciechJasko-BB

Thanks for your information. We want to know if calling the vTaskCoreAffinitySet is the cause to this problem. Can you help to run you code again without setting core affinity and share us the result?

for (size_t i = 0u; i < 4U; ++i)
    {
        // TaskHandle_t handle = xTaskCreateStaticAffinitySet(
        //     core_task, "core", MAIN_TASK_SIZE, NULL, MAIN_TASK_PRI, gCoreTaskStack[i], &gCoreTaskObj[i], (1 << i));
        // configASSERT(handle != NULL);
        TaskHandle_t handle = xTaskCreateStatic(
            core_task, "core", MAIN_TASK_SIZE, NULL, MAIN_TASK_PRI, gCoreTaskStack[i], &gCoreTaskObj[i]);
        configASSERT(handle != NULL);

        /* Comment out this line of code and run the application again. */
        // vTaskCoreAffinitySet(handle, (1 << i));
    }

chinglee-iot avatar Aug 13 '24 06:08 chinglee-iot

Yes it works. What is more, using the API to create task and set it's affinity at once works as well.

WojciechJasko-BB avatar Aug 13 '24 06:08 WojciechJasko-BB

@WojciechJasko-BB Can you help me double confirm your experiment result?

( x ) Sometimes one task is not executed (5% times), but it is on ReadyList.

for (size_t i = 0u; i < 4U; ++i)
    {
        // TaskHandle_t handle = xTaskCreateStaticAffinitySet(
        //     core_task, "core", MAIN_TASK_SIZE, NULL, MAIN_TASK_PRI, gCoreTaskStack[i], &gCoreTaskObj[i], (1 << i));
        // configASSERT(handle != NULL);
        TaskHandle_t handle = xTaskCreateStatic(
            core_task, "core", MAIN_TASK_SIZE, NULL, MAIN_TASK_PRI, gCoreTaskStack[i], &gCoreTaskObj[i]);
        configASSERT(handle != NULL);

        vTaskCoreAffinitySet(handle, (1 << i));
    }

It works well If removing the condition check in vTaskCoreAffinitySet.

    void vTaskCoreAffinitySet( const TaskHandle_t xTask,
                               UBaseType_t uxCoreAffinityMask )
    {
                        ....
                        /* Comment out the following condition. The application works well. 
                        // if( ( uxPrevNotAllowedCores & uxCoreAffinityMask ) != 0U )
                        {
                            prvYieldForTask( pxTCB );
                        }
    }

( O ) No core affinity. It works well. All the counters are increased by the tasks. ( edit to fix code example )

for (size_t i = 0u; i < 4U; ++i)
    {
        // TaskHandle_t handle = xTaskCreateStaticAffinitySet(
        //     core_task, "core", MAIN_TASK_SIZE, NULL, MAIN_TASK_PRI, gCoreTaskStack[i], &gCoreTaskObj[i], (1 << i));
        // configASSERT(handle != NULL);
        TaskHandle_t handle = xTaskCreateStatic(
            core_task, "core", MAIN_TASK_SIZE, NULL, MAIN_TASK_PRI, gCoreTaskStack[i], &gCoreTaskObj[i]);
        configASSERT(handle != NULL);

        /* Comment out this line of code and run the application again. */
        // vTaskCoreAffinitySet(handle, (1 << i));
    }

( O ) Use xTaskCreateStaticAffinitySet to set core affinity when it is created. It works well. All the counters are increased by the tasks.

for (size_t i = 0u; i < 4U; ++i)
    {
        TaskHandle_t handle = xTaskCreateStaticAffinitySet(
             core_task, "core", MAIN_TASK_SIZE, NULL, MAIN_TASK_PRI, gCoreTaskStack[i], &gCoreTaskObj[i], (1 << i));
        // configASSERT(handle != NULL);
        // TaskHandle_t handle = xTaskCreateStatic(
        //    core_task, "core", MAIN_TASK_SIZE, NULL, MAIN_TASK_PRI, gCoreTaskStack[i], &gCoreTaskObj[i]);
        configASSERT(handle != NULL);

        // vTaskCoreAffinitySet(handle, (1 << i));
    }

chinglee-iot avatar Aug 13 '24 07:08 chinglee-iot

@WojciechJasko-BB I am able to reproduce this problem in unit test. In summary, I think your observation about the problem is correct. This problem happens when a core is not able to handle a yield request in time. Handling tasks of ready state in the vTaskCoreAffinitySet implementation can solve the problem. We will create a pull request to fix this issue and update in this issue.

I also share my observation here. We can simplify the problem with creating one task only. In the vMainTask function, a task is created and set the core affinity.

void vMainTask( void * pvParameters )
{
        TaskHandle_t handle = xTaskCreateStatic(
            core_task, "core", MAIN_TASK_SIZE, NULL, MAIN_TASK_PRI, gCoreTaskStack, &gCoreTaskObj);

        vTaskCoreAffinitySet(handle, (1 << 1 ) );
}

Considering the following execute sequence:

  1. The system has 4 cores and main task is the highest priority task running on core 0. The other cores are running an idle task.
  2. main task calls xTaskCreateStatic to create the core task and request core 3 to yield for core task
  3. core 3 is not able to handle the yield request in time so the core task is still in ready state.
  4. main task continue to calls vTaskCoreAffinitySet to change core task core affinity to core 1.
  5. The core task core affinity is limited to core 1. The core task is still in ready state due to step 3 reason. In the current implementation, prvYieldForTask is not called for the core task when limiting core affinity of a blocking task, which result in core 1 is not requested to select core task to run.
  6. core 3 handles the yield request now. Previously, core 3 was supposed to select the core task to run, but due to core affinity being limited to Core 1 only now, core 3 could only select an idle task to run.

The result is that the core task has higher priority than idle task but can't run on core 1, which is probably the situation you observed. This problem is not observed if core 3 is able to handle the yield request before core 1 calls vTaskCoreAffinitySet.

Thank you again for reporting this issue and your patience to help us understanding this problem.

chinglee-iot avatar Aug 15 '24 07:08 chinglee-iot

I am closing this issue as the PR fixing this is merged. Please feel free to re-open if you still face some issue.

aggarg avatar Aug 21 '24 10:08 aggarg