Skip to content

Add configUSE_SCHEDULER_CORE_MASK and vTaskSetSchedulerCoreMask API#1418

Open
kuopinghsu wants to merge 4 commits into
FreeRTOS:mainfrom
kuopinghsu:main
Open

Add configUSE_SCHEDULER_CORE_MASK and vTaskSetSchedulerCoreMask API#1418
kuopinghsu wants to merge 4 commits into
FreeRTOS:mainfrom
kuopinghsu:main

Conversation

@kuopinghsu
Copy link
Copy Markdown

@kuopinghsu kuopinghsu commented May 15, 2026

Add configUSE_SCHEDULER_CORE_MASK and vTaskSetSchedulerCoreMask API

Description

Adds a new SMP-only feature that lets the application control which
cores the FreeRTOS scheduler is permitted to run non-idle tasks on, at run
time, without stopping the scheduler.

The primary use cases are:

  1. Dynamic load management -- exclude one or more cores from scheduling
    non-idle work so they can be idled or throttled without stopping the
    scheduler on the remaining cores.

  2. Core power-off preparation -- before powering off a secondary core
    (cores 1..N-1), clear that core's bit in the scheduler mask so the
    scheduler stops assigning new tasks to it. Once the core's current task
    yields and the core is running only its idle task, the platform power
    management layer can safely gate the clock or cut power to that core.
    Core 0 must never be masked out because it owns the tick and the
    scheduler entry point.

A new configuration macro configUSE_SCHEDULER_CORE_MASK gates the entire
feature. Setting it to 0 produces a clean preprocessor rollback to the
original code with zero overhead.

Files changed:

File Change
tasks.c +113 / -14
include/task.h +38 / 0
include/FreeRTOS.h +8 / 0
examples/template_configuration/FreeRTOSConfig.h +8 / 0

Details:

include/FreeRTOS.h: added default #define configUSE_SCHEDULER_CORE_MASK 1
and a compile-time #error that blocks use on single-core builds.

include/task.h: added declarations for both new API functions under the
combined configNUMBER_OF_CORES > 1 && configUSE_SCHEDULER_CORE_MASK == 1 guard.

tasks.c: added the uxSchedulerCoreMask static variable; added three
#if ( configUSE_SCHEDULER_CORE_MASK == 1 ) checks inside the SMP task
selection loop so that non-idle tasks are never assigned to or kept on a
masked core; added implementations of both API functions.

examples/template_configuration/FreeRTOSConfig.h: added
configUSE_SCHEDULER_CORE_MASK 1 to the SMP section.

Opt-out: set #define configUSE_SCHEDULER_CORE_MASK 0 in FreeRTOSConfig.h.
Every added line is inside a preprocessor guard, so the build is identical
to the unpatched kernel.

New API (available when configNUMBER_OF_CORES > 1 and
configUSE_SCHEDULER_CORE_MASK == 1):

/* Bit N = 1 means core N may run non-idle tasks.
 * Bit N = 0 means core N runs only its idle task.
 * Pass ( tskNO_AFFINITY ) to re-enable all cores. */
void vTaskSetSchedulerCoreMask( UBaseType_t uxCoreMask );

/* Returns the current global scheduler core mask. */
UBaseType_t uxTaskGetSchedulerCoreMask( void );

Usage example -- power off core 2 on a 4-core system:

Note: masking core 0 is valid at the scheduler level (core 0 will just run
only its idle task). What must not be done is powering off the core that
hosts the tick source (typically core 0), because the kernel tick and
scheduler entry point depend on it. Masking is a safe prerequisite step
before powering off any secondary core.

/* Step 1: stop the scheduler from assigning tasks to core 2. */
UBaseType_t uxMask = uxTaskGetSchedulerCoreMask();
uxMask &= ~( ( UBaseType_t ) 1U << 2U );
vTaskSetSchedulerCoreMask( uxMask );

/* Step 2: wait (platform-specific) until core 2 is running only idle. */
waitForCoreIdle( 2 );

/* Step 3: power off core 2 via the BSP. */
bspCorePowerOff( 2 );

/* To bring it back: power on, then re-enable in the mask. */
bspCorePowerOn( 2 );
vTaskSetSchedulerCoreMask( tskNO_AFFINITY );

Test Steps

  1. Build an SMP application with configNUMBER_OF_CORES > 1 and
    configUSE_SCHEDULER_CORE_MASK 1 (default). Confirm it compiles and
    links without warnings.
  2. Call vTaskSetSchedulerCoreMask( 1U ) at run time (only core 0 enabled).
    Confirm that tasks on cores 1..N-1 are no longer scheduled and those
    cores run only their idle tasks.
  3. Call vTaskSetSchedulerCoreMask( tskNO_AFFINITY ). Confirm normal
    scheduling resumes on all cores.
  4. Build with configUSE_SCHEDULER_CORE_MASK 0. Confirm it compiles
    cleanly and that neither vTaskSetSchedulerCoreMask nor
    uxTaskGetSchedulerCoreMask is referenced in the output binary.
  5. Build with configNUMBER_OF_CORES 1 and any non-zero value of
    configUSE_SCHEDULER_CORE_MASK. Confirm the expected #error fires.

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.

Copilot AI review requested due to automatic review settings May 15, 2026 08:08
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an SMP-only “scheduler core mask” feature that lets applications dynamically restrict which cores may run non-idle tasks at runtime (e.g., for load/power management) via a new config gate and two new task APIs.

Changes:

  • Introduces configUSE_SCHEDULER_CORE_MASK (intended to gate the feature at compile time).
  • Adds vTaskSetSchedulerCoreMask() / uxTaskGetSchedulerCoreMask() APIs and integrates the mask into SMP task selection to prevent scheduling non-idle tasks on disabled cores.
  • Updates the template configuration to expose the new option.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
tasks.c Adds a global scheduler core mask, enforces it in SMP scheduling paths, and implements the new APIs.
include/task.h Declares and documents the new APIs under an SMP-only config guard.
include/FreeRTOS.h Adds the new config macro default and a single-core build-time sanity check.
examples/template_configuration/FreeRTOSConfig.h Documents/enables the new config in the SMP section of the template.
Comments suppressed due to low confidence (1)

tasks.c:3144

  • vTaskSetSchedulerCoreMask clamps with ( 1UL << configNUMBER_OF_CORES ) - 1UL, which has the same undefined-shift risk as the static initializer and also ties the width to unsigned long rather than UBaseType_t. Consider adding a width guard (similar to the existing UBaseType_t vs configNUMBER_OF_CORES assert used for core affinity) and computing the mask in a way that avoids undefined shifts.
            uxOldMask = uxSchedulerCoreMask;

            /* Clamp to the number of physical cores so stray bits are ignored. */
            uxSchedulerCoreMask = uxCoreMask & ( UBaseType_t ) ( ( 1UL << configNUMBER_OF_CORES ) - 1UL );


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread include/FreeRTOS.h Outdated
Comment thread tasks.c
Comment thread tasks.c Outdated
Comment thread include/task.h
kuopinghsu and others added 3 commits May 15, 2026 16:34
@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown
Author

@kuopinghsu kuopinghsu left a comment

Choose a reason for hiding this comment

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

Reviewed

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