I ran into this bug while working with FreeRTOS-kernel (master branch, updated yesterday 2021-02-11) on a Raspberry Pi Pico W (SMP enabled).
Apparently new users can’t use links, so this’ll look worse than I wanted it to, but the URLs to the code are useful so they’ll stay in parentheses.
Effectively, the behavior I was seeing was that the same task was being scheduled again, and again, and again, starving all others. Debugging FreeRTOS led me to prvSelectHighestPriorityTask
(https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/7284d84dc88c5aaf2dc8337044177728b8bdae2d/tasks.c#L983
) where I noticed that there were a lot of recent changes due to the SMP work that was merged not too long ago.
Of note, there’s this little explanation and code earlier in the function (https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/7284d84dc88c5aaf2dc8337044177728b8bdae2d/tasks.c#L999
):
/* A new task is created and a running task with the same priority yields
* itself to run the new task. When a running task yields itself, it is still
* in the ready list. This running task will be selected before the new task
* since the new task is always added to the end of the ready list.
* The other problem is that the running task still in the same position of
* the ready list when it yields itself. It is possible that it will be selected
* earlier then other tasks which waits longer than this task.
*
* To fix these problems, the running task should be put to the end of the
* ready list before searching for the ready task in the ready list. */
if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxCurrentTCBs[ xCoreID ]->uxPriority ] ),
&pxCurrentTCBs[ xCoreID ]->xStateListItem ) == pdTRUE )
{
( void ) uxListRemove( &pxCurrentTCBs[ xCoreID ]->xStateListItem );
vListInsertEnd( &( pxReadyTasksLists[ pxCurrentTCBs[ xCoreID ]->uxPriority ] ),
&pxCurrentTCBs[ xCoreID ]->xStateListItem );
}
But, vListInsertEnd
doesn’t actually insert an node at the end of the list. It inserts a node such that it is the last node returned when listGET_OWNER_OF_NEXT_ENTRY
is called to iterate through the list. This node is only the last node if the list’s index points to the head node… which for some reason wasn’t the case for my program (maybe it has to do with the fact I make calls to uxTaskGetSystemState
from a different task for status/debugging purposes). In my program, what I was seeing was that the Ready
list’s index was actually the node right after the head node, meaning that vListInsertEnd was actually inserting the TCB for the current task at the head!
Later in the code, the ready list is iterated using (https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/7284d84dc88c5aaf2dc8337044177728b8bdae2d/tasks.c#L1041
):
for( pxIterator = listGET_HEAD_ENTRY( pxReadyList ); pxIterator != pxEndMarker; pxIterator = listGET_NEXT( pxIterator ) )
And for my program, this always gets the newly re-inserted task as it was apparently placed in the head, due to the index being the next node past the head!
I hacked together a “fix” by using listGET_OWNER_OF_NEXT_ENTRY
to iterate through the list. It seems to work, and I’m unable to crash my application with it.
Seeing as it is very late in my neck of the woods right now, and late night debugging is fraught with errors, I just wanted to make sure I’m interpreting the code correctly. Does this all sound like a bug? If so, I can try to polish up what I have and file a proper issue on Github, and maybe try a PR?
In summary, in some cases the new SMP scheduler routines in the kernel gets confused due to vListInsertEnd
not actually inserting a node to the end of the list if the ready list’s index variable doesn’t point to the head node.