 1fe57a5d8e
			
		
	
	1fe57a5d8e
	
	
	
		
			
			This commit fixes a race condition in dual-core SMP mode where in the xStreamBufferSend() makes the xTaskWaitingToSend NULL but it may have already been evaluated to not be NULL by xStreamBufferReceive() running on another core and eventually leading to a crash in tasks.c.
		
			
				
	
	
	
		
			10 KiB
		
	
	
	
	
	
	
	
			
		
		
	
	ESP-IDF Changes
This document is used to track all changes made the to FreeRTOS V10.5.1 source code when adding dual core SMP support or IDF additional features.
Todo: Add these to ESP-IDF docs once v10.5.1 becomes default kernel (IDF-8203)
License Headers
- Added SPDX-FileCopyrightTextandSPDX-FileContributortags to all files to pass ESP-IDF pre-commit checks.
- Changed kernel version tag to FreeRTOS Kernel V10.5.1 (ESP-IDF SMP modified)in all files to indicate files are different from upstream V10.5.1.
Omitted Features
- Removed croutine.candcroutine.h
Changes from Upstream Main Branch not Included in v10.5.1
- Added ...GetStaticBuffers functions that were upstreamed but not included in v10.5.1
Kernel SMP Changes
List of changes to the Vanilla FreeRTOS V10.5.1 kernel in order to support dual-core SMP
Scheduling Behavior Changes
- The kernel now executes two tasks concurrently
- The kernel now creates two IDLE tasks (pinned to each core)
- Tasks can be pinned to either core, or have no affinity (can run on both cores)
- Each core receives a tick interrupt, but only core 0 increments the tick count and unblocks timed out tasks
- Core 0 calls xTaskIncrementTick()
- Core 1 calls xTaskIncrementTickOtherCores()
 
- Core 0 calls 
- Each core independently calls vTaskSwitchContext()to pick the highest priority task it can currently run- In single-core scheduling algorithm taskSELECT_HIGHEST_PRIORITY_TASK()unchanged
- In SMP, prvSelectHighestPriorityTaskSMP()is called. This will select the highest priority ready state task that...- Has a compatible core affinity
- Is not being run by another core
 
 
- In single-core scheduling algorithm 
- Each core can suspend scheduling independently (i.e., vTaskSuspendAll())
Configuration
Following configurations have been added
- Added configNUMBER_OF_CORESto specify the number of cores to build. Can be1for vanilla, or2for SMP, error otherwise
- Disable configUSE_PORT_OPTIMISED_TASK_SELECTIONfor SMP
Data Structure Changes (tasks.c)
The following data fields have been expanded to have configNUMBER_OF_CORES copies:
- pxCurrentTCBs: Each core now has its own currently running task
- xPendingReadyList: Each core has its own list to pend ready tasks if the scheduler is suspended on the core
- xYieldPending: Each core has its own flag to track whether it has a pending yield
- xIdleTaskHandle: Each core now has its own idle task
- uxSchedulerSuspended: Each core can independently suspend scheduling on its core
- ulTaskSwitchedInTime: Each core tracks its own "task switched in" time
Their access is now indexed by a xCoreID if in SMP, or set to 0 in single core.
The following data structures have been added:
- TCB_t.xCoreID: All tasks now store their core affinity in a TCB member. Always set to 0 in single-core
API Additions
The following APIs have been added to support SMP
- xTaskCreatePinnedToCore()and- xTaskCreateStaticPinnedToCore()to create tasks with a core affinity- In single-core, core affinity is ignored. Same behavior as xTaskCreate()
 
- In single-core, core affinity is ignored. Same behavior as 
- xTaskGetCoreID()to get a task's affinity
- Add ForCore()versions of the following API- xTaskGetIdleTaskHandleForCore()
- xTaskGetCurrentTaskHandleForCore()
- ulTaskGetIdleRunTimeCounterForCore()
 
API Modifications
Added the following macros that abstract away single-core and SMP differences:
- taskYIELD_CORE()triggers a particular core to yield
- taskIS_YIELD_REQUIRED()/- taskIS_YIELD_REQUIRED_USING_PRIORITY()check if current core requires a yield after a task is unblocked
- taskIS_AFFINITY_COMPATIBLE()check if a task has compatible affinity
- taskIS_CURRENTLY_RUNNING()/- taskIS_CURRENTLY_RUNNING_ON_CORE()checks if a task is running on either core
- taskCAN_BE_SCHEDULED()checks if an unblocked task can be scheduled on any core
- taskIS_SCHEDULER_SUSPENDED()checks if the scheduler on the current core is suspended
- taskSELECT_HIGHEST_PRIORITY_TASK()selects the highest priority task to execute for the current core
- prvGetTCBFromHandle()updated in SMP to call- xTaskGetCurrentTaskHandle()when the handle is- NULL. Done so for thread safety (in case the current task switches cores at the same time).
The following functions were modified to accommodate SMP behavior:
- prvInitialiseNewTask()- Added xCoreIDargument to pin task on creation
- For single-core, xCoreIDis hard coded to0
 
- Added 
- prvAddNewTaskToReadyList()- Checks if new task can be scheduled on core 1
 
- vTaskDelete()- Checks if the deleted task is currently running on the other core.
- If so, sends a yield to the other core.
 
- vTaskPrioritySet()- Checks if the task is currently running on the both cores, and yields the appropriate core if so
 
- vTaskSuspend()- Checks if the task is currently running on the other core, and yields the other core if so.
 
