jcp-energy: RAPL reads each package's energy-status MSR where powercap lists none #625
Merged
dcharlot
merged 2 commits from 2026-11-05 04:52:42 -05:00
jcp-energy/rapl-msr into main 2026-11-04
jcp-energy: RAPL MSR domains are per die on Intel, per package on AMD; die zones are package zones
All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 6s
standards workspaces build + test / joule-ui-rs (joule-ui/joule-ui-rs, joule-ui-rs, linux) (pull_request) Successful in 30s
standards workspaces build + test / jcp-rs (jcp-conformance, jcp/jcp-rs, -p jcp-energy --features nvml, jcp-rs, linux) (pull_request) Successful in 1m2s
standards workspaces build + test / sandbox-rs (sandbox-conformance, sandbox/sandbox-rs, -p sandbox-tee --features amd-kds,intel-pcs, sandbox-rs, linux) (pull_request) Successful in 1m2s
standards workspaces build + test / joulehook-rs (joulehook/joulehook-rs, joulehook-rs, linux) (pull_request) Successful in 1m30s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 1m18s
standards workspaces build + test / mesh-rs (mesh/mesh-rs, -p mesh-engine-gguf -p mesh-openai --features mesh-engine-gguf/gguf,mesh-openai/server, mesh-rs, linux-arm64) (pull_request) Successful in 1m8s
standards workspaces build + test / jouleclaw-rs (-nodebug, jouleclaw/jouleclaw-rs, -p jouleclaw-energy --features rapl,nvml, jouleclaw-rs, linux-arm64, -C target-feature=+fp16) (pull_request) Successful in 3m30s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 6m7s
energy meters on real hardware / sink decode table · per-task energy and the GPU rail on macOS (pull_request) Successful in 2m41s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 1m39s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 9m25s
energy meters on real hardware / RAPL and NVML on jetson-hub (pull_request) Successful in 35s
energy meters on real hardware / sink decode table · RAPL on jetson-hub (NVML surveyed) (pull_request) Successful in 2m22s
The MSR fallback grouped CPUs by physical_package_id alone, but Linux scopes Intel's RAPL counters per die: arch/x86/events/rapl.c gives only AMD and Hygon a per-package package energy counter, and a multi-die Intel part (Cascade Lake-AP) has die-scope RAPL MSRs. Reading each package's first CPU there counts one die, an under-count, the unsafe direction at an energy ceiling. A counter domain is now, on Intel family 6, the pair (physical_package_id, die_id), die 0 where the kernel lists no die_id; on AMD and Hygon it stays the package. Each domain is read on its lowest-numbered CPU and all are summed, under the same rules: every domain or none, a zero unit is no counter, the wrap at 2^32 counts, powercap preferred. rapl_access asks the same domains. A die domain's rail is package-<n>-die-<m> msr where the package has more than one die, package-<n> msr where it has one. The powercap side had the matching gap. On such a part intel_rapl_common.c names each top-level zone "package-%d-die-%d" (the MSRs not package-scope and more than one die per package), one intel-rapl:<n> zone per die, and the package-name rule accepted only package-<n>: the host listed no package. The rule now also accepts package-<digits>-die-<digits>, and nothing looser, so every die zone is summed. The hardware test's oracle follows both rules. Tests: an Intel package with two dies is two domains, each read on its own first CPU and summed, with a one-die package beside it keeping its name; a die whose device file is missing leaves no reader; access over the dies; an AMD package whose CPUs list different die_ids is one domain; an Intel host with no die_id is read per package; the name rule (package-0-die-1 yes; package-, package-0-die-, package-0-core-0, package-0-die-1-die-2 no); a fake two-die powercap tree summing both zones. Passing on Linux x86_64 (default, nvml, jcp-gateway) and on macOS (the CI steps with their greps; clippy clean). Co-Authored-By: Claude Opus 5.5 <[email protected]>
jcp-energy: RAPL reads each package's energy-status MSR where powercap lists none
All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 7s
energy meters on real hardware / RAPL and NVML on jetson-hub (pull_request) Successful in 41s
standards workspaces build + test / mesh-rs (mesh/mesh-rs, -p mesh-engine-gguf -p mesh-openai --features mesh-engine-gguf/gguf,mesh-openai/server, mesh-rs, linux-arm64) (pull_request) Successful in 1m1s
standards workspaces build + test / jcp-rs (jcp-conformance, jcp/jcp-rs, -p jcp-energy --features nvml, jcp-rs, linux) (pull_request) Successful in 1m33s
standards workspaces build + test / joule-ui-rs (joule-ui/joule-ui-rs, joule-ui-rs, linux) (pull_request) Successful in 1m33s
standards workspaces build + test / joulehook-rs (joulehook/joulehook-rs, joulehook-rs, linux) (pull_request) Successful in 1m37s
standards workspaces build + test / sandbox-rs (sandbox-conformance, sandbox/sandbox-rs, -p sandbox-tee --features amd-kds,intel-pcs, sandbox-rs, linux) (pull_request) Successful in 2m12s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 2m47s
energy meters on real hardware / sink decode table · per-task energy and the GPU rail on macOS (pull_request) Successful in 2m49s
energy meters on real hardware / sink decode table · RAPL on jetson-hub (NVML surveyed) (pull_request) Successful in 2m34s
standards workspaces build + test / jouleclaw-rs (-nodebug, jouleclaw/jouleclaw-rs, -p jouleclaw-energy --features rapl,nvml, jouleclaw-rs, linux-arm64, -C target-feature=+fp16) (pull_request) Successful in 3m26s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 5m33s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 6m20s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 7m30s
Some kernels ship without the powercap RAPL driver (Ubuntu without linux-modules-extra, as on some rented bare-metal servers), so /sys/class/powercap lists no package while the counters are there, and the RAPL rung was Unavailable on hosts that can measure. Where powercap lists no package zone, the reader now takes the same counters directly: each package's MSR_PKG_ENERGY_STATUS (0x611, Intel family 6; 0xC001029B, AMD Zen and Hygon) through /dev/cpu/<first cpu of the package>/msr, the 32-bit count converted at the package's own unit (bits 12:8 of the power-unit MSR), so it wraps at 2^32 counts in microjoules. A power-unit MSR that reads zero is no counter (KVM guests read zeros). Powercap is still preferred, a listed package zone this process cannot read is no reason to read the MSRs instead, and the liveness rule is unchanged: every package readable and live, or no figure. rapl_access now asks the MSRs where powercap exposes no package zone, so a host whose MSR device files need CAP_SYS_RAWIO reports NeedsPrivilege rather than Absent; no msr module, an unknown CPU or a zero unit is NoInterface. The hardware test's oracle reads the MSRs itself on such a host, so the status, the gate and the package-only bracket are checked there too. On two bare-metal servers with no powercap zone, as root: a Xeon E-2278G live, 8.85 J over a 300 ms one-thread burn, inside the oracle's bracket; two Xeon Silver 4215R sockets, package 1 read on CPU 8, live, 23.1 J over the same burn. As an unprivileged user (no CAP_SYS_RAWIO) RAPL is Unavailable, its access NeedsPrivilege, and the meter is the default-power estimate. Tests: fake device files (a register's bytes at its offset) cover a two-socket host read on each package's first CPU, the 32-bit wrap, AMD's registers, a zero unit, an unknown CPU, a missing device, a counter that does not read, powercap preferred, the production probe over MSR packages, and the access answer with permission errors injected. Passing on Linux x86_64 (default and nvml) and on macOS (the CI step, with its ioreport grep; jcp-gateway; clippy clean for jcp-energy and jcp-gateway). Co-Authored-By: Claude Opus 5.5 <[email protected]>