Skip to content

Feature/smp granular locks v4#1154

Draft
sudeep-mohanty wants to merge 33 commits into
FreeRTOS:mainfrom
sudeep-mohanty:feature/smp_granular_locks_v4
Draft

Feature/smp granular locks v4#1154
sudeep-mohanty wants to merge 33 commits into
FreeRTOS:mainfrom
sudeep-mohanty:feature/smp_granular_locks_v4

Conversation

@sudeep-mohanty
Copy link
Copy Markdown
Contributor

@sudeep-mohanty sudeep-mohanty commented Oct 9, 2024

This PR adds support for granular locking to the FreeRTOS kernel.

Description

Granular locking introduces the concept of having localized locks per kernel data group for SMP configuration. This method is an optional replacement of the existing kernel locks and is controlled by a new port layer configuration, viz., portUSING_GRANULAR_LOCKS. More details about the approach can be found here.

Test Steps

The implementation has been tested on Espressif SoC targets, viz., the ESP32 using the ESP-IDF framework.

1. Testing on an esp32 target

  • To test the implementation of the granular locking scheme on esp32, setup the ESP-IDF environment on your local machine. The steps to follow are listed in the Getting Started Guide.
  • Instead of the main ESP-IDF repository, the granular locks implementation resides in this froked repository - https://github.com/sudeep-mohanty/esp-idf.
  • Once you have cloned the forked repository and setup the ESP-IDF environment, change your branch to feat/granular_locks_tests.
  • To run the FreeRTOS unit tests, change the directory to components/freertos/test_apps/freertos where all the test cases are located.
  • Performance tests are located in the performance subfolder at the same location.
  • Setup the target device with the command idf.py seet-target esp32.
  • Select the Amazon FreeRTOS SMP kernel using the menuconfig options. To do this you must enter the command -idf.py menuconfig -> Component config -> FreeRTOS -> Kernel -> Run the Amazon SMP FreeRTOS kernel instead (FEATURE UNDER DEVELOPMENT). Save the configuration and exit the menuconfig.
  • Now, build and flash the unit test application with the command idf.py build flash monitor.
  • Once the app is up, you can enter the number of the test case and run the unity test case.

TODO

  • Test setup for other targets (Raspberry Pi Pico)
  • Generic target tests to be uploaded to the FreeRTOS repository.

Checklist:

  • I have tested my changes. No regression in existing tests.
  • I have modified and/or added unit-tests to cover the code changes in this Pull Request.

Related Issue

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@sudeep-mohanty
Copy link
Copy Markdown
Contributor Author

@chinglee-iot @aggarg This PR introduces the granular locks changes to the FreeRTOS kernel. Please have a look and we could have discussions/changes in this PR context. Thank you.

cc: @ESP-Marius @Dazza0

@rawalexe
Copy link
Copy Markdown
Member

rawalexe commented Oct 9, 2024

Thank you for your contribution, I'll forward this request to the team. There are few error in the PR can you please try fixing them

@rawalexe
Copy link
Copy Markdown
Member

Hello @sudeep-mohanty, I am just following up if you had time to fix the build issues

@sudeep-mohanty sudeep-mohanty marked this pull request as draft October 15, 2024 08:25
@sudeep-mohanty
Copy link
Copy Markdown
Contributor Author

@rawalexe Yes! I shall work on the failures and would also do some refactoring for an easier review process. For now, I've put this PR in draft.

@sudeep-mohanty sudeep-mohanty force-pushed the feature/smp_granular_locks_v4 branch 9 times, most recently from 0159a4a to 5bf8c33 Compare October 30, 2024 10:26
@sudeep-mohanty sudeep-mohanty marked this pull request as ready for review October 30, 2024 10:34
@sudeep-mohanty sudeep-mohanty requested a review from a team as a code owner October 30, 2024 10:34
@ActoryOu
Copy link
Copy Markdown
Member

Hi @sudeep-mohanty,
Could you help check CI failing?

@sudeep-mohanty sudeep-mohanty force-pushed the feature/smp_granular_locks_v4 branch from 5bf8c33 to 9f8acc7 Compare October 31, 2024 09:25
@sudeep-mohanty
Copy link
Copy Markdown
Contributor Author

Hi @sudeep-mohanty, Could you help check CI failing?

Hi @ActoryOu, I've made some updates which should fix the CI failures however I could not understand why the link-verifier action fails. Seems more like a script failure to me. So this action would still fail. If you have more information on what is causing it, could you let me know and I shall fix it. Thanks.