- prvTaskIsTaskSuspended()- Checks the xPendingReadyListof both cores to see if a task is suspended
 
- Checks the 
- xTaskResumeAll()- Limit catching up of tick counts to core 0 (given only core 0 calls xTaskIncrementTick())
 
- Limit catching up of tick counts to core 0 (given only core 0 calls 
- xTaskIncrementTick()- Limited to core 0
 
- vTaskSwitchContext()- Switches context for current core
 
- xTaskRemoveFromEventList()- Created SMP copy of the function
- Checks if pxEventListhas already been emptied by the other core before removing
- Checks if task can be scheduled on both cores, adds it to the appropriate core's pending list if it can't be scheduled.
 
- Checks if 
 
- Created SMP copy of the function
- vTaskRemoveFromUnorderedEventList()- In SMP, check if the task can be scheduled before adding it to the appropriate list. Whereas in single-core, the scheduler is always suspended thus the unblocked task always goes onto the pending ready list.
 
- eTaskConfirmSleepModeStatus()- Updated logic to determine whether sleep is possible in SMP by checking the status of both cores.
 
- prvCheckTasksWaitingTermination()- Updated logic so that we don't delete tasks on xTasksWaitingTerminationwhich are still currently running on the other core.
 
- Updated logic so that we don't delete tasks on 
- xTaskGetCurrentTaskHandle()- In SMP, the function will now disables interrupts to ensure that the calling task does not switch cores while fetching the current core's TCB.
 
- xTaskGetSchedulerState()- In SMP, the function now disables interrupts to ensure that the calling task does not switch cores while checking its own copy of uxSchedulerSuspended.
 
- In SMP, the function now disables interrupts to ensure that the calling task does not switch cores while checking its own copy of 
- prvAddCurrentTaskToDelayedList()- Added extra check to see if current blocking task has already been deleted by the other core.
 
- xStreamBufferReceive()- Added a critical section for setting xTaskWaitingToReceivetoNULLso that the write is SMP safe.
 
- Added a critical section for setting 
- xStreamBufferSend()- Added a critical section for setting xTaskWaitingToSendtoNULLso that the write is SMP safe.
 
- Added a critical section for setting 
Critical Section Changes
- Granular Locks: The following objects are now given their own spinlocks
- Kernel objects (i.e., tasks.c):xKernelLock
- Each queue: xQueueLock
- Queue Registry: xQueueRegistryLock
- Each event group: xEventGroupLock
- Each stream buffer: xStreamBufferLock
- All timers: xTimerLock
 
- Kernel objects (i.e., 
- Critical sections now target the appropriate spinlocks
- Added missing critical sections for SMP (see ..._SMP_ONLY()critical section calls)
- Queues no longer use queue locks (see queueUSE_LOCKS)- Queues now just use critical sections and skips queue locking
- Queue functions can now execute within a single critical section block
 
Single Core Differences
List of differences between Vanilla FreeRTOS V10.5.1 and building the dual-core SMP kernel with congigNUMBER_OF_CORES == 1.
- prvAddNewTaskToReadyList()- Extended critical section so that SMP can check for yields while still inside critical section
 
- vTaskStepTick()- Extended critical section so that SMP can access xTickCountwhile still inside critical section
 
- Extended critical section so that SMP can access 
Header File & Doxygen Changes
List of changes made to Vanilla FreeRTOS V10.5.1 header files to allow for building in ESP-IDF documentation build system.
- 
Removed leading header name line (e.g., xxx.h) in doxygen comment blocks. For example:/** * xxx.h * * Documentation from some func */ void some_func(void);
- 
Removed leading @code{c}blocks in containing redundant function prototypes. For example:/** * @code{c} * void some_func(int var_a, int var_b); * @endcode * * Documentation from some func */ void some_func(int var_a, int var_b);
- 
Added /** @cond !DOC_EXCLUDE_HEADER_SECTION */and/** @endcond */labels to exclude various doxygen sections from being included into documentation builds. These excluded sections include:- In doxygen blocks that describe multiple related set of functions/macros, only the function/macro that matches the doxygen blocks parameters is included. For example:
/** * Description that covers both some_func() and some_func_extra() * * @param var_a var_a description * @param var_b var_b description */ /** @cond !DOC_EXCLUDE_HEADER_SECTION */ #define some_func(var_a) #define some_func_generic(var_a, NULL) /** @endcond */ #define some_func_extra(var_a, var_b) #define some_func_generic(var_a, var_b)
- In functions/macros that are not meant to be directly called by users (i.e., internal), such as the various Genericvariants of functions
 
- In doxygen blocks that describe multiple related set of functions/macros, only the function/macro that matches the doxygen blocks parameters is included. For example:
- 
Some types/functions/macros are manually documented, thus are documented with regular comment blocks (i.e., /* */) instead of doxygen comment blocks (i.e.,/** */). Some of these blocks are changed into doxygen blocks.
Changes backported to IDF-FreeRTOS Kernel from upstream kernel beyond v10.5.1 LTS release
tasks.c
- Backported a change where the IDLE tasks are created with the core ID as a suffix in the task name.
timers.c
- Backported configTIMER_SERVICE_TASK_CORE_AFFINITY config option to enable configurability of the Timer Service task's core affinity.
- The change also entails updating the task creation APIs to use IDF-FreeRTOS task creation APIs, adding a assert check for valid affinity values and dropping the use of configUSE_CORE_AFFINITY.