Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass core ID to critical nesting count macros #1206

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

felixvanoost
Copy link

@felixvanoost felixvanoost commented Dec 12, 2024

Description

Passes the core ID as an argument to the critical nesting count macros port{GET/SET/INCREMENT/DECREMENT}_CRITICAL_NESTING_COUNT in tasks.c. This allows the functions that use these macros to read and store the core ID once per call using portGET_CORE_ID() when using FreeRTOS SMP, thereby reducing the number of unnecessary, and generally slow, accesses to a peripheral register. I have summarised the worst-case number of calls to portGET_CORE_ID() before and after these changes:

Function # of calls (before) # of calls (after)
prvCheckForRunStateChange() 4 1
prvYieldForTask() 5 1
vTaskSwitchContext() 2 1
vTaskYieldWithinAPI() 2 1
vTaskEnterCritical() 3 1
vTaskEnterCriticalFromISR() 2 1
vTaskExitCritical() 4 1
vTaskExitCriticalFromISR() 4 1

The execution time of each of the above functions has been reduced on the architecture I tested (an ARM Cortex-M7), though I expect this will yield similar improvements on most ISAs. Further improvements are possible by also passing the core ID to the port{GET/RELEASE}_{ISR/TASK}_LOCK}() macros, but this involves changes to the port API so I haven't included them here.

Test Steps

I compared the execution time of the functions listed in the table above using ETM trace on an NXP S32K396 microcontroller running FreeRTOS v11.1 in SMP mode. I looked at the execution time for 10 random calls to each function while the system is running:

Function Mean execution time (before) Mean execution time (after) % reduction
prvCheckForRunStateChange() 768 ns 769 ns 0%
prvYieldForTask() 2995 ns 3050 ns -2%
vTaskSwitchContext() 11963 ns 11055 ns 8%
vTaskEnterCritical() 4936 ns 4695 ns 5%
vTaskEnterCriticalFromISR() 3095 ns 2799 ns 10%
vTaskExitCritical() 5342 ns 5352 ns 0%
vTaskExitCriticalFromISR() 3974 ns 2665 ns 33%

I was not able to get individual results for vTaskYieldWithinAPI() because it calls some of the other functions above. For prvCheckForRunStateChange() and prvYieldForTask(), I noticed that the number of calls to portGET_CORE_ID() on my system were the same as before, so my code is not branching to the worst-case path. These are average-case results on my particular project and not definitive, but certain functions do see a clear improvement.

These changes are made under the assumption that the core ID cannot change within the context of each function, which I believe is true.

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

#1204

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

Copy link
Member

@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.

I give my initial review suggest here. I will feedback my further suggestion about the followings topic:

  • portXXX_CRITICAL_NESTING_COUNT prototype change. Currently, only RP2040 supports portCRITICAL_NESTING_IN_TCB = 0. We will discuss the backward compatibility problem and update in this PR.
  • vTaskYieldWithinAPI() implementation. The critical nesting count should be acquired with interrupt disabled. It would be great if we can also fix this in the PR.

@@ -855,7 +856,7 @@ static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB ) PRIVILEGED_FUNCTION;
portGET_TASK_LOCK();
portGET_ISR_LOCK();

portSET_CRITICAL_NESTING_COUNT( uxPrevCriticalNesting );
portSET_CRITICAL_NESTING_COUNT( xCoreID, uxPrevCriticalNesting );
Copy link
Member

Choose a reason for hiding this comment

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

The running state of the task may be altered after enabling or disabling interrupts. We need to obtain the core ID again to determine which core the task is currently running on.

Copy link
Author

Choose a reason for hiding this comment

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

Done!

tasks.c Outdated
@@ -3866,6 +3868,7 @@ void vTaskSuspendAll( void )
#else /* #if ( configNUMBER_OF_CORES == 1 ) */
{
UBaseType_t ulState;
const BaseType_t xCoreID = ( BaseType_t ) portGET_CORE_ID();
Copy link
Member

Choose a reason for hiding this comment

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

The xCoreID must be acquired with interrupts disabled to prevent the task from being scheduled to another core.

Copy link
Author

Choose a reason for hiding this comment

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

Is it sufficient to do this after the call to portSET_INTERRUPT_MASK()?

tasks.c Outdated
@@ -6992,27 +6996,29 @@ static void prvResetNextTaskUnblockTime( void )

void vTaskEnterCritical( void )
{
const BaseType_t xCoreID = ( BaseType_t ) portGET_CORE_ID();
Copy link
Member

Choose a reason for hiding this comment

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

Similar reason above. Suggest to acquire xCoreID with interrupt disabled.

Copy link
Author

Choose a reason for hiding this comment

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

Done!

Copy link

sonarcloud bot commented Dec 13, 2024

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.

2 participants