@sudeep-mohanty
Copy link
Copy Markdown
Contributor Author

sudeep-mohanty commented Nov 1, 2024

Created a PR to fix the CI failure - FreeRTOS/FreeRTOS#1292

@sudeep-mohanty sudeep-mohanty force-pushed the feature/smp_granular_locks_v4 branch 4 times, most recently from 70eb466 to edc1c98 Compare November 4, 2024 15:38
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Nov 4, 2024

@sudeep-mohanty sudeep-mohanty marked this pull request as draft November 15, 2024 14:25
Comment thread timers.c Outdated
@sudeep-mohanty sudeep-mohanty force-pushed the feature/smp_granular_locks_v4 branch from b20d191 to 1b91c54 Compare October 13, 2025 08:34
prvLockQueue() and prvUnlockQueue() helper functions now only take the
ISR locks because they are always called with the task lock already
taken. However, these functions must also increment and decrement the
critical nesting count as both locks get taken eventually. This commit
fixes it.
…itical sections

Stream buffer task lists (xTaskWaitingToSend and xTaskWaitingToReceive)
are updated without critical sections. These objects are shared objects
and can be updated from an ISR context as well. Hence, these lists must
always be updated in critical sections.
This commit adds support for direct transfer for queue operations. A ew
config option configQUEUE_DIRECT_TRANSFER is introduced to enable this
option. With this feature, we enable directly copying data to a waiting
task's buffer and avoid copying data to the queue buffer. This helps in
avoiding priority inversions where a lower priority task can steal
a data from a higher priority task blocked on the queue. This also
reduces a copy operation and helps with the queue performance.
…ask TCB

This commit adds support for queue direct transfer buffer being owned by
the task's TCB. This inherently solves some design issues with the
buffer being owned by the queue's object. On the flip side, this adds
memory cost to the task's TCB.
@sudeep-mohanty sudeep-mohanty force-pushed the feature/smp_granular_locks_v4 branch from 5830f5c to e850728 Compare December 3, 2025 10:28
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Dec 3, 2025

@sudeep-mohanty sudeep-mohanty force-pushed the feature/smp_granular_locks_v4 branch from 2555192 to 5463443 Compare January 31, 2026 08:16
When TCB locks are used, we observe a race condition before taking
a deferred action which can result in a task being orphaned and hence
does not undergo the deferred action. THis can lead to memory leaks.
This commit fixes the issue by ensuring that we do not allow a task to
yield when a deferred action is pending.
@sudeep-mohanty sudeep-mohanty force-pushed the feature/smp_granular_locks_v4 branch from 5463443 to d33a460 Compare February 9, 2026 10:09
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Feb 9, 2026

…evelopment

Integrate latest granular lock reconciliation changes from the development
branch (chinglee-iot/FreeRTOS-Kernel, branch
dev/update_for_granular_lock_demo_TCB_lock) to establish a common baseline
for continued granular lock development.

Key changes:
- Eliminate configUSE_TCB_DATA_GROUP_LOCK; TCB locks now integral to
  portUSING_GRANULAR_LOCKS
- Convert taskDATA_GROUP_ENTER_CRITICAL to function for run-state checking
- Rename prvTaskPreemptionEnable to xTaskPreemptionEnableWithYieldStatus
- Add prvTaskDataGroupCheckForRunStateChange, prvKernelEnterISROnlyCritical,
  xTaskUnlockCanYield
- Restructure prvYieldCore and prvYieldForTask with TCB spinlock protection
- Add scheduler suspension in event group list walking
- Add queue set container spinlock protection
- Enable single-priority mode with preemption disable
- Expose timer spinlocks for testing

Bug fixes applied during reconciliation:
- Fix typo: vTaskTCBExtiCritical -> vTaskTCBExitCritical
- Fix unused variable in xTaskPreemptionEnableWithYieldStatus
- Fix broken tick hook preprocessor guard
- Fix unused variable in prvNotifyQueueSetContainerFromISR
@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown
Contributor

@chinglee-iot chinglee-iot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Update to adapt to the unit test, C89 and GNU C extension.
  2. Suggest not to include configQUEUE_DIRECT_TRANSFER. We can consider this feature individually.
  3. Suggest to remove configLIGHTWEIGHT_CRITICAL_SECTION.
  4. Documentation for 'prvKernelEnterISROnlyCritical' and 'prvKernelExitISROnlyCritical'. The name of the function may be updated to prvKernelEnterNestedCriticalSection or other to imply this should be called when task lock of a data group is acquired already.
  5. queue prvNotifyQueueSetContainer take only ISR spinlock only
  6. xTaskIncrementTick function update for vApplicationTickHook()
  7. Use macro to reduce lines of code for portBASE_TYPE_ENTER/EXIT_CRITICAL

