Skip to content

Extend cpu affinity mask to support up to 64 cores#286

Open
shegazyy wants to merge 3 commits into
eclipse-score:mainfrom
Valeo-S-CORE-Organization:fix/extend_runmask_to_64_cores
Open

Extend cpu affinity mask to support up to 64 cores#286
shegazyy wants to merge 3 commits into
eclipse-score:mainfrom
Valeo-S-CORE-Organization:fix/extend_runmask_to_64_cores

Conversation

@shegazyy

@shegazyy shegazyy commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

The cpu_mask_ field in OsalConfig and the setaffinity() function used uint32_t, limiting process affinity to at most 32 cores. Systems with more than 32 cores would silently use only the lower 32 bits.

Change the mask type to uint64_t throughout the affected chain:

  • OsalConfig::cpu_mask_ (iprocess.hpp)
  • setaffinity() signature (set_affinity.hpp and both platform impls)
  • kDefaultProcessorAffinityMask() return type (configuration_manager)
  • kDefaultNumCores cap raised from 32 to 64 (num_cores.hpp)

On Linux the loop variable and shift literal are updated to uint64_t / 1ULL to avoid the undefined behaviour for i >= 32 noted in the original review discussion (PR #113).

On QNX the 32-bit ThreadCtl(_NTO_TCTL_RUNMASK_GET_AND_SET) is replaced with _NTO_TCTL_RUNMASK64_GET_AND_SET which accepts a uint64_t mask.

kDefaultProcessorAffinityMask() guards against the n == 64 edge case (where 1ULL << 64 would itself be UB) by returning UINT64_MAX instead.

The XML configuration reader is updated to use std::stoull so the full 64-bit coremask value is parsed without truncation.

Closes #122

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

License Check Results

🚀 The license check job ran with the Bazel command:

bazel run --lockfile_mode=error //:license-check

Status: ⚠️ Needs Review

Click to expand output
[License Check Output]
Extracting Bazel installation...
Starting local Bazel server (8.6.0) and connecting to it...
INFO: Invocation ID: 844b6a73-507d-4339-b1ef-421394ccddfc
Computing main repo mapping: 
Computing main repo mapping: 
Loading: 
Loading: 0 packages loaded
Loading: 0 packages loaded
Loading: 0 packages loaded
    currently loading: 
Loading: 0 packages loaded
    currently loading: 
Loading: 0 packages loaded
    currently loading: 
Analyzing: target //:license-check (1 packages loaded, 0 targets configured)
Analyzing: target //:license-check (1 packages loaded, 0 targets configured)

Analyzing: target //:license-check (31 packages loaded, 10 targets configured)

Analyzing: target //:license-check (77 packages loaded, 10 targets configured)

Analyzing: target //:license-check (86 packages loaded, 10 targets configured)

Analyzing: target //:license-check (144 packages loaded, 2767 targets configured)

Analyzing: target //:license-check (152 packages loaded, 6259 targets configured)

Analyzing: target //:license-check (153 packages loaded, 6916 targets configured)

Analyzing: target //:license-check (164 packages loaded, 8259 targets configured)

Analyzing: target //:license-check (164 packages loaded, 8271 targets configured)

Analyzing: target //:license-check (164 packages loaded, 8271 targets configured)

Analyzing: target //:license-check (164 packages loaded, 8271 targets configured)

Analyzing: target //:license-check (164 packages loaded, 8271 targets configured)

Analyzing: target //:license-check (168 packages loaded, 10283 targets configured)

INFO: Analyzed target //:license-check (169 packages loaded, 10409 targets configured).
[12 / 16] JavaToolchainCompileClasses external/rules_java+/toolchains/platformclasspath_classes; 0s disk-cache, processwrapper-sandbox ... (2 actions running)
[14 / 16] JavaToolchainCompileBootClasspath external/rules_java+/toolchains/platformclasspath.jar; 0s disk-cache, processwrapper-sandbox
INFO: Found 1 target...
[16 / 16] no actions running
Target //:license.check.license_check up-to-date:
  bazel-bin/license.check.license_check
  bazel-bin/license.check.license_check.jar
INFO: Elapsed time: 26.639s, Critical Path: 2.72s
INFO: 16 processes: 12 internal, 3 processwrapper-sandbox, 1 worker.
INFO: Build completed successfully, 16 total actions
INFO: Running command line: bazel-bin/license.check.license_check ./formatted.txt <args omitted>
usage: org.eclipse.dash.licenses.cli.Main [-batch <int>] [-cd <url>]
       [-confidence <int>] [-ef <url>] [-excludeSources <sources>] [-help] [-lic
       <url>] [-project <shortname>] [-repo <url>] [-review] [-summary <file>]
       [-timeout <seconds>] [-token <token>]

@shegazyy

shegazyy commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Hey @NicolasFussberger 🙏 ready for review whenever you have a moment!

@NicolasFussberger

Copy link
Copy Markdown
Contributor

Hey @NicolasFussberger 🙏 ready for review whenever you have a moment!

Thanks! I requested @SimonKozik to do a review, he already had a closer look at this some time in the past.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

The created documentation from the pull request is available at: docu-html

@SimonKozik SimonKozik left a comment

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.

Looks OK

@shegazyy shegazyy temporarily deployed to workflow-approval July 6, 2026 13:57 — with GitHub Actions Inactive
@shegazyy shegazyy temporarily deployed to workflow-approval July 6, 2026 13:57 — with GitHub Actions Inactive
@NicolasFussberger

Copy link
Copy Markdown
Contributor

The qnx8 pipeline fails with

 score/launch_manager/src/daemon/src/osal/details/qnx/set_affinity.cpp: In function 'int32_t score::lcm::internal::osal::setaffinity(uint64_t)':
score/launch_manager/src/daemon/src/osal/details/qnx/set_affinity.cpp:30:27: error: '_NTO_TCTL_RUNMASK64_GET_AND_SET' was not declared in this scope
   30 |     return 0 == ThreadCtl(_NTO_TCTL_RUNMASK64_GET_AND_SET, &cpumask) ? 0 : -1;
      |  

I also could not find a reference to _NTO_TCTL_RUNMASK64_GET_AND_SET.

But I think also the runmask support in qnx8 changed significantly with the introduction of cpu clusters.
Do you have any suggestion how to proceed here?

@NicolasFussberger NicolasFussberger left a comment

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.

see above

@shegazyy shegazyy requested a deployment to workflow-approval July 6, 2026 15:38 — with GitHub Actions Waiting
@shegazyy shegazyy requested a deployment to workflow-approval July 6, 2026 15:38 — with GitHub Actions Waiting
@shegazyy

shegazyy commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for catching this @NicolasFussberger 🙏 turns out _NTO_TCTL_RUNMASK64_GET_AND_SET is a QNX 7.x constant that got removed in QNX 8 when the CPU cluster model changed. Added an #ifdef guard so QNX 7 keeps the 64-bit path and QNX 8 falls back to the 32-bit mask (functionally no worse than before this PR). Full 64-core support on QNX 8 with the cluster API can be a follow-up issue. Should fix the QNX 8 build!

Comment thread score/launch_manager/src/daemon/src/osal/details/qnx/set_affinity.cpp Outdated
shegazyy added 2 commits July 8, 2026 15:28
The cpu_mask_ field in OsalConfig and the setaffinity() function used
uint32_t, limiting process affinity to at most 32 cores. Systems with
more than 32 cores would silently use only the lower 32 bits.

Change the mask type to uint64_t throughout the affected chain:
- OsalConfig::cpu_mask_ (iprocess.hpp)
- setaffinity() signature (set_affinity.hpp and both platform impls)
- kDefaultProcessorAffinityMask() return type (configuration_manager)
- kDefaultNumCores cap raised from 32 to 64 (num_cores.hpp)

On Linux the loop variable and shift literal are updated to uint64_t /
1ULL to avoid the undefined behaviour for i >= 32 noted in the original
review discussion (PR eclipse-score#113).

On QNX the 32-bit ThreadCtl(_NTO_TCTL_RUNMASK_GET_AND_SET) is replaced
with _NTO_TCTL_RUNMASK64_GET_AND_SET which accepts a uint64_t mask.

kDefaultProcessorAffinityMask() guards against the n == 64 edge case
(where 1ULL << 64 would itself be UB) by returning UINT64_MAX instead.

The XML configuration reader is updated to use std::stoull so the full
64-bit coremask value is parsed without truncation.

Closes eclipse-score#122
_NTO_TCTL_RUNMASK_GET_AND_SET returns EINVAL on systems with more
than 32 CPUs. Use _NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT instead,
which accepts a struct _thread_runmask with RMSK_SIZE/RMSK_SET macros
to support up to 64 CPUs across multiple CPU clusters.

Issue: eclipse-score#122
@shegazyy shegazyy force-pushed the fix/extend_runmask_to_64_cores branch from 3966871 to 195e81f Compare July 8, 2026 12:33
@shegazyy shegazyy temporarily deployed to workflow-approval July 8, 2026 12:33 — with GitHub Actions Inactive
@shegazyy shegazyy temporarily deployed to workflow-approval July 8, 2026 12:33 — with GitHub Actions Inactive
@shegazyy

shegazyy commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Hey @NicolasFussberger 🙏 reworked it to use _NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT with the multi-cluster runmask struct, so full 64-core support on QNX 8 with no QNX 7 dead code. Ready for another look!

@shegazyy shegazyy requested a deployment to workflow-approval July 13, 2026 10:50 — with GitHub Actions Waiting
@shegazyy shegazyy requested a deployment to workflow-approval July 13, 2026 10:50 — with GitHub Actions Waiting
{
if (cpumask & (1ULL << i))
{
RMSK_SET(static_cast<int>(i), tm.runmask);

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.

I tried this out and the code was working for > 32 cpus 👍

I wonder if we should also set the inheritmask here, otherwise only the main thread of the child process gets the affinity. Any further threads created by the child process will not get this affinity.

Suggested change
RMSK_SET(static_cast<int>(i), tm.runmask);
RMSK_SET(static_cast<int>(i), tm.runmask);
RMSK_SET(static_cast<int>(i), tm.inherit_mask);

This is what I assume is the wanted behaviour from an integrators point or view and I think this is also the behavior of the linux implementation of this method

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

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

Improvement: Extend LaunchManager to support runmask for 64 cores

3 participants