Skip to content

procfs: hide host CPUs beyond num_cpus - #336

Merged
congwang-mk merged 3 commits into
mainfrom
num-cpus-sysfs
Oct 30, 2026
Merged

congwang-mk merged 3 commits into
mainfrom
num-cpus-sysfs

Conversation

@congwang-mk

@congwang-mk congwang-mk commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

num_cpus virtualized /proc/cpuinfo and sched_getaffinity, but glibc's sysconf(_SC_NPROCESSORS_ONLN) and sysconf(_SC_NPROCESSORS_CONF) count CPUs from /sys/devices/system/cpu/online and /possible instead. Inside a 2-CPU sandbox on a 12-CPU host:

$ sandlock run -r / --num-cpus 2 -- python3 -c 'import os; print(os.cpu_count(), len(os.sched_getaffinity(0)))'
12 2    # before
2 2     # after

With num_cpus set, /sys/devices/system/cpu/{online,possible,present} now return the same 0-(N-1) list, served by the handler that already serves /proc/cpuinfo.

Why it matters

num_cpus is the determinism knob for CPU count (it does not limit CPU usage; that is cpu_cores for pinning and max_cpu for quota). Builds commonly size parallelism with make -j$(getconf _NPROCESSORS_ONLN), os.cpu_count() or std::thread::available_parallelism fallbacks, and the parallelism can leak into the output. reprotest, the Debian Reproducible Builds checker, has a num_cpus variation for exactly this. Before this change the sandbox still exposed the runner's core count through sysconf, so builds remained non-reproducible across CI runners with different core counts.

Other places the CPU count showed through

Each is a separate commit:

  • /proc/stat kept a line per host CPU. Only the first N cpuN lines are kept now, and the aggregate cpu line is summed from them so usage ratios stay consistent. This covers glibc's fallback when sysfs is unavailable, plus top and psutil.
  • Listing /sys/devices/system/cpu/ showed every host cpuN directory, which older glibc, hwloc and some build scripts count. The getdents filter that hides foreign PIDs in /proc now also drops cpuN entries at or past N there.

Test plan

  • Unit tests: test_generate_cpu_list, test_filter_proc_stat, test_sysfs_cpu_id, test_build_filtered_dirents
  • test_num_cpus_virtualization now also checks getconf _NPROCESSORS_ONLN, getconf _NPROCESSORS_CONF, /sys/devices/system/cpu/present, the cpuN lines in /proc/stat and the cpuN entries listed in /sys/devices/system/cpu; procfs integration suite passes (51 tests)
  • Manual CLI check with Python os.cpu_count()

🤖 Generated with Claude Code

@congwang-mk congwang-mk changed the title procfs: serve the sysfs CPU lists from num_cpus procfs: hide host CPUs beyond num_cpus Oct 29, 2026
num_cpus virtualized /proc/cpuinfo and sched_getaffinity, but glibc's
sysconf(_SC_NPROCESSORS_ONLN) and _NPROCESSORS_CONF count the CPUs in
/sys/devices/system/cpu/online and possible instead. Inside a 2-CPU
sandbox on a 12-CPU host, nproc said 2 while os.cpu_count() and
getconf said 12, so builds that size their parallelism with
make -j$(getconf _NPROCESSORS_ONLN) still saw the host and stayed
non-reproducible across runners. Serve online, possible and present
as the same 0-(N-1) list.

Signed-off-by: Cong Wang <cwang@multikernel.io>
/proc/stat kept a line per host CPU under num_cpus, so code that counts
cpuN lines (glibc's fallback when sysfs is unavailable, top, psutil)
still saw the host's core count. Keep only the first N per-CPU lines and
sum the aggregate cpu line from them, so usage ratios computed from the
total agree with the lines a reader can see.

Signed-off-by: Cong Wang <cwang@multikernel.io>
Code that counts CPUs by listing the cpuN directories under
/sys/devices/system/cpu (older glibc, hwloc, some build scripts) still
saw every host core under num_cpus. Extend the getdents filter that
hides foreign PIDs in /proc to that directory, dropping cpuN entries
at or past N, so the listing agrees with the online, possible and
present lists.

Filter native directory records through a duplicated descriptor so EOF,
seek cookies, rewinds, and cursors shared by dup and fork retain kernel
semantics. Preserve the record layout of both getdents variants and
restore the cursor when a read cannot be delivered. Cover both variants
with regression tests for iteration, seeking, fd reuse, and read errors.

Signed-off-by: Cong Wang <cwang@multikernel.io>
@congwang-mk
congwang-mk merged commit 6356a76 into main Oct 30, 2026
25 checks passed
@congwang-mk
congwang-mk deleted the num-cpus-sysfs branch October 30, 2026 17:13
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.

1 participant