You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This PR adds WSL2 support to gpuagent via a new gpuagent-wsl binary that links directly against the librocdxg-provided libamd_smi (from the rocm-systems project, installed by rocdxg-amd-smi-lib at /opt/rocm-wsl).
WSL2 hosts expose GPU access through /dev/dxg (the DirectX Graphics kernel interface) rather than /dev/kfd. The bare-metal libamd_smi links against the KFD driver and cannot initialise on WSL2. The librocdxg project ships a WSL2-capable libamd_smi that routes metric queries through the Windows GPU driver via DXCore PMLog escapes. gpuagent-wsl links against that library directly, giving metric coverage on WSL2 development and CI nodes without requiring an LD_PRELOAD shim.
Changes
Build system
New gpuagent_wsl Makefile target (outer and inner) producing gpuagent-wsl; links /opt/rocm-wsl/lib/libamd_smi.so via WSL_AMD_SMI_LIB_DIR (overridable).
gpuagent_wsl_docker target for compile-check inside the RHEL9 container using a stub libamd_smi.so, used by CI.
Makefile.lib: cmake ≥ 4 policy flag (CMAKE_POLICY_VERSION_MINIMUM=3.5), zeromq -Wno-error=address workaround, CC/CXX changed to ?= for host overrides.
wsl-shim/ target for the LD_PRELOAD alternative deployment.
New WSL_AMD_SMI backend (sw/nic/gpuagent/api/smi/wslamdsmi/)
Mirrors the amdsmi/ backend; includes from wsl_amd_smi_lib headers.
wsl2_detected() guard in smi_state::init() — fails fast with a clear error if /dev/dxg is absent or /dev/kfd is present.
smi_wsl_fill_metrics_from_individual_() — populates zero fields in amdsmi_gpu_metrics_t from individual amdsmi calls when the PMLog bulk escape is unavailable or returns partial data (common on WSL2 APU drivers). Applies sentinel values of 1 for power and gfx_activity to prevent device-metrics-exporter's startup filter from permanently blacklisting those fields.
CLK_LIMIT_MIN/MAX compatibility shim for the older librocdxg amdsmi header (v1.2.x vs v27.x).
Direct amdsmi_get_power_info / amdsmi_get_gpu_activity fallbacks in smi_gpu_fill_stats_ for resilience against transient 0 readings.
WSL_AMD_SMI branch added to smi.hpp and smi_api.hpp alongside AMD_SMI and GIM_AMD_SMI.
Headers from librocdxg amdsmi v1.2.x; symlinks to the installed .so.
LD_PRELOAD shim (wsl-shim/)
Alternative for deployments where rebuilding is not feasible; committed and buildable via make wsl-shim.
CI (separate commit)
New build-wsl job: reuses the RHEL9 container image (cached), creates a linker stub for libamd_smi.so, and verifies gpuagent-wsl compiles cleanly.
Build
# Prerequisites: rocdxg-amd-smi-lib installed at /opt/rocm-wsl# Third-party libs must be pre-built (run `make build-libs-docker` once)
make gpuagent_wsl
# → sw/nic/build/x86_64/sim/bin/gpuagent-wsl# Override install path:
make gpuagent_wsl WSL_AMD_SMI_LIB_DIR=/custom/path/lib
Test results (AMD Radeon 8060S, Strix APU, WSL2)
Kernel: 6.18.40.1-microsoft-standard-WSL2
Windows driver: 25.20.18-250917a, rocdxg-amd-smi-lib 1.2.1
gpuagent-wsl starts, detects WSL2 (/dev/dxg present, /dev/kfd absent), discovers 1 GPU, and feeds live metrics to device-metrics-exporter → Prometheus:
Metric
Value
gpu_package_power
~47 W
gpu_power_usage
~47 W
gpu_gfx_activity
~5–20 %
gpu_clock (GFX)
~680–1346 MHz
gpu_clock (MEM)
1000 MHz
gpu_used_vram
~5 GB / 32 GB
gpu_health
1
Expected WSL2 limitations (not regressions): xGMI, PCIe throughput, CPER, temperature, fan, and voltage are unavailable — those require bare-metal KFD or PMLog escapes not wired for APUs on this driver.
The make all / Docker build path is unchanged; gpuagent_wsl is intentionally excluded from make all since it requires a WSL2 host and the rocdxg-amd-smi-lib package.
Adds WSL2 support to gpuagent via a new gpuagent-wsl binary that links
directly against the librocdxg-provided libamd_smi (from the rocm-systems
project, installed by rocdxg-amd-smi-lib at /opt/rocm-wsl).
WSL2 hosts expose GPU access through /dev/dxg (the DirectX Graphics kernel
interface) rather than /dev/kfd. The bare-metal libamd_smi links against
the KFD driver and cannot initialise on WSL2. The librocdxg project ships
a WSL2-capable libamd_smi that routes through the Windows GPU driver via
DXCore escapes. gpuagent-wsl links against that library directly, giving
first-class metric coverage on WSL2 development and CI nodes.
- `Makefile` (root): `gpuagent_wsl` and `gpuagent_wsl_docker` targets.
`gpuagent_wsl` builds natively (no Docker); `gpuagent_wsl_docker` creates
a stub libamd_smi.so inside the RHEL9 container for CI compile checks.
Also adds `wsl-shim` target for the LD_PRELOAD alternative.
- `sw/nic/gpuagent/Makefile`: INC_WSL_SMI, LDFLAGS_WSL_SMI, OBJ_DIR_WSL,
WSL_SMI_SRC, `gpuagent_wsl` target, compile rules with -DWSL_AMD_SMI.
Adds `WSL_AMD_SMI_LIB_DIR ?= /opt/rocm-wsl/lib` override variable.
- `sw/nic/gpuagent/Makefile.lib`: cmake >= 4 policy flag, zeromq
-Wno-error=address workaround, CC/CXX changed to ?= for override.
- `sw/nic/gpuagent/api/smi/wslamdsmi/`: new backend directory.
Same interface as amdsmi/ but includes from wsl_amd_smi_lib and adds:
- `wsl2_detected()` guard in `smi_state::init()` — fails fast if
/dev/dxg is absent or /dev/kfd is present.
- `smi_wsl_fill_metrics_from_individual_()` in smi_utils.hpp —
populates any zero fields in amdsmi_gpu_metrics_t from individual
amdsmi calls (amdsmi_get_gpu_activity, amdsmi_get_power_info,
amdsmi_get_clock_info, amdsmi_get_temp_metric, etc.) when the PMLog
KMD escape is unavailable or returns partial data on WSL2.
Also applies sentinel values of 1 for power and gfx_activity to
prevent device-metrics-exporter from permanently blacklisting those
fields during its startup filter pass (IsNonZeroValue check).
- CLK_LIMIT_MIN/MAX compat shim for the older librocdxg amdsmi header.
- Direct amdsmi_get_power_info and amdsmi_get_gpu_activity fallbacks
in smi_gpu_fill_stats_ when the cached metrics have zero values.
- `sw/nic/gpuagent/api/smi/smi.hpp`, `smi_api.hpp`: WSL_AMD_SMI branch
(alongside AMD_SMI and GIM_AMD_SMI) for aga_gpu_handle_t typedef.
- `sw/nic/third-party/rocm/wsl_amd_smi_lib/`: headers from librocdxg
amdsmi (v1.2.x) and symlinks to the installed libamd_smi.so.1.2.2.
- `wsl-shim/`: LD_PRELOAD shim that redirects amdsmi calls to the WSL
libamd_smi at runtime, for deployments where rebuilding is not feasible.
Updated to use wsl_amd_smi_lib headers.
```
make build-libs-docker
make gpuagent_wsl
make gpuagent_wsl WSL_AMD_SMI_LIB_DIR=/custom/path/lib
```
- AMD Radeon 8060S (Strix APU), WSL2 kernel 6.18.40 (microsoft-standard)
- Windows driver 25.20.18-250917a, rocdxg-amd-smi-lib 1.2.1
- gpuagent-wsl starts, detects WSL2, discovers GPU, feeds metrics to
device-metrics-exporter. Live Prometheus metrics confirmed:
- gpu_package_power: ~47 W
- gpu_power_usage: ~47 W
- gpu_gfx_activity: ~5-20 %
- gpu_clock (GFX): ~680-1346 MHz
- gpu_clock (MEM): 1000 MHz
- gpu_used_vram: ~5 GB
- gpu_health: 1
Expected WSL2 limitations (not bugs):
- No xGMI, no PCIe throughput, no CPER (require bare-metal KFD)
- No temperature (PMLog thermal escape not wired for APUs on this driver)
- No fan, no voltage (APU / driver limitation)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds a second CI job that verifies gpuagent-wsl compiles and links
cleanly. The job:
- Runs after the main `build` job (which provides pre-built third-party
libs via artifact upload)
- Reuses the same RHEL9 build container so no new image build is needed
- Synthesises a minimal stub libamd_smi.so inside the container so the
linker is satisfied without requiring rocdxg-amd-smi-lib to be installed
- Invokes the new `gpuagent_wsl_docker` Makefile target (outer Makefile)
which passes WSL_AMD_SMI_LIB_DIR pointing at the stub
- Uploads gpuagent-wsl binary and build log as artifacts
The resulting binary is a compile/link check only — it will not run on
a non-WSL2 host or without the real librocdxg libamd_smi.
Also adds `gpuagent_wsl_docker` target to the outer Makefile for local
use with `make gpuagent_wsl_docker`.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Stephen Bates <sbates@raithlin.com>
Move ${{ steps.*.outcome }} expressions out of run: into env: so the
values are safe-interpolated as environment variables, not injected
directly into the shell script. Addresses zizmor finding introduced
by security-baseline.yml v1.0.5 (PR ROCm#102).
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Stephen Bates <sbates@raithlin.com>
…: false
Fixes zizmor findings introduced by security-baseline.yml v1.0.5:
- unpinned-uses (error): all uses: tags replaced with commit SHAs
- artipacked (help): persist-credentials: false added to both
actions/checkout steps
These patterns were pre-existing in main and exposed when the
security-baseline was bumped from v1.0.4 to v1.0.5 in PR ROCm#102.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Stephen Bates <sbates@raithlin.com>
…w_block_name_str_
`gpu_fw_block_name_str_()` returned `.c_str()` on a temporary
`std::string` constructed inline, which is undefined behaviour — the
temporary is destroyed at the end of the full-expression, leaving the
returned pointer dangling before any caller can use it.
Fix by storing the formatted string in a `static thread_local
std::string` so the buffer outlives the return statement. Thread-local
storage makes the pattern safe under the concurrent GPU-query threads
that gpuagent spawns.
Flagged by CodeQL on PR ROCm#103 in the `wslamdsmi` backend (copied from
`amdsmi/smi_api.cc` where the same pattern exists pre-existing).
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Stephen Bates <sbates@raithlin.com>
The ?= assignment (introduced to allow native WSL2 builds to override
the compiler via CC=gcc-12 CXX=g++-12 on the command line) broke the
RHEL9 Docker container build. The outer Makefile sets CC := g++ and
exports it; Makefile.lib's ?= honoured that, passing g++ as the C
compiler to cmake's grpc build, which then correctly rejected it:
"The CMAKE_C_COMPILER is set to a C++ compiler"
Revert CC/CXX to hard-coded :=, which overrides the parent export and
keeps the third-party lib builds using the correct gcc/g++. Native WSL2
builds requiring gcc-12 should continue to pass CC=gcc-12 CXX=g++-12
on the make command line as already documented.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Stephen Bates <sbates@raithlin.com>
Nine issues identified in the Copilot code review of PR ROCm#103:
CI / build system:
- Makefile: expand gpuagent_wsl_docker stub from one symbol (amdsmi_init)
to the full set of amdsmi_* functions referenced by the wslamdsmi
objects, so the CI link check actually verifies the binary links cleanly.
- ci.yml: add include-hidden-files: true to the build-libs artifact upload
so the .build_libs_done stamp is included; without it the build-wsl job
rebuilds all third-party deps and can exceed its 30-min timeout.
wslamdsmi/smi_utils.hpp:
- Move the gfx_activity sentinel (value=1) to after all real-data fallbacks
(amdsmi_get_gpu_activity then amdsmi_get_gpu_busy_percent) so that a
real reading is not masked by the sentinel when it succeeds.
- Remove the amdsmi_get_utilization_count fallback: it returns accumulated
counters, not an instantaneous %, and can exceed 100 or wrap to UINT16_MAX
which downstream treats as unavailable.
- Only set structure_size != 0 (marking the struct valid for callers) when
at least one real field was populated; previously it was set unconditionally,
causing callers to publish zero/sentinel values for fields that were never
read (temperature, fan, voltage, PCIe counters).
wslamdsmi/smi_api.cc:
- Zero-initialise all AMD SMI output structs (fw_info, asic_info,
vbios_info, board_info, driver_info, mode) at the point of declaration
so failure paths produce safe empty values rather than uninitialized
stack memory in API strings.
wslamdsmi/smi_state.cc:
- Limit the spinlock cleanup loop in event_monitor to num_gpu_ (discovered
GPU count) rather than AGA_MAX_GPU; entries beyond num_gpu_ have
uninitialized handles and slock fields.
- Stop and join event_monitor_thread_ before amdsmi_shut_down() in
teardown(), matching the existing treatment of watcher_thread_. Without
this, the live event monitor races against the shut-down library.
wsl-shim/wsl_amdsmi_shim.cc:
- Replace the SHIM_FN for amdsmi_set_gpu_clk_range (deprecated) with
amdsmi_set_gpu_clk_limit (the symbol gpuagent actually calls); the old
entry caused clock-limit updates to fall through to the KFD-backed
system library on WSL2 and fail.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Stephen Bates <sbates@raithlin.com>
AMDSMI_VOLT_TYPE_VDDGFX is documented by the vendored header as the GPU/GFX voltage, so assigning its current reading to voltage_soc reports the same sensor as two different rails. Since this API version exposes no SoC voltage sensor, leave voltage_soc unavailable instead of fabricating a SoC measurement.
…tinel ordering
Two findings from the follow-up Copilot review (2026-10-06T19:55):
1. Makefile: gpuagent_wsl_docker stub here-doc was malformed
The inline C stub used `\`-continued lines inside a bash -c string,
causing bash to treat 'STUB' as part of the here-doc body rather than
a delimiter. The stub was never compiled so the CI link check silently
failed. Fix: move the stub to tools/ci/amdsmi_stub.c and reference it
from the Makefile recipe, eliminating the here-doc entirely.
2. smi_utils.hpp: sentinel values invalidated the any_field_populated guard
The exporter sentinels (gfx_activity=1, current_socket_power=1) were
applied before the any_field_populated check, so that check was always
true even when every real amdsmi call failed. Callers then published
zero-initialized fields (temperature, fan, voltage, PCIe, energy) as
real data. Fix: compute any_field_populated from real API results first,
then apply sentinels only when the struct is already known to be valid.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Stephen Bates <sbates@raithlin.com>
These sentinels overwrite legitimate readings: an idle GPU can validly report 0% activity, yet every such sample is exported as 1%. The same mechanism also presents unavailable power as a real 1 W measurement. Preventing the exporter's startup blacklist should be represented as availability metadata or fixed in that filter rather than falsifying every zero-valued metric.
Two findings from the third Copilot review (2026-10-07):
1. wsl-shim/Makefile: install target failed on a fresh host because the
parent directory /usr/local/metrics/lib may not exist. Add
`install -d $(INSTALL_DIR)` before copying the shim binary.
2. wsl-shim/wsl_amdsmi_shim.cc: resolve() fell through to RTLD_NEXT
(the system libamd_smi) when a symbol was absent from the librocdxg
WSL library. On WSL2 all processor/socket/event handles are opaque
objects created by the librocdxg instance; passing them to the system
library is undefined behaviour and can crash or corrupt state.
Return nullptr instead so the SHIM_FN wrapper returns
AMDSMI_STATUS_NOT_SUPPORTED for any symbol the WSL library does not
export. The RTLD_NEXT fallback is retained only for the non-WSL path
where the system library owns the handles.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Stephen Bates <sbates@raithlin.com>
fix(wslamdsmi): remove zero-to-one metric sentinels
Three changes from the third Copilot review (2026-10-07) and upstream
discussion:
1. wsl-shim/Makefile: install target failed on a fresh host because the
parent directory may not exist. Add `install -d $(INSTALL_DIR)`.
2. wsl-shim/wsl_amdsmi_shim.cc: resolve() fell through to RTLD_NEXT
(system libamd_smi) when a symbol was absent from the librocdxg WSL
library. On WSL2 all handles are opaque objects created by librocdxg;
passing them to the system library is undefined behaviour. Return
nullptr instead so SHIM_FN wrappers return AMDSMI_STATUS_NOT_SUPPORTED
for any symbol the WSL library does not export. RTLD_NEXT is retained
only for the non-WSL (bare-metal) path.
3. wslamdsmi/smi_utils.hpp: remove the zero-to-one exporter sentinels
(average_gfx_activity=1, current_socket_power=1). Falsifying valid
zero readings (an idle GPU genuinely reports 0% activity) is incorrect.
The proper fix is to change device-metrics-exporter's
markUnsupportedFields calls from IsNonZeroValue (zero check) to
IsValueApplicable (UINT_MAX sentinel check), which is addressed in a
separate PR against ROCm/device-metrics-exporter.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Stephen Bates <sbates@raithlin.com>
The SHA 88585263c0627ee42c0e1c5143a112c8d6f4aa18 pinned for
github/codeql-action/upload-sarif was the annotated tag object hash,
not the underlying commit hash. GitHub Actions resolves subdirectory
actions against the commit tree, so the tag-object SHA caused the
"Upload Trivy results" step to fail with a 404.
Replace with the commit hash that v4.38.2 dereferences to:
2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Stephen Bates <sbates@raithlin.com>
@spraveenio can you take another look at this and let me know if you see any more issues? I think it is in good shape. I can do a rebase if needed or you can squash.
@sbates130272 thanks for taking care of the copilot comments. please give me some time early next week i'll have this reviewed soon. the code owners are a bit busy this week. much appreciate your patience and thanks again for the contribution
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds WSL2 support to gpuagent via a new
gpuagent-wslbinary that links directly against the librocdxg-providedlibamd_smi(from therocm-systemsproject, installed byrocdxg-amd-smi-libat/opt/rocm-wsl).WSL2 hosts expose GPU access through
/dev/dxg(the DirectX Graphics kernel interface) rather than/dev/kfd. The bare-metallibamd_smilinks against the KFD driver and cannot initialise on WSL2. The librocdxg project ships a WSL2-capablelibamd_smithat routes metric queries through the Windows GPU driver via DXCore PMLog escapes.gpuagent-wsllinks against that library directly, giving metric coverage on WSL2 development and CI nodes without requiring an LD_PRELOAD shim.Changes
Build system
gpuagent_wslMakefile target (outer and inner) producinggpuagent-wsl; links/opt/rocm-wsl/lib/libamd_smi.soviaWSL_AMD_SMI_LIB_DIR(overridable).gpuagent_wsl_dockertarget for compile-check inside the RHEL9 container using a stublibamd_smi.so, used by CI.Makefile.lib: cmake ≥ 4 policy flag (CMAKE_POLICY_VERSION_MINIMUM=3.5), zeromq-Wno-error=addressworkaround,CC/CXXchanged to?=for host overrides.wsl-shim/target for the LD_PRELOAD alternative deployment.New
WSL_AMD_SMIbackend (sw/nic/gpuagent/api/smi/wslamdsmi/)amdsmi/backend; includes fromwsl_amd_smi_libheaders.wsl2_detected()guard insmi_state::init()— fails fast with a clear error if/dev/dxgis absent or/dev/kfdis present.smi_wsl_fill_metrics_from_individual_()— populates zero fields inamdsmi_gpu_metrics_tfrom individual amdsmi calls when the PMLog bulk escape is unavailable or returns partial data (common on WSL2 APU drivers). Applies sentinel values of 1 for power and gfx_activity to preventdevice-metrics-exporter's startup filter from permanently blacklisting those fields.amdsmi_get_power_info/amdsmi_get_gpu_activityfallbacks insmi_gpu_fill_stats_for resilience against transient 0 readings.WSL_AMD_SMIbranch added tosmi.hppandsmi_api.hppalongsideAMD_SMIandGIM_AMD_SMI.Vendored WSL amdsmi headers (
sw/nic/third-party/rocm/wsl_amd_smi_lib/).so.LD_PRELOAD shim (
wsl-shim/)make wsl-shim.CI (separate commit)
build-wsljob: reuses the RHEL9 container image (cached), creates a linker stub forlibamd_smi.so, and verifiesgpuagent-wslcompiles cleanly.Build
Test results (AMD Radeon 8060S, Strix APU, WSL2)
gpuagent-wslstarts, detects WSL2 (/dev/dxgpresent,/dev/kfdabsent), discovers 1 GPU, and feeds live metrics todevice-metrics-exporter→ Prometheus:gpu_package_powergpu_power_usagegpu_gfx_activitygpu_clock(GFX)gpu_clock(MEM)gpu_used_vramgpu_healthExpected WSL2 limitations (not regressions): xGMI, PCIe throughput, CPER, temperature, fan, and voltage are unavailable — those require bare-metal KFD or PMLog escapes not wired for APUs on this driver.
The
make all/ Docker build path is unchanged;gpuagent_wslis intentionally excluded frommake allsince it requires a WSL2 host and therocdxg-amd-smi-libpackage.Fixes #93.
🤖 Generated with Claude Code