Comment thread include/task.h
Comment on lines +409 to +416
#if ( portUSING_GRANULAR_LOCKS == 1 )
#define taskDATA_GROUP_UNLOCK( pxTaskSpinlock ) \
( { \
portRELEASE_SPINLOCK( portGET_CORE_ID(), ( portSPINLOCK_TYPE * ) ( pxTaskSpinlock ) ); \
/* Re-enable preemption after releasing the task spinlock. */ \
xTaskPreemptionEnableWithYieldStatus( NULL ); \
} )
#endif /* #if ( portUSING_GRANULAR_LOCKS == 1 ) */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest not to use statement expression GNU C extension.

Comment thread tasks.c
Comment on lines +5753 to 5761
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configUSE_TICK_HOOK == 1 ) )
{
xApplicationTickRequired = pdTRUE;
}
#else
{
vApplicationTickHook();
}
#endif
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configUSE_TICK_HOOK == 1 ) )
{
xApplicationTickRequired = pdTRUE;
}
#else
{
vApplicationTickHook();
}
#endif
#if ( configUSE_TICK_HOOK == 1 )
{
#if ( portUSING_GRANULAR_LOCKS == 1 )
{
xApplicationTickRequired = pdTRUE;
}
#else
{
vApplicationTickHook();
}
#endif
}
#endif

Comment thread tasks.c
Comment on lines +6248 to 6250
traceENTER_xTaskRemoveFromEventList( pxEventList );

BaseType_t xReturn;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declaration before statement to comply with C89.

Suggested change
traceENTER_xTaskRemoveFromEventList( pxEventList );
BaseType_t xReturn;
BaseType_t xReturn;
traceENTER_xTaskRemoveFromEventList( pxEventList );

Comment thread tasks.c
Comment on lines +6271 to +6273
traceENTER_xTaskRemoveFromEventListFromISR( pxEventList );

#if ( configUSE_TICKLESS_IDLE != 0 )
BaseType_t xReturn;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declaration before statement to comply with C89.

Suggested change
traceENTER_xTaskRemoveFromEventListFromISR( pxEventList );
#if ( configUSE_TICKLESS_IDLE != 0 )
BaseType_t xReturn;
BaseType_t xReturn;
traceENTER_xTaskRemoveFromEventListFromISR( pxEventList );

Comment thread tasks.c
Comment on lines +6275 to +6286
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
UBaseType_t uxSavedInterruptStatus;

/* Lock the kernel data group as we are about to access its members */
uxSavedInterruptStatus = kernelENTER_CRITICAL_FROM_ISR();
{
/* If a task is blocked on a kernel object then xNextTaskUnblockTime
* might be set to the blocked task's time out time. If the task is
* unblocked for a reason other than a timeout xNextTaskUnblockTime is
* normally left unchanged, because it is automatically reset to a new
* value when the tick count equals xNextTaskUnblockTime. However if
* tickless idling is used it might be more important to enter sleep mode
* at the earliest possible time - so reset xNextTaskUnblockTime here to
* ensure it is updated at the earliest possible time. */
prvResetNextTaskUnblockTime();
xReturn = prvTaskRemoveFromEventList( pxEventList );
}
#endif
}
else
{
/* The delayed and ready lists cannot be accessed, so hold this task
* pending until the scheduler is resumed. */
listINSERT_END( &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) );
}
kernelEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );
#else
xReturn = prvTaskRemoveFromEventList( pxEventList );
#endif
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declare uxSavedInterruptStatus within brackets.

Suggested change
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
UBaseType_t uxSavedInterruptStatus;
/* Lock the kernel data group as we are about to access its members */
uxSavedInterruptStatus = kernelENTER_CRITICAL_FROM_ISR();
{
/* If a task is blocked on a kernel object then xNextTaskUnblockTime
* might be set to the blocked task's time out time. If the task is
* unblocked for a reason other than a timeout xNextTaskUnblockTime is
* normally left unchanged, because it is automatically reset to a new
* value when the tick count equals xNextTaskUnblockTime. However if
* tickless idling is used it might be more important to enter sleep mode
* at the earliest possible time - so reset xNextTaskUnblockTime here to
* ensure it is updated at the earliest possible time. */
prvResetNextTaskUnblockTime();
xReturn = prvTaskRemoveFromEventList( pxEventList );
}
#endif
}
else
{
/* The delayed and ready lists cannot be accessed, so hold this task
* pending until the scheduler is resumed. */
listINSERT_END( &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) );
}
kernelEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );
#else
xReturn = prvTaskRemoveFromEventList( pxEventList );
#endif
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
{
UBaseType_t uxSavedInterruptStatus;
/* Lock the kernel data group as we are about to access its members */
uxSavedInterruptStatus = kernelENTER_CRITICAL_FROM_ISR();
{
xReturn = prvTaskRemoveFromEventList( pxEventList );
}
kernelEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );
}
#else
{
xReturn = prvTaskRemoveFromEventList( pxEventList );
}
#endif

Comment thread tasks.c
kernelENTER_CRITICAL();
#endif
{
const BaseType_t xCoreID = portGET_CORE_ID();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable.

Suggested change
const BaseType_t xCoreID = portGET_CORE_ID();

Comment thread queue.c
*
* @return pdTRUE if the queue contains no items, otherwise pdFALSE.
*/
static BaseType_t prvIsQueueEmpty( const Queue_t * pxQueue ) PRIVILEGED_FUNCTION;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The queue spinlock state is changed in this function.
Remove const prefix both declaration and implementation.

Suggested change
static BaseType_t prvIsQueueEmpty( Queue_t * pxQueue ) PRIVILEGED_FUNCTION;

Comment thread queue.c
*
* @return pdTRUE if there is no space, otherwise pdFALSE;
*/
static BaseType_t prvIsQueueFull( const Queue_t * pxQueue ) PRIVILEGED_FUNCTION;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The queue spinlock state is changed in this function.
Remove const prefix both declaration and implementation.

Suggested change

Comment thread queue.c
Comment on lines +3686 to +3692
portGET_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) &( pxQueue->pxQueueSetContainer->xTaskSpinlock ) );
portGET_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) &( pxQueue->pxQueueSetContainer->xISRSpinlock ) );
{
xReturn = prvNotifyQueueSetContainerGeneric( pxQueue, pdFALSE );
}
portRELEASE_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) &( pxQueue->pxQueueSetContainer->xISRSpinlock ) );
portRELEASE_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) &( pxQueue->pxQueueSetContainer->xTaskSpinlock ) );
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'prvNotifyQueueSetContainer' is called within queue critical section. Therefore, only ISR lock is required here.

Suggested change
portGET_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) &( pxQueue->pxQueueSetContainer->xTaskSpinlock ) );
portGET_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) &( pxQueue->pxQueueSetContainer->xISRSpinlock ) );
{
xReturn = prvNotifyQueueSetContainerGeneric( pxQueue, pdFALSE );
}
portRELEASE_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) &( pxQueue->pxQueueSetContainer->xISRSpinlock ) );
portRELEASE_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) &( pxQueue->pxQueueSetContainer->xTaskSpinlock ) );
portGET_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) &( pxQueue->pxQueueSetContainer->xISRSpinlock ) );
{
xReturn = prvNotifyQueueSetContainerGeneric( pxQueue, pdFALSE );
}
portRELEASE_SPINLOCK( xCoreID, ( portSPINLOCK_TYPE * ) &( pxQueue->pxQueueSetContainer->xISRSpinlock ) );

Comment thread tasks.c
Comment on lines +1009 to +1012
static void prvKernelEnterISROnlyCritical( void );

static void prvKernelExitISROnlyCritical( void );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest to add internal function documentation here.
These two function are used when enter nested data group and kernel critical section.

Comment thread tasks.c
portBASE_TYPE_ENTER_CRITICAL();
#if ( ( configNUMBER_OF_CORES > 1 ) )
{
kernelENTER_CRITICAL();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SMP FreeRTOS may not be able to benefit from portBASE_TYPE_ENTER/EXIT_CRITICAL due to multiple access from different core at the same time. We can use macro to reduce the line of code here and similar change below.

Comment thread tasks.c
@@ -4907,7 +5680,15 @@ BaseType_t xTaskIncrementTick( void )
* count is being unwound (when the scheduler is being unlocked). */
if( xPendedTicks == ( TickType_t ) 0 )
{
vApplicationTickHook();
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configUSE_TICK_HOOK == 1 ) )
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configUSE_TICK_HOOK == 1 ) )
#if ( portUSING_GRANULAR_LOCKS == 1 )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Granular Lock for SMP

5 participants